@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
@@ -183,7 +183,9 @@ function loadAgentRegistry() {
183
183
 
184
184
  const ANTHROPIC_MODEL = "claude-haiku-4-5-20251001";
185
185
  // Resolve claude against the agent's PATH (not launchd's bare env).
186
- import { resolveClaudeBin, augmentedPath, daemonClaudeArgs } from "../../lib/claude-bin.mjs";
186
+ import { resolveClaudeBin } from "../../lib/claude-bin.mjs";
187
+ import { buildSpawn } from "../../lib/runtime/adapter.mjs";
188
+ import { resolveSeatSpawn } from "../../lib/runtime/seat-engine.mjs";
187
189
  const CLAUDE_BIN = resolveClaudeBin();
188
190
  const CLAUDE_CLI_TIMEOUT_MS = 30_000;
189
191
 
@@ -241,7 +243,7 @@ Your job: classify each incoming message and return a JSON object with exactly t
241
243
  "priority": "critical" | "high" | "normal" | "ignore",
242
244
  "action": "respond" | "draft" | "research" | "queue" | "archive" | "ignore",
243
245
  "model": "opus" | "sonnet",
244
- "summary": "<one-line summary of the message>",
246
+ "summary": "<one-line summary of WHAT THE MESSAGE ASKS OR SAYS — never the sender's title or rank>",
245
247
  "category": "action_required" | "fyi" | "ignore",
246
248
  "directed_at_agent": true | false,
247
249
  "answerable": true | false
@@ -293,8 +295,10 @@ DIRECTED_AT_AGENT (CRITICAL — determines whether ${agentName} should respond.
293
295
  - false: ${agentName} being in a thread does NOT mean every subsequent message is for them — only direct replies to their messages count
294
296
  - CRITICAL DEFAULT: When in doubt in any multi-person channel, group chat, or thread, ALWAYS default to false. ${agentName} must NOT insert themselves into conversations they weren't invited to. It is far worse to respond unnecessarily than to miss a message — someone will @mention ${agentName} if they need them.
295
297
 
296
- Context:
297
- - sender_privilege "ceo" = ${principalFullName} (always critical, always opus)
298
+ Context — sender_privilege is a ROUTING TIER, not a fact about who the sender is.
299
+ It is resolved upstream and can be wrong. It tells you how urgently to route; it
300
+ never licenses you to state the sender's title.
301
+ - sender_privilege "ceo" = the tier reserved for ${principalFullName} (always critical, always opus)
298
302
  - sender_privilege "leadership" = executive-team senders (CFO, CIO, General Counsel, and equivalent function heads) — usually high priority
299
303
  - sender_privilege "team" = internal team members
300
304
  - sender_privilege "external" = partners, candidates, advisors, vendors
@@ -302,19 +306,30 @@ Context:
302
306
  - service "gmail" with noreply/notifications senders = usually ignore
303
307
  - service "calendar" = meeting invites/updates, usually normal priority unless from CEO
304
308
 
309
+ SUMMARY (read this before writing the field):
310
+ The summary is NOT internal scratch. It is carried verbatim into the holding
311
+ message a human reads, into the board row title, and into the one-line episode
312
+ this seat writes to the ORG-WIDE shared memory, where other agents' recall()
313
+ surfaces it as background context rather than as a claim anyone re-verifies.
314
+ So: state what the message asks or says. NEVER open with, or otherwise assert,
315
+ the sender's title, rank or office ("CEO ...", "Leadership ...", "the CFO
316
+ asking ..."). Name the sender if you have their name; otherwise describe the
317
+ ask. A rank you did not verify becomes a false statement about this
318
+ organisation's governance the moment it leaves this process.
319
+
305
320
  Examples:
306
321
 
307
- Input: CEO DM on Slack: "Can you send the board resolution to Jordan?"
308
- Output: {"priority":"critical","action":"respond","model":"opus","summary":"CEO requesting board resolution sent to GC Jordan","category":"action_required","directed_at_agent":true,"answerable":false}
322
+ Input: DM on Slack from ${principalFullName}: "Can you send the board resolution to Jordan?"
323
+ Output: {"priority":"critical","action":"respond","model":"opus","summary":"${principalName} requesting the board resolution be sent to GC Jordan","category":"action_required","directed_at_agent":true,"answerable":false}
309
324
 
310
- Input: CEO DM on Slack: "what kind of machine are you running on?"
311
- Output: {"priority":"critical","action":"respond","model":"opus","summary":"CEO asking what machine/infrastructure the agent runs on","category":"action_required","directed_at_agent":true,"answerable":true}
325
+ Input: DM on Slack from ${principalFullName}: "what kind of machine are you running on?"
326
+ Output: {"priority":"critical","action":"respond","model":"opus","summary":"${principalName} asking what machine/infrastructure the agent runs on","category":"action_required","directed_at_agent":true,"answerable":true}
312
327
 
313
328
  Input: GitHub notification email about a merged PR
314
329
  Output: {"priority":"ignore","action":"ignore","model":"sonnet","summary":"GitHub PR merge notification","category":"ignore","directed_at_agent":false,"answerable":false}
315
330
 
316
331
  Input: Leadership Slack: "We need the the regulator gap analysis updated by Thursday"
317
- Output: {"priority":"high","action":"draft","model":"opus","summary":"Leadership requesting the regulator gap analysis update by Thursday deadline","category":"action_required","directed_at_agent":true,"answerable":false}
332
+ Output: {"priority":"high","action":"draft","model":"opus","summary":"Request for the regulator gap analysis update by Thursday deadline","category":"action_required","directed_at_agent":true,"answerable":false}
318
333
 
319
334
  Input: Team member: "Standup notes from today's sync"
320
335
  Output: {"priority":"normal","action":"archive","model":"sonnet","summary":"Team standup notes shared","category":"fyi","directed_at_agent":false,"answerable":false}
@@ -325,8 +340,8 @@ Output: {"priority":"normal","action":"respond","model":"sonnet","summary":"Exte
325
340
  Input: Channel message from Jacob: "@${agentName} can you check the engine deployment?"
326
341
  Output: {"priority":"normal","action":"respond","model":"sonnet","summary":"Jacob asking ${agentName} to check engine deployment","category":"action_required","directed_at_agent":true,"answerable":false}
327
342
 
328
- Input: Channel message from CEO: "@AnotherAgent can you review the agentic libraries in this channel?"
329
- Output: {"priority":"normal","action":"archive","model":"sonnet","summary":"CEO asking another agent to review agentic libraries — not directed at ${agentName}","category":"fyi","directed_at_agent":false,"answerable":false}
343
+ Input: Channel message from ${principalFullName}: "@AnotherAgent can you review the agentic libraries in this channel?"
344
+ Output: {"priority":"normal","action":"archive","model":"sonnet","summary":"${principalName} asking another agent to review agentic libraries — not directed at ${agentName}","category":"fyi","directed_at_agent":false,"answerable":false}
330
345
 
331
346
  Input: Channel message from Sam to Jordan: "Jordan, did you file the compliance response yet?"
332
347
  Output: {"priority":"normal","action":"archive","model":"sonnet","summary":"Sam asking Jordan about compliance response filing","category":"fyi","directed_at_agent":false,"answerable":false}
@@ -450,22 +465,44 @@ export function parseClassification(text) {
450
465
  // • Any failure (non-zero exit, timeout, unparseable output) throws so
451
466
  // the Tier-2 llmTask fallback engages exactly as before.
452
467
 
468
+ /**
469
+ * The classifier lane's spawn from the runtime adapter (lane "classifier"):
470
+ * --print <permissions> <bare MCP> --model <m> --append-system-prompt <sys>,
471
+ * prompt on stdin; stock env posture (stale ANTHROPIC_API_KEY/AUTH_TOKEN
472
+ * stripped, PATH augmented).
473
+ *
474
+ * W4-E1 (CF-44): `seat` is the seat's engine resolved once for this spawn. A
475
+ * claude seat adds no fields (unchanged); an engine-cohort seat runs `cli.mjs run`.
476
+ * @param {{systemPrompt:string, userPrompt:string, env?:object, bin?:string, seat?:{fields:object}}} o
477
+ */
478
+ export function classifierSpawn({ systemPrompt, userPrompt, env = process.env, bin = CLAUDE_BIN, seat = { fields: {} } }) {
479
+ return buildSpawn({
480
+ lane: "classifier",
481
+ bin,
482
+ model: ANTHROPIC_MODEL,
483
+ systemPrompt,
484
+ prompt: userPrompt,
485
+ permissions: sessionPermissionArgs({ source: "classifier" }),
486
+ mcp: { source: "classifier" },
487
+ env,
488
+ ...seat.fields,
489
+ });
490
+ }
491
+
453
492
  async function runClaudeCLI(systemPrompt, userPrompt) {
493
+ // W4-E1: the seat's engine, once per spawn (a claude seat reads config/agent.json only).
494
+ const seat = await resolveSeatSpawn(agentDir());
454
495
  return new Promise((resolvePromise, rejectPromise) => {
455
- const args = [
456
- "--print",
457
- ...sessionPermissionArgs({ source: "classifier" }),
458
- ...daemonClaudeArgs(undefined, { source: "classifier" }),
459
- "--model", ANTHROPIC_MODEL,
460
- "--append-system-prompt", systemPrompt,
461
- ];
462
-
463
- const proc = spawn(CLAUDE_BIN, args, {
496
+ const spawnSpec = classifierSpawn({ systemPrompt, userPrompt, seat });
497
+ if (!spawnSpec.ok) {
498
+ // Throw-path parity: a rejection engages the Tier-2 llmTask fallback.
499
+ rejectPromise(new Error(`claude CLI unavailable: ${spawnSpec.error.message}`));
500
+ return;
501
+ }
502
+
503
+ const proc = spawn(spawnSpec.bin, spawnSpec.argv, {
464
504
  stdio: ["pipe", "pipe", "pipe"],
465
- // Force claude CLI onto keychain OAuth (Max subscription); strip any
466
- // stale ANTHROPIC_API_KEY/AUTH_TOKEN inherited from the daemon env.
467
- // Augment PATH so spawned subsessions find homebrew/nvm binaries.
468
- env: { ...process.env, PATH: augmentedPath(), ANTHROPIC_API_KEY: "", ANTHROPIC_AUTH_TOKEN: "" },
505
+ env: spawnSpec.env,
469
506
  });
470
507
 
471
508
  let stdout = "";
@@ -671,12 +708,24 @@ function classifyWithRules(item) {
671
708
  const directed = isDirectedAtAgent(item);
672
709
 
673
710
  // CEO → critical, opus (but in channels, respect directed check)
711
+ //
712
+ // The summary deliberately does NOT name the tier ("CEO message from …").
713
+ // sender_privilege is a routing tier resolved upstream and it has been wrong
714
+ // fleet-wide: the answerItem() call site (now resolveSenderPrivilege() in
715
+ // agent-daemon.mjs) once read the unparenthesised
716
+ // `a || b ? "ceo" : "unknown"`, which stamped every sender "ceo". The
717
+ // summary is carried verbatim into holding text, board rows and org-wide
718
+ // memory, so a tier name here becomes an asserted fact about the sender's
719
+ // office that nothing downstream re-verifies. Route on the tier; never
720
+ // narrate it. (The rules-path sweep covered every summary below: the
721
+ // urgent-keyword limb's "Urgent message from …" names the message's own
722
+ // content, not a rank, and is kept as is.)
674
723
  if (item.sender_privilege === "ceo") {
675
724
  return {
676
725
  priority: "critical",
677
726
  action: directed ? "respond" : "archive",
678
727
  model: "opus",
679
- summary: `CEO message from ${item.sender}`,
728
+ summary: `Message from ${item.sender}`,
680
729
  category: directed ? "action_required" : "fyi",
681
730
  directed_at_agent: directed,
682
731
  };
@@ -688,7 +737,7 @@ function classifyWithRules(item) {
688
737
  priority: "high",
689
738
  action: directed ? "respond" : "archive",
690
739
  model: "opus",
691
- summary: `Leadership message from ${item.sender}`,
740
+ summary: `Message from ${item.sender}`,
692
741
  category: directed ? "action_required" : "fyi",
693
742
  directed_at_agent: directed,
694
743
  };
@@ -5,7 +5,8 @@
5
5
  * Single export: compileContext(item, classResult, options)
6
6
  *
7
7
  * Assembles sender profile, disclosure boundaries, conversation history,
8
- * active sessions, and sent-message registry into a single context block.
8
+ * current work (this agent's sessions on the machine), and sent-message
9
+ * registry into a single context block.
9
10
  * Filters history entries by information barrier domains.
10
11
  *
11
12
  * Adaptive token budget: 12K for critical/high, 8K for normal, 4K for low.
@@ -15,6 +16,9 @@ import { readFileSync, readdirSync } from "fs";
15
16
  import { join } from "path";
16
17
  import { isEnabled as orgEnabled, loadOrgConfig } from "../../lib/org/client.mjs";
17
18
  import { recall as orgRecall } from "../../lib/org/knowledge.mjs";
19
+ import { isPrivateConversation, historyDirNames } from "../../lib/context/history-scope.mjs";
20
+ import { fitSections } from "../../lib/context/budget.mjs";
21
+ import { currentWorkBlock, audienceForItem } from "../../lib/session/current-work.mjs";
18
22
 
19
23
  const AGENT_REPO_DIR = process.env.__TEST_AGENT_DIR || process.env.AGENT_DIR || join(new URL(".", import.meta.url).pathname, "../..");
20
24
  const SESSION_DIR = process.env.__TEST_SESSION_DIR || join(AGENT_REPO_DIR, "state", "sessions");
@@ -56,6 +60,8 @@ const HISTORY_SHARE = 0.6;
56
60
  */
57
61
  function extractChannelId(item) {
58
62
  if (!item) return null;
63
+ // Every non-Slack service sets this outright; Slack is the one that has to be
64
+ // dug out of `raw_ref`.
59
65
  if (item.channel_id) return item.channel_id;
60
66
  if (item.raw_ref) {
61
67
  const match = item.raw_ref.match(/slack:([DC][A-Z0-9]+):/);
@@ -164,6 +170,24 @@ function matchesForbiddenDomain(text, forbiddenDomains, domainKeywords) {
164
170
  return false;
165
171
  }
166
172
 
173
+ /**
174
+ * The information-barrier predicate for a sender: `text → true` when the text
175
+ * trips 2+ keywords of a domain that sender may not see. This is the SAME test
176
+ * `loadConversationHistory` applies to each history turn, exported so the
177
+ * current-work block gets it on BOTH reply paths (the quick path has no
178
+ * barrier machinery of its own and imports this). Fail-closed on a sender we
179
+ * cannot evaluate: `forbiddenDomains` for an unknown sender is every domain.
180
+ *
181
+ * @param {string|null} senderSlug
182
+ * @returns {(text:string)=>boolean}
183
+ */
184
+ export function forbidsTextFor(senderSlug) {
185
+ const { forbiddenDomains } = loadDisclosureBoundaries(senderSlug, ALLOC.boundaries);
186
+ if (!forbiddenDomains || forbiddenDomains.length === 0) return () => false;
187
+ const domainKeywords = buildDomainKeywords();
188
+ return (text) => matchesForbiddenDomain(String(text || ""), forbiddenDomains, domainKeywords);
189
+ }
190
+
167
191
  /**
168
192
  * Load disclosure boundaries for a sender.
169
193
  * Returns { boundariesSection, forbiddenDomains }.
@@ -234,14 +258,32 @@ function loadDisclosureBoundaries(senderSlug, budgetTokens) {
234
258
 
235
259
  /**
236
260
  * Load conversation history from interaction logs, filtered by disclosure boundaries.
261
+ *
262
+ * READS THE ITEM'S OWN SERVICE. `responder.mjs#logInteraction` files every
263
+ * exchange under `memory/interactions/<service>/…`; this read was pinned to
264
+ * `…/slack/…`, so a Cohort, Telegram or WhatsApp exchange was written down and
265
+ * then never found (design §3 R12) — which is how "why can't you see the
266
+ * context of our ongoing conversation?" happened in a thread whose whole
267
+ * history was on disk the whole time.
268
+ *
269
+ * Slack keeps its legacy bare `<sender-slug>` directory alongside the `dm-`
270
+ * one, so historical logs do not go dark on this change.
271
+ *
272
+ * AND IT READS ONLY WHAT THIS ROOM MAY SEE. `logInteraction` files every
273
+ * exchange under BOTH the room and `dm-<sender-slug>`, so merging the candidate
274
+ * directories put one person's private DM sentence into the prompt built for a
275
+ * public channel the moment this read stopped being Slack-only. The gate lives
276
+ * in `lib/context/history-scope.mjs`, shared with the legacy prompt path and
277
+ * with the writer (design §5.5, resource scope).
237
278
  */
238
- function loadConversationHistory(senderSlug, channelId, budgetTokens, forbiddenDomains) {
239
- const candidateDirs = [];
240
- if (channelId) candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", channelId));
241
- if (senderSlug) {
242
- candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", `dm-${senderSlug}`));
243
- candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", senderSlug));
244
- }
279
+ function loadConversationHistory(senderSlug, channelId, budgetTokens, forbiddenDomains, service = "slack", isPrivate = false) {
280
+ const svc = String(service || "slack");
281
+ const candidateDirs = historyDirNames({
282
+ channelId,
283
+ senderSlug,
284
+ isPrivate: isPrivate === true,
285
+ service: svc,
286
+ }).map((name) => join(AGENT_REPO_DIR, "memory", "interactions", svc, name));
245
287
 
246
288
  const entries = [];
247
289
  const seenKeys = new Set();
@@ -292,65 +334,60 @@ function loadConversationHistory(senderSlug, channelId, budgetTokens, forbiddenD
292
334
 
293
335
  if (filtered.length === 0) return null;
294
336
 
295
- // Truncate to character budget — most recent messages prioritised (iterate from end)
296
- const charBudget = budgetTokens * CHARS_PER_TOKEN;
297
- const outputLines = [];
298
- let totalChars = 0;
299
-
300
- for (let i = filtered.length - 1; i >= 0; i--) {
301
- const entry = filtered[i];
337
+ // Spend the budget from the NEWEST turn backwards, and SAY what fell off.
338
+ // This used to `break` out of the loop and return the survivors, so a prompt
339
+ // that had quietly lost thirty turns of a conversation looked exactly like a
340
+ // conversation thirty turns shorter — design §5.4: "dropping is visible in
341
+ // the prompt, never silent". `lib/context/budget.mjs` owns the arithmetic and
342
+ // the wording; this function only supplies the turns.
343
+ const turns = filtered.map((entry) => {
302
344
  const from = entry.from || entry.sender || entry.user_name || "unknown";
303
345
  const text = entry.content || entry.text || "(no text)";
304
346
  const ts = entry.received_at || entry.timestamp || entry.processed_at || entry.ts || "";
305
347
  const responded = entry.response_sent ? " [Responded]" : "";
306
- const line = `[${ts}] ${from}: ${text.substring(0, 500)}${responded}`;
348
+ return `[${ts}] ${from}: ${text.substring(0, 500)}${responded}`;
349
+ });
307
350
 
308
- if (totalChars + line.length > charBudget) break;
309
- outputLines.unshift(line);
310
- totalChars += line.length;
311
- }
351
+ const fitted = fitSections({
352
+ sections: [{ name: "thread", turns }],
353
+ budgetTokens: Math.max(1, Number(budgetTokens) || 0),
354
+ charsPerToken: CHARS_PER_TOKEN,
355
+ });
312
356
 
313
- return outputLines.length > 0 ? outputLines.join("\n") : null;
357
+ return fitted.text ? fitted.text : null;
314
358
  }
315
359
 
316
- // ── Active sessions ────────────────────────────────────────────────
360
+ // ── Current work (the agent's own sessions on this machine) ────────
317
361
 
318
362
  /**
319
- * Load active sessions and format as bullet list.
363
+ * The bounded "what am I doing right now" block — lib/session/current-work.
364
+ *
365
+ * This used to read `active.json` alone: the daemon's own `--print` children,
366
+ * summary + model + sender, nothing else. A human asking "what are you doing
367
+ * right now, across sessions on your machine?" was answered from a list that
368
+ * did not contain the front door, its peers, the board items in progress, or
369
+ * the fact that a person had an interactive session open. The block now
370
+ * carries all of those (and names the board item where one is linked), under
371
+ * the same token allocation, and reports a human's session as EXISTING only.
372
+ *
373
+ * SCOPED TO THE AUDIENCE. The old list put every live session's summary —
374
+ * the classifier's restatement of some other person's message — into any
375
+ * reply's context. The block is now rendered for `item`'s conversation
376
+ * (`audienceForItem`): a session from another conversation is reported as
377
+ * existing only, and what is disclosable passes the same information-barrier
378
+ * keyword filter the history above it does (`forbiddenDomains`).
320
379
  */
321
- function loadActiveSessions(budgetTokens) {
380
+ async function loadCurrentWork(budgetTokens, item, forbiddenDomains) {
322
381
  try {
323
- const filePath = join(SESSION_DIR, "active.json");
324
- const raw = readFileSync(filePath, "utf-8");
325
- const data = JSON.parse(raw);
326
-
327
- // data may be an object with session entries or an array
328
- const sessions = Array.isArray(data) ? data : Object.values(data);
329
- if (sessions.length === 0) return null;
330
-
331
- const lines = [];
382
+ const domainKeywords = forbiddenDomains && forbiddenDomains.length > 0 ? buildDomainKeywords() : null;
383
+ const audience = audienceForItem(item, {
384
+ isPrivate: isPrivateConversation(item),
385
+ forbidsText: domainKeywords ? (t) => matchesForbiddenDomain(t, forbiddenDomains, domainKeywords) : undefined,
386
+ });
387
+ const block = await currentWorkBlock(AGENT_REPO_DIR, { activePath: join(SESSION_DIR, "active.json"), audience });
388
+ if (!block) return null;
332
389
  const charBudget = budgetTokens * CHARS_PER_TOKEN;
333
- let totalChars = 0;
334
-
335
- for (const sess of sessions) {
336
- const id = sess.sessionId || sess.id || "unknown";
337
- const summary = sess.summary || sess.task || "no summary";
338
- const model = sess.model || "unknown";
339
- const sender = sess.sender || sess.owner || "unknown";
340
- const started = sess.startTime || sess.started_at || "";
341
- let age = "";
342
- if (started) {
343
- const ms = Date.now() - new Date(started).getTime();
344
- const mins = Math.round(ms / 60000);
345
- age = `${mins}m ago`;
346
- }
347
- const line = `- ${id}: ${summary} (model=${model}, sender=${sender}${age ? ", " + age : ""})`;
348
- if (totalChars + line.length > charBudget) break;
349
- lines.push(line);
350
- totalChars += line.length;
351
- }
352
-
353
- return lines.length > 0 ? lines.join("\n") : null;
390
+ return block.length > charBudget ? block.slice(0, charBudget) : block;
354
391
  } catch {
355
392
  return null;
356
393
  }
@@ -522,10 +559,18 @@ export async function compileContext(item, classResult, options = {}) {
522
559
  const fixedTokens = ALLOC.profile + ALLOC.boundaries + ALLOC.active_sessions + ALLOC.sent_registry + ALLOC.org_memory;
523
560
  const remainingTokens = tokenBudget - fixedTokens;
524
561
  const historyBudget = Math.floor(remainingTokens * HISTORY_SHARE);
525
- const historyText = loadConversationHistory(senderSlug, channelId, historyBudget, forbiddenDomains);
526
-
527
- // Step 5 — Active sessions
528
- const sessionsText = loadActiveSessions(ALLOC.active_sessions);
562
+ const historyText = loadConversationHistory(
563
+ senderSlug,
564
+ channelId,
565
+ historyBudget,
566
+ forbiddenDomains,
567
+ item?.service,
568
+ isPrivateConversation(item),
569
+ );
570
+
571
+ // Step 5 — Current work (this agent's sessions on this machine, bounded,
572
+ // scoped to this item's conversation and the sender's barrier)
573
+ const sessionsText = await loadCurrentWork(ALLOC.active_sessions, item, forbiddenDomains);
529
574
 
530
575
  // Step 6 — Sent registry
531
576
  const sentText = loadSentRegistry(channelId, ALLOC.sent_registry);
@@ -568,7 +613,7 @@ export async function compileContext(item, classResult, options = {}) {
568
613
  sections.push("");
569
614
 
570
615
  if (sessionsText) {
571
- sections.push("## Active Sessions");
616
+ // The block carries its own "## Current Work" header.
572
617
  sections.push(sessionsText);
573
618
  sections.push("");
574
619
  }
@@ -118,7 +118,10 @@ export function resolveSlackChannel(item) {
118
118
  */
119
119
 
120
120
  /** Surfaces that genuinely live in a Cohort room — these reply with a message. */
121
- const CHANNEL_SURFACES = Object.freeze(["dm", "mention", "thread_reply", "call"]);
121
+ // `broadcast` is a room message like a mention: the roll-call answer posts
122
+ // into the space it was asked in. Without this entry the reply has no
123
+ // transport, `canDeliverTo` says no, and the surface is dark at the last step.
124
+ const CHANNEL_SURFACES = Object.freeze(["dm", "mention", "broadcast", "thread_reply", "call"]);
122
125
 
123
126
  /**
124
127
  * Error frame codes a retry cannot change.
@@ -624,6 +627,26 @@ async function sendCohortEntityComment(route, text, o = {}) {
624
627
  // The one public entry point
625
628
  // ---------------------------------------------------------------------------
626
629
 
630
+ // Refuse any outbound body carrying raw scaffold markup. A closing </thinking>,
631
+ // an <assistant> wrapper and a <user_message> marker have each reached a shared
632
+ // room, from three separate seats. The seam is the join between the daemon
633
+ // placeholder and the generated output; whatever the cause on a given seat,
634
+ // nothing carrying these markers may leave it. Permanent refusal (no retry),
635
+ // same shape as the persona-leak gate: the caller escalates, never re-sends.
636
+ const SCAFFOLD_MARKER_RE = /<\/?(thinking|assistant|user_message|user|system|antml:[a-z_]+)>/i;
637
+
638
+ /**
639
+ * The first raw scaffold marker in `text`, or null when it carries none.
640
+ * Prose that merely NAMES a marker ("the thinking tag") is not a leak — only
641
+ * the literal angle-bracketed form matches.
642
+ * @param {string} text
643
+ * @returns {string|null}
644
+ */
645
+ export function scaffoldMarkerLeak(text) {
646
+ const m = typeof text === "string" ? text.match(SCAFFOLD_MARKER_RE) : null;
647
+ return m ? m[0] : null;
648
+ }
649
+
627
650
  /**
628
651
  * Deliver `text` to the human behind `item`. No generation, no model, no
629
652
  * budget — only transport.
@@ -657,6 +680,12 @@ export async function deliver(item, text, o = {}) {
657
680
  if (!item || !text || !String(text).trim()) {
658
681
  return { sent: false, via: null, error: "nothing to deliver" };
659
682
  }
683
+ const leak = scaffoldMarkerLeak(text);
684
+ if (leak) {
685
+ const surface = (item && item.channel_id) ? "room" : "unknown";
686
+ try { console.error(`[deliver] refused: raw scaffold marker ${JSON.stringify(leak)} in outbound body (permanent, not retrying)`); } catch { /* */ }
687
+ return { sent: false, via: null, permanent: true, channel: item && item.channel_id, surface, code: "FORBIDDEN_SCOPE", error: `FORBIDDEN_SCOPE: raw scaffold marker ${leak} in outbound body — strip the marker; never send reasoning or transcript wrappers` };
688
+ }
660
689
  // `permanent` says retrying changes nothing. A caller that cannot tell the
661
690
  // difference between "hq blipped" and "there is no transport for telegram"
662
691
  // retries both at the same cadence, which is how an impossible send becomes an