@cohortapp/agent-sdk 2.17.0 → 2.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (526) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +18 -5
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/setup-wizard.md +1 -1
  9. package/docs/runbooks/fleet-rollout.md +156 -0
  10. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  11. package/lib/action-executor.js +19 -3
  12. package/lib/budget-guard.mjs +279 -3
  13. package/lib/channels/base-adapter.mjs +3 -1
  14. package/lib/channels/contract.mjs +2 -1
  15. package/lib/channels/inbox-item.mjs +8 -0
  16. package/lib/claude-bin.mjs +5 -6
  17. package/lib/cli/doctor-checks.mjs +141 -10
  18. package/lib/cli/global-setup-extras.mjs +5 -1
  19. package/lib/cli/inbox.mjs +100 -15
  20. package/lib/cli/seat-auth.mjs +463 -0
  21. package/lib/cli/session.mjs +80 -12
  22. package/lib/collective/capture.mjs +8 -6
  23. package/lib/collective/global-config.mjs +63 -1
  24. package/lib/collective/presence.mjs +142 -5
  25. package/lib/comms/send-gate.mjs +559 -1
  26. package/lib/diagnostics/alerts.mjs +49 -0
  27. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  28. package/lib/engine/agents/definitions.mjs +343 -0
  29. package/lib/engine/agents/persist.mjs +275 -0
  30. package/lib/engine/agents/runtime.mjs +748 -0
  31. package/lib/engine/agents/usage.mjs +95 -0
  32. package/lib/engine/auth-status.mjs +139 -0
  33. package/lib/engine/budget.mjs +194 -0
  34. package/lib/engine/cli.mjs +1204 -0
  35. package/lib/engine/commands/index.mjs +269 -0
  36. package/lib/engine/context/budget.mjs +219 -0
  37. package/lib/engine/context/cache.mjs +125 -0
  38. package/lib/engine/context/child-env.mjs +215 -0
  39. package/lib/engine/context/compaction.mjs +342 -0
  40. package/lib/engine/context/images.mjs +90 -0
  41. package/lib/engine/context/instructions.mjs +327 -0
  42. package/lib/engine/context/lazy-instructions.mjs +169 -0
  43. package/lib/engine/context/manager.mjs +182 -0
  44. package/lib/engine/context/real-path.mjs +91 -0
  45. package/lib/engine/context/secret-values.mjs +163 -0
  46. package/lib/engine/context/settings.mjs +274 -0
  47. package/lib/engine/context/stream-input.mjs +159 -0
  48. package/lib/engine/guard.mjs +152 -0
  49. package/lib/engine/hooks.mjs +713 -0
  50. package/lib/engine/loop.mjs +560 -0
  51. package/lib/engine/mcp/client.mjs +254 -0
  52. package/lib/engine/mcp/config.mjs +301 -0
  53. package/lib/engine/mcp/http.mjs +201 -0
  54. package/lib/engine/mcp/index.mjs +146 -0
  55. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  56. package/lib/engine/mcp/naming.mjs +66 -0
  57. package/lib/engine/mcp/resources.mjs +89 -0
  58. package/lib/engine/mcp/results.mjs +133 -0
  59. package/lib/engine/mcp/stdio.mjs +137 -0
  60. package/lib/engine/mcp/supervisor.mjs +116 -0
  61. package/lib/engine/messages.mjs +104 -0
  62. package/lib/engine/output/json.mjs +164 -0
  63. package/lib/engine/output/stream-json.mjs +266 -0
  64. package/lib/engine/permissions.mjs +845 -0
  65. package/lib/engine/process-identity.mjs +164 -0
  66. package/lib/engine/process-tree.mjs +551 -0
  67. package/lib/engine/prompt.mjs +60 -0
  68. package/lib/engine/session/store.mjs +299 -0
  69. package/lib/engine/session-runtime/args.mjs +97 -0
  70. package/lib/engine/session-runtime/host.mjs +143 -0
  71. package/lib/engine/session-runtime/inbox.mjs +122 -0
  72. package/lib/engine/session-runtime/notifications.mjs +129 -0
  73. package/lib/engine/session-runtime/registry.mjs +328 -0
  74. package/lib/engine/session-runtime/runner.mjs +344 -0
  75. package/lib/engine/session-runtime/socket.mjs +212 -0
  76. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  77. package/lib/engine/skills/index.mjs +321 -0
  78. package/lib/engine/tools/bash-background.mjs +533 -0
  79. package/lib/engine/tools/bash.mjs +216 -0
  80. package/lib/engine/tools/edit.mjs +97 -0
  81. package/lib/engine/tools/glob.mjs +81 -0
  82. package/lib/engine/tools/grep.mjs +224 -0
  83. package/lib/engine/tools/index.mjs +84 -0
  84. package/lib/engine/tools/list-agents.mjs +32 -0
  85. package/lib/engine/tools/ls.mjs +127 -0
  86. package/lib/engine/tools/monitor.mjs +82 -0
  87. package/lib/engine/tools/notebook-edit.mjs +218 -0
  88. package/lib/engine/tools/read.mjs +103 -0
  89. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  90. package/lib/engine/tools/schema.mjs +144 -0
  91. package/lib/engine/tools/send-message.mjs +77 -0
  92. package/lib/engine/tools/session.mjs +70 -0
  93. package/lib/engine/tools/todo.mjs +144 -0
  94. package/lib/engine/tools/toolsearch.mjs +217 -0
  95. package/lib/engine/tools/walk.mjs +193 -0
  96. package/lib/engine/tools/web-switch.mjs +31 -0
  97. package/lib/engine/tools/webfetch-html.mjs +387 -0
  98. package/lib/engine/tools/webfetch-net.mjs +340 -0
  99. package/lib/engine/tools/webfetch.mjs +198 -0
  100. package/lib/engine/tools/websearch.mjs +91 -0
  101. package/lib/engine/tools/workflow.mjs +95 -0
  102. package/lib/engine/tools/write.mjs +76 -0
  103. package/lib/engine/tui/line-editor.mjs +137 -0
  104. package/lib/engine/tui/render.mjs +86 -0
  105. package/lib/engine/tui/tui.mjs +274 -0
  106. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  107. package/lib/engine/wire/effort.mjs +36 -0
  108. package/lib/engine/wire/errors.mjs +496 -0
  109. package/lib/engine/wire/http.mjs +441 -0
  110. package/lib/engine/wire/index.mjs +76 -0
  111. package/lib/engine/wire/openai-chat.mjs +332 -0
  112. package/lib/engine/wire/prompt-cache.mjs +79 -0
  113. package/lib/engine/wire/search.mjs +140 -0
  114. package/lib/engine/wire/sse.mjs +114 -0
  115. package/lib/engine/wire/stall.mjs +349 -0
  116. package/lib/engine/wire/token-provider.mjs +175 -0
  117. package/lib/engine/wire/usage.mjs +192 -0
  118. package/lib/engine/workflow/host.mjs +524 -0
  119. package/lib/engine/workflow/journal.mjs +188 -0
  120. package/lib/engine/workflow/json-schema.mjs +171 -0
  121. package/lib/engine/workflow/meta.mjs +329 -0
  122. package/lib/engine/workflow/notifications.mjs +52 -0
  123. package/lib/engine/workflow/runtime.mjs +447 -0
  124. package/lib/engine/workflow/sandbox.mjs +534 -0
  125. package/lib/engine/workflow/worker.mjs +141 -0
  126. package/lib/engine/workflow/worktree.mjs +74 -0
  127. package/lib/execution/disposition.mjs +1 -1
  128. package/lib/execution/intake.mjs +10 -0
  129. package/lib/execution/surface-policy.mjs +15 -0
  130. package/lib/learning/curator.mjs +8 -6
  131. package/lib/learning/reflect.mjs +8 -6
  132. package/lib/model-router/catalog/cohort.yaml +137 -0
  133. package/lib/model-router/catalog.mjs +118 -1
  134. package/lib/model-router/failover.mjs +67 -16
  135. package/lib/model-router/llm-task.mjs +39 -3
  136. package/lib/model-router/resolve.mjs +89 -3
  137. package/lib/model-router/spawn.mjs +46 -47
  138. package/lib/model-router/taxonomy.mjs +126 -4
  139. package/lib/org/cost-sync.mjs +141 -11
  140. package/lib/org/inbound/broadcast.mjs +289 -0
  141. package/lib/org/inbound/collective.mjs +375 -0
  142. package/lib/org/inbound/directedness.mjs +96 -8
  143. package/lib/org/inbound/facts.mjs +78 -2
  144. package/lib/org/inbound/project.mjs +22 -0
  145. package/lib/org/inbound/surfaces.mjs +14 -0
  146. package/lib/org/llm-token.mjs +879 -0
  147. package/lib/org/mesh.mjs +61 -0
  148. package/lib/org/messaging.mjs +3 -1
  149. package/lib/org/protocol.checksum +1 -1
  150. package/lib/org/protocol.mjs +15 -0
  151. package/lib/org/quota.mjs +520 -0
  152. package/lib/org/tool-surface.mjs +104 -16
  153. package/lib/org/ui-parity.mjs +16 -1
  154. package/lib/org/work-ledger.mjs +37 -6
  155. package/lib/rate-guard.mjs +114 -1
  156. package/lib/resource-governor.mjs +41 -6
  157. package/lib/runtime/adapter.mjs +823 -0
  158. package/lib/runtime/child-env.mjs +191 -0
  159. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  160. package/lib/runtime/seat-engine.mjs +162 -0
  161. package/lib/session/ask-ledger.mjs +271 -0
  162. package/lib/session/current-work.mjs +676 -0
  163. package/lib/session/feed-core.mjs +40 -3
  164. package/lib/session/launch-args.mjs +56 -4
  165. package/lib/session/status-summary.mjs +26 -9
  166. package/lib/session/upgrade-notice.mjs +42 -0
  167. package/lib/setup/claude-probe.mjs +117 -13
  168. package/lib/setup/enrich.mjs +13 -10
  169. package/lib/setup/sections/model.mjs +39 -13
  170. package/lib/telemetry/collect.mjs +208 -9
  171. package/lib/upgrade/ignored-drift.mjs +105 -0
  172. package/lib/voice/post-call-brief.mjs +30 -17
  173. package/package.json +13 -3
  174. package/plugins/maestro-skills/skills/board-work.md +5 -0
  175. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  176. package/plugins/maestro-skills/skills/main-session.md +18 -7
  177. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  178. package/scripts/ci/run-tests.mjs +47 -19
  179. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  180. package/scripts/collective/hook-runner.mjs +29 -2
  181. package/scripts/continuous-monitor.sh +13 -0
  182. package/scripts/cost/track-claude-usage.mjs +15 -0
  183. package/scripts/daemon/agent-daemon.mjs +408 -20
  184. package/scripts/daemon/assurance.mjs +48 -12
  185. package/scripts/daemon/cadence-consumer.mjs +218 -68
  186. package/scripts/daemon/cadence-handlers.mjs +73 -4
  187. package/scripts/daemon/classifier.mjs +75 -26
  188. package/scripts/daemon/context-compiler.mjs +51 -37
  189. package/scripts/daemon/deliver.mjs +30 -1
  190. package/scripts/daemon/dispatcher.mjs +595 -149
  191. package/scripts/daemon/health.mjs +14 -1
  192. package/scripts/daemon/maestro-daemon.mjs +11 -0
  193. package/scripts/daemon/prompt-builder.mjs +24 -0
  194. package/scripts/daemon/responder.mjs +246 -79
  195. package/scripts/daemon/sdk-version.mjs +98 -16
  196. package/scripts/eval/probe-gateway.mjs +635 -0
  197. package/scripts/eval/replay/extract.mjs +270 -0
  198. package/scripts/eval/replay/grade.mjs +260 -0
  199. package/scripts/eval/replay/lib/config.mjs +50 -0
  200. package/scripts/eval/replay/lib/effects.mjs +65 -0
  201. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  202. package/scripts/eval/replay/lib/judge.mjs +72 -0
  203. package/scripts/eval/replay/lib/redact.mjs +136 -0
  204. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  205. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  206. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  207. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  208. package/scripts/eval/replay/report.mjs +185 -0
  209. package/scripts/eval/replay/run.mjs +404 -0
  210. package/scripts/fleet/rollout.mjs +1094 -0
  211. package/scripts/hooks/pre-send-audit.sh +36 -245
  212. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  213. package/scripts/hooks/validate-state-yaml.sh +190 -0
  214. package/scripts/huddle/huddle-llm.mjs +361 -0
  215. package/scripts/huddle/huddle-server.mjs +46 -121
  216. package/scripts/local-triggers/autoupdate.sh +448 -78
  217. package/scripts/local-triggers/run-trigger.sh +13 -0
  218. package/scripts/maintenance/pin-integrity.mjs +364 -0
  219. package/scripts/poll-slack-events.sh +41 -9
  220. package/scripts/poller/slack-socket-mode.mjs +28 -3
  221. package/scripts/session/supervisor.mjs +80 -13
  222. package/scripts/spawn-session.sh +13 -0
  223. package/bin/maestro.test.mjs +0 -1574
  224. package/lib/action-executor.test.mjs +0 -871
  225. package/lib/archetype.test.mjs +0 -132
  226. package/lib/assurance/plan-note.test.mjs +0 -234
  227. package/lib/assurance/room-budget.test.mjs +0 -486
  228. package/lib/assurance/tier.test.mjs +0 -174
  229. package/lib/autonomy.test.mjs +0 -66
  230. package/lib/backlog.test.mjs +0 -302
  231. package/lib/backup/policy.test.mjs +0 -305
  232. package/lib/budget-escalate.test.mjs +0 -232
  233. package/lib/budget-guard.envelope.test.mjs +0 -476
  234. package/lib/budget-guard.test.mjs +0 -427
  235. package/lib/cadence-bus-requeue.test.mjs +0 -83
  236. package/lib/cadence-bus-schedule.test.mjs +0 -194
  237. package/lib/cadence-bus.test.mjs +0 -720
  238. package/lib/cadences.test.mjs +0 -230
  239. package/lib/capability/inventory.test.mjs +0 -232
  240. package/lib/capability.test.mjs +0 -78
  241. package/lib/channels/base-adapter.test.mjs +0 -590
  242. package/lib/channels/channels.test.mjs +0 -371
  243. package/lib/channels/contract.test.mjs +0 -162
  244. package/lib/channels/inbox-item.test.mjs +0 -368
  245. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  246. package/lib/channels/pairing.test.mjs +0 -270
  247. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  248. package/lib/channels/slack-adapter.test.mjs +0 -212
  249. package/lib/channels/telegram-adapter.test.mjs +0 -306
  250. package/lib/channels/voice/adapter.test.mjs +0 -278
  251. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  252. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  253. package/lib/charter.test.mjs +0 -89
  254. package/lib/claude-bin.test.mjs +0 -131
  255. package/lib/cli/board.test.mjs +0 -227
  256. package/lib/cli/design.test.mjs +0 -270
  257. package/lib/cli/doctor-checks.test.mjs +0 -336
  258. package/lib/cli/global-setup-extras.test.mjs +0 -462
  259. package/lib/cli/inbox.test.mjs +0 -230
  260. package/lib/cli/session-ack.test.mjs +0 -63
  261. package/lib/cli/session.test.mjs +0 -613
  262. package/lib/collective/capture.test.mjs +0 -121
  263. package/lib/collective/cards.test.mjs +0 -114
  264. package/lib/collective/config.test.mjs +0 -123
  265. package/lib/collective/global-config.test.mjs +0 -220
  266. package/lib/collective/global-skills.test.mjs +0 -126
  267. package/lib/collective/presence.test.mjs +0 -95
  268. package/lib/collective/recall.test.mjs +0 -116
  269. package/lib/collective/vendor-skills.test.mjs +0 -306
  270. package/lib/comms/send-gate.test.mjs +0 -770
  271. package/lib/comms.test.mjs +0 -41
  272. package/lib/context/budget.test.mjs +0 -252
  273. package/lib/context/history-scope.test.mjs +0 -79
  274. package/lib/cost/ledger-row.test.mjs +0 -183
  275. package/lib/design/design-md.test.mjs +0 -318
  276. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  277. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  278. package/lib/design/fixtures/foundation.json +0 -133
  279. package/lib/design/refresh-gate.test.mjs +0 -144
  280. package/lib/design/write.test.mjs +0 -241
  281. package/lib/diagnostics/alerts.test.mjs +0 -318
  282. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  283. package/lib/diagnostics/counters.test.mjs +0 -206
  284. package/lib/diagnostics/events.test.mjs +0 -290
  285. package/lib/diagnostics/otel.test.mjs +0 -196
  286. package/lib/diagnostics/trace.test.mjs +0 -251
  287. package/lib/env-compat.test.mjs +0 -104
  288. package/lib/execution/disposition.test.mjs +0 -553
  289. package/lib/execution/drive.test.mjs +0 -270
  290. package/lib/execution/effects.test.mjs +0 -344
  291. package/lib/execution/intake.test.mjs +0 -389
  292. package/lib/execution/journal.test.mjs +0 -261
  293. package/lib/execution/match.test.mjs +0 -235
  294. package/lib/execution/pipeline.test.mjs +0 -392
  295. package/lib/execution/route.test.mjs +0 -186
  296. package/lib/execution/surface-policy.test.mjs +0 -162
  297. package/lib/fs-atomic.test.mjs +0 -72
  298. package/lib/fs-ownership.test.mjs +0 -158
  299. package/lib/goals/admission.test.mjs +0 -164
  300. package/lib/goals/classify.test.mjs +0 -167
  301. package/lib/goals/collaborate.test.mjs +0 -336
  302. package/lib/goals/gaps.test.mjs +0 -284
  303. package/lib/goals/loop.test.mjs +0 -845
  304. package/lib/hooks/bus.test.mjs +0 -387
  305. package/lib/identity/persona.test.mjs +0 -142
  306. package/lib/kpi-sensors.test.mjs +0 -278
  307. package/lib/kpi.test.mjs +0 -244
  308. package/lib/learning/config.test.mjs +0 -75
  309. package/lib/learning/counters.test.mjs +0 -69
  310. package/lib/learning/curator-consolidate.test.mjs +0 -238
  311. package/lib/learning/curator.test.mjs +0 -106
  312. package/lib/learning/reflect.test.mjs +0 -0
  313. package/lib/learning/session-index.test.mjs +0 -125
  314. package/lib/learning/skill-writer.test.mjs +0 -210
  315. package/lib/mandate/audit.test.mjs +0 -195
  316. package/lib/mandate/contract.test.mjs +0 -185
  317. package/lib/mandate/derive.test.mjs +0 -274
  318. package/lib/mandate/model.test.mjs +0 -164
  319. package/lib/mandate/refresh.test.mjs +0 -389
  320. package/lib/mcp/server.test.mjs +0 -426
  321. package/lib/model-router/auth-profiles.test.mjs +0 -580
  322. package/lib/model-router/catalog.test.mjs +0 -385
  323. package/lib/model-router/economics.test.mjs +0 -438
  324. package/lib/model-router/failover.test.mjs +0 -439
  325. package/lib/model-router/health.test.mjs +0 -338
  326. package/lib/model-router/integration-coverage.test.mjs +0 -831
  327. package/lib/model-router/integration.test.mjs +0 -564
  328. package/lib/model-router/ledger.test.mjs +0 -415
  329. package/lib/model-router/llm-task.test.mjs +0 -392
  330. package/lib/model-router/org-credentials.test.mjs +0 -265
  331. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  332. package/lib/model-router/reconcile.test.mjs +0 -316
  333. package/lib/model-router/repair.test.mjs +0 -180
  334. package/lib/model-router/spawn.test.mjs +0 -446
  335. package/lib/model-router/taxonomy.test.mjs +0 -410
  336. package/lib/model-router.test.mjs +0 -1207
  337. package/lib/org/activity.test.mjs +0 -134
  338. package/lib/org/approvals.test.mjs +0 -216
  339. package/lib/org/awareness.test.mjs +0 -159
  340. package/lib/org/board-mine-cache.test.mjs +0 -53
  341. package/lib/org/board.test.mjs +0 -187
  342. package/lib/org/bootstrap-context.test.mjs +0 -153
  343. package/lib/org/client.test.mjs +0 -1206
  344. package/lib/org/cohort-client.test.mjs +0 -126
  345. package/lib/org/cost-sync.test.mjs +0 -153
  346. package/lib/org/doctor.test.mjs +0 -346
  347. package/lib/org/engagement-ledger.test.mjs +0 -112
  348. package/lib/org/engagement.test.mjs +0 -739
  349. package/lib/org/handoff.test.mjs +0 -269
  350. package/lib/org/inbound/directedness.test.mjs +0 -668
  351. package/lib/org/inbound/facts.test.mjs +0 -471
  352. package/lib/org/inbound/hydrate.test.mjs +0 -908
  353. package/lib/org/inbound/index.test.mjs +0 -429
  354. package/lib/org/inbound/project.test.mjs +0 -287
  355. package/lib/org/integration-tools.test.mjs +0 -160
  356. package/lib/org/keys.test.mjs +0 -92
  357. package/lib/org/knowledge.test.mjs +0 -326
  358. package/lib/org/leases.test.mjs +0 -235
  359. package/lib/org/mesh-directives.test.mjs +0 -110
  360. package/lib/org/mesh-integration.test.mjs +0 -127
  361. package/lib/org/mesh.test.mjs +0 -400
  362. package/lib/org/messaging.test.mjs +0 -471
  363. package/lib/org/param-contract.test.mjs +0 -477
  364. package/lib/org/policy.test.mjs +0 -237
  365. package/lib/org/protocol.checksum.test.mjs +0 -90
  366. package/lib/org/protocol.test.mjs +0 -323
  367. package/lib/org/push.test.mjs +0 -792
  368. package/lib/org/registry.test.mjs +0 -100
  369. package/lib/org/resource-tools.test.mjs +0 -361
  370. package/lib/org/tool-access.test.mjs +0 -144
  371. package/lib/org/tool-surface-integration.test.mjs +0 -120
  372. package/lib/org/tool-surface.test.mjs +0 -1268
  373. package/lib/org/typing.test.mjs +0 -291
  374. package/lib/org/ui-parity.test.mjs +0 -560
  375. package/lib/org/verify.test.mjs +0 -194
  376. package/lib/org/work-ledger.test.mjs +0 -273
  377. package/lib/plan/adoption-e2e.test.mjs +0 -366
  378. package/lib/plan/budget-enforcement.test.mjs +0 -400
  379. package/lib/plan/compile.test.mjs +0 -382
  380. package/lib/plan/emit.test.mjs +0 -269
  381. package/lib/plan/explain.test.mjs +0 -188
  382. package/lib/prompts/parallelism.test.mjs +0 -177
  383. package/lib/rag/rag.test.mjs +0 -505
  384. package/lib/rate-guard.test.mjs +0 -272
  385. package/lib/reactive-gate.test.mjs +0 -57
  386. package/lib/render.test.mjs +0 -68
  387. package/lib/resource-governor.test.mjs +0 -488
  388. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  389. package/lib/scheduling/jitter.test.mjs +0 -140
  390. package/lib/secrets/broker.test.mjs +0 -280
  391. package/lib/secrets/providers.test.mjs +0 -274
  392. package/lib/security/audit-engine.test.mjs +0 -424
  393. package/lib/security/coerce-args.test.mjs +0 -281
  394. package/lib/security/dangerous-tools.test.mjs +0 -68
  395. package/lib/security/external-content.test.mjs +0 -84
  396. package/lib/security/redact.test.mjs +0 -441
  397. package/lib/security/secret-equal.test.mjs +0 -55
  398. package/lib/session/config.test.mjs +0 -92
  399. package/lib/session/feed-core.test.mjs +0 -198
  400. package/lib/session/first-run.test.mjs +0 -121
  401. package/lib/session/frontdoor.test.mjs +0 -205
  402. package/lib/session/handoffs.test.mjs +0 -183
  403. package/lib/session/identity.test.mjs +0 -180
  404. package/lib/session/inbox-claims.test.mjs +0 -286
  405. package/lib/session/launch-args.test.mjs +0 -157
  406. package/lib/session/liveness.test.mjs +0 -100
  407. package/lib/session/status-summary.test.mjs +0 -118
  408. package/lib/session-permissions.test.mjs +0 -120
  409. package/lib/setup/claude-probe.test.mjs +0 -187
  410. package/lib/setup/completeness.test.mjs +0 -110
  411. package/lib/setup/context-pack.test.mjs +0 -89
  412. package/lib/setup/enrich.test.mjs +0 -115
  413. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  414. package/lib/setup/integration.test.mjs +0 -162
  415. package/lib/setup/io.test.mjs +0 -77
  416. package/lib/setup/runner.test.mjs +0 -132
  417. package/lib/setup/sections/identity.test.mjs +0 -234
  418. package/lib/setup/sections/inventory.test.mjs +0 -198
  419. package/lib/setup/sections/learning.test.mjs +0 -81
  420. package/lib/setup/sections/mandate.test.mjs +0 -388
  421. package/lib/setup/sections/messaging.test.mjs +0 -127
  422. package/lib/setup/sections/model.test.mjs +0 -240
  423. package/lib/setup/sections/org.test.mjs +0 -346
  424. package/lib/setup/sections/orgmail.test.mjs +0 -118
  425. package/lib/setup/sections/recovery.test.mjs +0 -98
  426. package/lib/setup/sections/subagents.test.mjs +0 -429
  427. package/lib/setup/sections/verify.test.mjs +0 -175
  428. package/lib/setup/sot.test.mjs +0 -81
  429. package/lib/setup/state.test.mjs +0 -115
  430. package/lib/singleton.test.mjs +0 -151
  431. package/lib/subagents/cli.test.mjs +0 -389
  432. package/lib/subagents/client.test.mjs +0 -309
  433. package/lib/subagents/gap.test.mjs +0 -234
  434. package/lib/subagents/lock.test.mjs +0 -248
  435. package/lib/subagents/manifest.test.mjs +0 -175
  436. package/lib/subagents/refs.test.mjs +0 -204
  437. package/lib/subagents/resolve.test.mjs +0 -422
  438. package/lib/subagents/schema.test.mjs +0 -328
  439. package/lib/telemetry/alerts.test.mjs +0 -109
  440. package/lib/telemetry/collect.test.mjs +0 -1274
  441. package/lib/tool-definitions-integration.test.mjs +0 -83
  442. package/lib/tool-definitions.test.mjs +0 -437
  443. package/lib/upgrade/global-refresh.test.mjs +0 -65
  444. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  445. package/lib/upgrade/post-steps.test.mjs +0 -200
  446. package/lib/upgrade/verify.test.mjs +0 -164
  447. package/lib/util/fetch-timeout.test.mjs +0 -202
  448. package/lib/util/reconnect.test.mjs +0 -369
  449. package/lib/util/unhandled.test.mjs +0 -216
  450. package/lib/voice/outbound.test.mjs +0 -69
  451. package/lib/voice/session-rotation.test.mjs +0 -114
  452. package/lib/voice/stt.test.mjs +0 -226
  453. package/lib/voice/voice.test.mjs +0 -990
  454. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  455. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  456. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  457. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  458. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  459. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  460. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  461. package/scripts/ci/check.test.mjs +0 -194
  462. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  463. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  464. package/scripts/collective/hook-runner.test.mjs +0 -173
  465. package/scripts/cost/fleet-digest.test.mjs +0 -207
  466. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  467. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  468. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  469. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  470. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  471. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  472. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  473. package/scripts/daemon/assurance.test.mjs +0 -1791
  474. package/scripts/daemon/board-mirror.test.mjs +0 -165
  475. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  476. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  477. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  478. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  479. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  480. package/scripts/daemon/classifier.test.mjs +0 -266
  481. package/scripts/daemon/classify-kind.test.mjs +0 -40
  482. package/scripts/daemon/context-compiler.test.mjs +0 -406
  483. package/scripts/daemon/deliver.test.mjs +0 -564
  484. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  485. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  486. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  487. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  488. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  489. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  490. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  491. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  492. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  493. package/scripts/daemon/integration.test.mjs +0 -149
  494. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  495. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  496. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  497. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  498. package/scripts/daemon/responder-cost.test.mjs +0 -68
  499. package/scripts/daemon/responder-history.test.mjs +0 -221
  500. package/scripts/daemon/sdk-version.test.mjs +0 -31
  501. package/scripts/daemon/session-lock.test.mjs +0 -252
  502. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  503. package/scripts/daemon/typing-registry.test.mjs +0 -102
  504. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  505. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  506. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  507. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  508. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  509. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  510. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  511. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  512. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  513. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  514. package/scripts/poller-launchd/install.test.mjs +0 -243
  515. package/scripts/restore-from-backup.test.mjs +0 -181
  516. package/scripts/session/feed.test.mjs +0 -196
  517. package/scripts/session/supervisor-sh.test.mjs +0 -218
  518. package/scripts/session/supervisor.test.mjs +0 -482
  519. package/scripts/setup/configure-macos.test.mjs +0 -306
  520. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  521. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  522. package/scripts/setup/generate-capability.test.mjs +0 -134
  523. package/scripts/setup/init-agent.test.mjs +0 -370
  524. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  525. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  526. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -1,336 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { mkdtempSync, mkdirSync, writeFileSync, readdirSync, rmSync } from "node:fs";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
-
7
- import { markDeferred, promoteDeferred } from "./inbox-deferral.mjs";
8
-
9
- function makeAgentRoot() {
10
- const root = mkdtempSync(join(tmpdir(), "maestro-deferral-"));
11
- mkdirSync(join(root, "state", "inbox", "slack"), { recursive: true });
12
- return root;
13
- }
14
-
15
- function writeInboxItem(root, name, { channel_id, timestamp, id = "test-id", thread_id, scope_id }) {
16
- const body = [
17
- `id: "${id}"`,
18
- `service: "slack"`,
19
- `channel_id: "${channel_id}"`,
20
- ...(thread_id ? [`thread_id: "${thread_id}"`] : []),
21
- ...(scope_id ? [`scope_id: "${scope_id}"`] : []),
22
- `timestamp: "${timestamp}"`,
23
- `content: |`,
24
- ` body`,
25
- "",
26
- ].join("\n");
27
- writeFileSync(join(root, "state", "inbox", "slack", name), body);
28
- }
29
-
30
- test("markDeferred renames live file to .deferred", () => {
31
- const root = makeAgentRoot();
32
- try {
33
- writeInboxItem(root, "msg-A.yaml", {
34
- channel_id: "D001",
35
- timestamp: "2026-05-13T00:00:00Z",
36
- id: "msg-A",
37
- });
38
- const n = markDeferred({ id: "msg-A" }, "slack", root);
39
- assert.equal(n, 1);
40
- const files = readdirSync(join(root, "state", "inbox", "slack"));
41
- assert.deepEqual(files, ["msg-A.yaml.deferred"]);
42
- } finally {
43
- rmSync(root, { recursive: true, force: true });
44
- }
45
- });
46
-
47
- test("markDeferred is idempotent — already-deferred files are skipped", () => {
48
- const root = makeAgentRoot();
49
- try {
50
- writeInboxItem(root, "msg-B.yaml.deferred", {
51
- channel_id: "D001",
52
- timestamp: "2026-05-13T00:00:00Z",
53
- id: "msg-B",
54
- });
55
- const n = markDeferred({ id: "msg-B" }, "slack", root);
56
- assert.equal(n, 0);
57
- } finally {
58
- rmSync(root, { recursive: true, force: true });
59
- }
60
- });
61
-
62
- test("markDeferred is no-op when item has no matching file (e.g. backlog)", () => {
63
- const root = makeAgentRoot();
64
- try {
65
- const n = markDeferred({ id: "no-such-id" }, "slack", root);
66
- assert.equal(n, 0);
67
- } finally {
68
- rmSync(root, { recursive: true, force: true });
69
- }
70
- });
71
-
72
- test("promoteDeferred with single item: renames back to live", () => {
73
- const root = makeAgentRoot();
74
- try {
75
- writeInboxItem(root, "msg-C.yaml.deferred", {
76
- channel_id: "D099N1JEA10",
77
- timestamp: "2026-05-13T00:00:00Z",
78
- id: "msg-C",
79
- });
80
- const r = promoteDeferred("D099N1JEA10", root);
81
- assert.equal(r.promoted, 1);
82
- assert.equal(r.bundled, 0);
83
- assert.equal(r.service, "slack");
84
- const files = readdirSync(join(root, "state", "inbox", "slack"));
85
- assert.deepEqual(files, ["msg-C.yaml"]);
86
- } finally {
87
- rmSync(root, { recursive: true, force: true });
88
- }
89
- });
90
-
91
- test("promoteDeferred with multiple items: keeps latest, bundles rest", () => {
92
- const root = makeAgentRoot();
93
- try {
94
- writeInboxItem(root, "msg-1.yaml.deferred", {
95
- channel_id: "D001",
96
- timestamp: "2026-05-13T00:00:00Z",
97
- id: "msg-1",
98
- });
99
- writeInboxItem(root, "msg-2.yaml.deferred", {
100
- channel_id: "D001",
101
- timestamp: "2026-05-13T00:01:00Z",
102
- id: "msg-2",
103
- });
104
- writeInboxItem(root, "msg-3.yaml.deferred", {
105
- channel_id: "D001",
106
- timestamp: "2026-05-13T00:02:00Z",
107
- id: "msg-3",
108
- });
109
- const r = promoteDeferred("D001", root);
110
- assert.equal(r.promoted, 1);
111
- assert.equal(r.bundled, 2);
112
- const files = readdirSync(join(root, "state", "inbox", "slack")).sort();
113
- assert.deepEqual(files, [
114
- "msg-1.yaml.processed-bundled",
115
- "msg-2.yaml.processed-bundled",
116
- "msg-3.yaml", // latest survives as live
117
- ]);
118
- } finally {
119
- rmSync(root, { recursive: true, force: true });
120
- }
121
- });
122
-
123
- test("promoteDeferred bundles WITHIN a surface, not across the whole channel", () => {
124
- // A directed 1:1 DM thread AND the actor's doc-comment events resolve to the
125
- // same channel_id. Bundling latest-wins across the channel would drop the DM
126
- // just because a newer doc event landed. Each surface must promote its own
127
- // latest; nothing is bundled across surfaces.
128
- const root = makeAgentRoot();
129
- try {
130
- // The directed DM (one message, thread T-DM).
131
- writeInboxItem(root, "dm-1.yaml.deferred", {
132
- channel_id: "room-1", thread_id: "T-DM", id: "dm-1",
133
- timestamp: "2026-05-13T00:00:30Z",
134
- });
135
- // Two doc-comment events on the same room but a different surface (scope S-DOC),
136
- // the later of which would have "won" under the old channel-wide bundling.
137
- writeInboxItem(root, "doc-1.yaml.deferred", {
138
- channel_id: "room-1", scope_id: "S-DOC", id: "doc-1",
139
- timestamp: "2026-05-13T00:01:00Z",
140
- });
141
- writeInboxItem(root, "doc-2.yaml.deferred", {
142
- channel_id: "room-1", scope_id: "S-DOC", id: "doc-2",
143
- timestamp: "2026-05-13T00:02:00Z",
144
- });
145
- const r = promoteDeferred("room-1", root);
146
- assert.equal(r.promoted, 2, "the DM and the latest doc event are BOTH promoted");
147
- assert.equal(r.bundled, 1, "only the older doc event (same surface) is bundled");
148
- const files = readdirSync(join(root, "state", "inbox", "slack")).sort();
149
- assert.deepEqual(files, [
150
- "dm-1.yaml", // the directed DM survives — NOT dropped
151
- "doc-1.yaml.processed-bundled", // older doc event folded into doc-2
152
- "doc-2.yaml", // latest doc event promoted
153
- ]);
154
- } finally {
155
- rmSync(root, { recursive: true, force: true });
156
- }
157
- });
158
-
159
- test("promoteDeferred ignores items in other channels", () => {
160
- const root = makeAgentRoot();
161
- try {
162
- writeInboxItem(root, "for-A.yaml.deferred", {
163
- channel_id: "D-A",
164
- timestamp: "2026-05-13T00:00:00Z",
165
- id: "for-A",
166
- });
167
- writeInboxItem(root, "for-B.yaml.deferred", {
168
- channel_id: "D-B",
169
- timestamp: "2026-05-13T00:00:00Z",
170
- id: "for-B",
171
- });
172
- const r = promoteDeferred("D-A", root);
173
- assert.equal(r.promoted, 1);
174
- assert.equal(r.bundled, 0);
175
- const files = readdirSync(join(root, "state", "inbox", "slack")).sort();
176
- assert.deepEqual(files, ["for-A.yaml", "for-B.yaml.deferred"]);
177
- } finally {
178
- rmSync(root, { recursive: true, force: true });
179
- }
180
- });
181
-
182
- test("promoteDeferred no-op when nothing to promote", () => {
183
- const root = makeAgentRoot();
184
- try {
185
- const r = promoteDeferred("D999", root);
186
- assert.equal(r.promoted, 0);
187
- assert.equal(r.bundled, 0);
188
- assert.equal(r.service, null);
189
- } finally {
190
- rmSync(root, { recursive: true, force: true });
191
- }
192
- });
193
-
194
- // ---------------------------------------------------------------------------
195
- // Audit L7 — block-scalar body must not be mis-parsed for routing fields
196
- // ---------------------------------------------------------------------------
197
-
198
- /**
199
- * Write an inbox item whose `content` block scalar body itself contains lines
200
- * that look like top-level `channel_id:` / `timestamp:` keys. A naive
201
- * `^\s*field:` regex would match the INDENTED body line and mis-route.
202
- */
203
- function writeInboxItemWithTrickyBody(root, name, { channel_id, timestamp, id, bodyChannel, bodyTimestamp }) {
204
- const lines = [
205
- `id: "${id}"`,
206
- `service: "slack"`,
207
- `channel_id: "${channel_id}"`,
208
- `timestamp: "${timestamp}"`,
209
- `content: |`,
210
- ` Hi team — quoting an old config below:`,
211
- ` channel_id: ${bodyChannel}`,
212
- ` timestamp: ${bodyTimestamp}`,
213
- ` please review.`,
214
- "",
215
- ];
216
- writeFileSync(join(root, "state", "inbox", "slack", name), lines.join("\n"));
217
- }
218
-
219
- test("L7: body line `channel_id:` in a block scalar does not mis-route", () => {
220
- const root = makeAgentRoot();
221
- try {
222
- // Real top-level channel is D-REAL. The body mentions D-EVIL on an
223
- // indented `channel_id:` line — promote must target D-REAL only.
224
- writeInboxItemWithTrickyBody(root, "tricky.yaml.deferred", {
225
- channel_id: "D-REAL",
226
- timestamp: "2026-05-13T00:00:00Z",
227
- id: "tricky",
228
- bodyChannel: "D-EVIL",
229
- bodyTimestamp: "1999-01-01T00:00:00Z",
230
- });
231
-
232
- // Promoting the body's channel must find NOTHING (the body line is not a
233
- // real top-level field).
234
- const evil = promoteDeferred("D-EVIL", root);
235
- assert.equal(evil.promoted, 0, "must not match the indented body channel_id");
236
- assert.equal(evil.bundled, 0);
237
-
238
- // Promoting the genuine top-level channel must succeed.
239
- const real = promoteDeferred("D-REAL", root);
240
- assert.equal(real.promoted, 1, "real top-level channel_id must match");
241
- assert.equal(real.bundled, 0);
242
- const files = readdirSync(join(root, "state", "inbox", "slack"));
243
- assert.deepEqual(files, ["tricky.yaml"]);
244
- } finally {
245
- rmSync(root, { recursive: true, force: true });
246
- }
247
- });
248
-
249
- test("L7: latest-wins uses top-level timestamp, not a body `timestamp:` line", () => {
250
- const root = makeAgentRoot();
251
- try {
252
- // Two items in the same channel. Item B is genuinely later by its
253
- // top-level timestamp, but its block-scalar body contains an EARLY
254
- // `timestamp:` line. A naive parser could read the body line, pick A as
255
- // "latest" and bundle B. Correct behaviour: B wins (later top-level ts).
256
- writeInboxItem(root, "A.yaml.deferred", {
257
- channel_id: "D-TS",
258
- timestamp: "2026-05-13T00:00:00Z",
259
- id: "A",
260
- });
261
- writeInboxItemWithTrickyBody(root, "B.yaml.deferred", {
262
- channel_id: "D-TS",
263
- timestamp: "2026-05-13T09:00:00Z", // genuinely later
264
- id: "B",
265
- bodyChannel: "D-TS",
266
- bodyTimestamp: "2000-01-01T00:00:00Z", // decoy early ts in the body
267
- });
268
-
269
- const r = promoteDeferred("D-TS", root);
270
- assert.equal(r.promoted, 1);
271
- assert.equal(r.bundled, 1);
272
- const files = readdirSync(join(root, "state", "inbox", "slack")).sort();
273
- assert.deepEqual(files, [
274
- "A.yaml.processed-bundled", // earlier item bundled
275
- "B.yaml", // later top-level timestamp survives as live
276
- ]);
277
- } finally {
278
- rmSync(root, { recursive: true, force: true });
279
- }
280
- });
281
-
282
- // --- service-dir discovery (the cohort/orgmail regression) -------------------
283
-
284
- /** Write a deferred item into ANY service dir (creating it). */
285
- function writeDeferredIn(root, service, name, { channel_id, timestamp, id }) {
286
- mkdirSync(join(root, "state", "inbox", service), { recursive: true });
287
- const body = [
288
- `id: "${id}"`,
289
- `service: "${service}"`,
290
- `channel_id: "${channel_id}"`,
291
- `timestamp: "${timestamp}"`,
292
- `content: |`,
293
- ` body`,
294
- "",
295
- ].join("\n");
296
- writeFileSync(join(root, "state", "inbox", service, name), body);
297
- }
298
-
299
- test("promoteDeferred finds services the hardcoded list never knew about (cohort/orgmail)", () => {
300
- const root = makeAgentRoot();
301
- try {
302
- writeDeferredIn(root, "cohort", "cohort-msg.yaml.deferred", {
303
- channel_id: "ch_build",
304
- timestamp: "2026-08-11T00:00:00Z",
305
- id: "cohort-msg",
306
- });
307
- const r = promoteDeferred("ch_build", root);
308
- assert.equal(r.promoted, 1, "a deferred Cohort message was previously un-promotable forever");
309
- assert.equal(r.service, "cohort");
310
- assert.deepEqual(readdirSync(join(root, "state", "inbox", "cohort")), ["cohort-msg.yaml"]);
311
- } finally {
312
- rmSync(root, { recursive: true, force: true });
313
- }
314
- });
315
-
316
- test("promoteDeferred signals the inbox wake so the promoted item is not stuck behind the poll", async () => {
317
- const { waitForInboxWake, resetInboxWake } = await import("./inbox-wake.mjs");
318
- const root = makeAgentRoot();
319
- resetInboxWake();
320
- try {
321
- writeDeferredIn(root, "cohort", "burst-2.yaml.deferred", {
322
- channel_id: "ch_dm",
323
- timestamp: "2026-08-11T00:00:01Z",
324
- id: "burst-2",
325
- });
326
- // scanMs is disabled so ONLY the in-process signal can settle this.
327
- const waiting = waitForInboxWake({ agentRoot: root, timeoutMs: 3_000, scanMs: 600_000 });
328
- setTimeout(() => promoteDeferred("ch_dm", root), 20);
329
- const woke = await waiting;
330
- assert.equal(woke.woke, true);
331
- assert.equal(woke.service, "cohort");
332
- } finally {
333
- resetInboxWake();
334
- rmSync(root, { recursive: true, force: true });
335
- }
336
- });
@@ -1,199 +0,0 @@
1
- /**
2
- * inbox-wake.test.mjs — the daemon's "something landed" wake signal.
3
- *
4
- * Exercised against a REAL temp filesystem (fs.watch semantics are the thing
5
- * under test), plus one injected-watch case that proves the scan floor carries
6
- * the load when every watcher is dead — the property that keeps a missed event
7
- * from turning into a silently late agent.
8
- *
9
- * Run: node --test scripts/daemon/inbox-wake.test.mjs
10
- */
11
-
12
- import { test } from "node:test";
13
- import assert from "node:assert/strict";
14
- import { mkdtempSync, mkdirSync, writeFileSync, renameSync, rmSync } from "node:fs";
15
- import { tmpdir } from "node:os";
16
- import { join } from "node:path";
17
-
18
- import {
19
- waitForInboxWake,
20
- signalInbox,
21
- findLiveInboxService,
22
- isLiveItemName,
23
- resetInboxWake,
24
- DEFAULT_SERVICES,
25
- } from "./inbox-wake.mjs";
26
-
27
- function makeRoot() {
28
- const root = mkdtempSync(join(tmpdir(), "inbox-wake-"));
29
- mkdirSync(join(root, "state", "inbox", "cohort"), { recursive: true });
30
- return root;
31
- }
32
-
33
- function writeItem(root, service, name, body = "id: x\n") {
34
- writeFileSync(join(root, "state", "inbox", service, name), body);
35
- }
36
-
37
- // --- the predicate ----------------------------------------------------------
38
-
39
- test("isLiveItemName: only un-drained, non-dot files count as work", () => {
40
- assert.equal(isLiveItemName("2026-08-11-abc.yaml"), true);
41
- assert.equal(isLiveItemName("2026-08-11-abc.yaml.processed"), false);
42
- assert.equal(isLiveItemName("2026-08-11-abc.yaml.deferred"), false);
43
- assert.equal(isLiveItemName("2026-08-11-abc.yaml.processed-bundled"), false);
44
- assert.equal(isLiveItemName(".DS_Store"), false);
45
- assert.equal(isLiveItemName(""), false);
46
- });
47
-
48
- test("findLiveInboxService: names the service holding work, else null", () => {
49
- const root = makeRoot();
50
- try {
51
- assert.equal(findLiveInboxService({ agentRoot: root }), null);
52
- writeItem(root, "cohort", "a.yaml.processed");
53
- assert.equal(findLiveInboxService({ agentRoot: root }), null, "a drained item is not work");
54
- writeItem(root, "cohort", "b.yaml");
55
- assert.equal(findLiveInboxService({ agentRoot: root }), "cohort");
56
- } finally {
57
- rmSync(root, { recursive: true, force: true });
58
- }
59
- });
60
-
61
- test("the cohort + orgmail dirs are in the default service set (the ones that were missed)", () => {
62
- assert.ok(DEFAULT_SERVICES.includes("cohort"));
63
- assert.ok(DEFAULT_SERVICES.includes("orgmail"));
64
- assert.ok(DEFAULT_SERVICES.includes("telegram"));
65
- });
66
-
67
- // --- the wake ---------------------------------------------------------------
68
-
69
- test("returns immediately when work is already waiting (never sleep on a full inbox)", async () => {
70
- const root = makeRoot();
71
- try {
72
- writeItem(root, "cohort", "a.yaml");
73
- const r = await waitForInboxWake({ agentRoot: root, timeoutMs: 5_000 });
74
- assert.equal(r.woke, true);
75
- assert.equal(r.reason, "pending");
76
- assert.equal(r.service, "cohort");
77
- assert.equal(r.waitedMs, 0);
78
- } finally {
79
- rmSync(root, { recursive: true, force: true });
80
- }
81
- });
82
-
83
- test("wakes on a file written by another writer, far inside the 60s poll interval", async () => {
84
- const root = makeRoot();
85
- try {
86
- const started = Date.now();
87
- const waiting = waitForInboxWake({ agentRoot: root, timeoutMs: 8_000 });
88
- setTimeout(() => writeItem(root, "cohort", "late.yaml"), 50);
89
- const r = await waiting;
90
- assert.equal(r.woke, true);
91
- assert.ok(["watch", "scan"].includes(r.reason), `unexpected reason ${r.reason}`);
92
- assert.equal(r.service, "cohort");
93
- assert.ok(Date.now() - started < 3_000, "a wake must not take seconds");
94
- } finally {
95
- rmSync(root, { recursive: true, force: true });
96
- }
97
- });
98
-
99
- test("does NOT wake when the only change is an item being drained", async () => {
100
- const root = makeRoot();
101
- try {
102
- writeItem(root, "cohort", "a.yaml");
103
- // Drain it, then wait: renaming to `.processed` raises watch events for BOTH
104
- // names, so an event-driven wake would spin here.
105
- renameSync(
106
- join(root, "state", "inbox", "cohort", "a.yaml"),
107
- join(root, "state", "inbox", "cohort", "a.yaml.processed")
108
- );
109
- const r = await waitForInboxWake({ agentRoot: root, timeoutMs: 400 });
110
- assert.equal(r.woke, false);
111
- assert.equal(r.reason, "timeout");
112
- } finally {
113
- rmSync(root, { recursive: true, force: true });
114
- }
115
- });
116
-
117
- test("times out cleanly with an empty inbox (the loop shape is unchanged)", async () => {
118
- const root = makeRoot();
119
- try {
120
- const r = await waitForInboxWake({ agentRoot: root, timeoutMs: 200 });
121
- assert.deepEqual(
122
- { woke: r.woke, reason: r.reason, service: r.service },
123
- { woke: false, reason: "timeout", service: null }
124
- );
125
- assert.ok(r.waitedMs >= 150);
126
- } finally {
127
- rmSync(root, { recursive: true, force: true });
128
- }
129
- });
130
-
131
- test("signalInbox wakes an in-process waiter (the dispatcher's promote path)", async () => {
132
- const root = makeRoot();
133
- resetInboxWake();
134
- try {
135
- const waiting = waitForInboxWake({ agentRoot: root, timeoutMs: 5_000, scanMs: 60_000 });
136
- setTimeout(() => {
137
- // Simulate promoteDeferred: the file becomes live, then we nudge.
138
- writeItem(root, "cohort", "promoted.yaml");
139
- signalInbox("promote");
140
- }, 20);
141
- const r = await waiting;
142
- assert.equal(r.woke, true);
143
- assert.equal(r.service, "cohort");
144
- } finally {
145
- resetInboxWake();
146
- rmSync(root, { recursive: true, force: true });
147
- }
148
- });
149
-
150
- test("signalInbox with no work does NOT wake (a spurious nudge is free)", async () => {
151
- const root = makeRoot();
152
- resetInboxWake();
153
- try {
154
- const waiting = waitForInboxWake({ agentRoot: root, timeoutMs: 400, scanMs: 60_000 });
155
- setTimeout(() => signalInbox("noise"), 20);
156
- const r = await waiting;
157
- assert.equal(r.woke, false);
158
- } finally {
159
- resetInboxWake();
160
- rmSync(root, { recursive: true, force: true });
161
- }
162
- });
163
-
164
- test("with EVERY watcher dead the scan floor still wakes it — and says so once", async () => {
165
- const root = makeRoot();
166
- const logs = [];
167
- try {
168
- const watchImpl = () => {
169
- throw new Error("EMFILE: watcher limit reached");
170
- };
171
- const waiting = waitForInboxWake({
172
- agentRoot: root,
173
- timeoutMs: 6_000,
174
- scanMs: 100,
175
- watchImpl,
176
- log: (m) => logs.push(m),
177
- });
178
- setTimeout(() => writeItem(root, "cohort", "floor.yaml"), 30);
179
- const r = await waiting;
180
- assert.equal(r.woke, true);
181
- assert.equal(r.reason, "scan", "correctness must not depend on fs.watch");
182
- assert.equal(logs.length, 1, "a dead watcher is reported — fail-open, never silent");
183
- assert.match(logs[0], /falling back to the 100ms rescan/);
184
- } finally {
185
- resetInboxWake();
186
- rmSync(root, { recursive: true, force: true });
187
- }
188
- });
189
-
190
- test("a missing service dir is not an error (an un-enrolled agent just times out)", async () => {
191
- const root = mkdtempSync(join(tmpdir(), "inbox-wake-bare-"));
192
- try {
193
- const r = await waitForInboxWake({ agentRoot: root, timeoutMs: 200 });
194
- assert.equal(r.woke, false);
195
- assert.equal(r.reason, "timeout");
196
- } finally {
197
- rmSync(root, { recursive: true, force: true });
198
- }
199
- });
@@ -1,149 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * test-integration.mjs — End-to-end integration tests for session lock + context compiler pipeline
4
- */
5
-
6
- import { describe, it, beforeEach, afterEach } from "node:test";
7
- import assert from "node:assert/strict";
8
- import { mkdirSync, rmSync, writeFileSync, readFileSync, existsSync } from "fs";
9
- import { join, dirname } from "path";
10
- import { fileURLToPath } from "url";
11
-
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const TEST_DIR = join(__dirname, "../../.test-integration");
14
-
15
- // Set env BEFORE importing modules. context-compiler.mjs reads its repo root
16
- // from __TEST_AGENT_DIR (the legacy __TEST_SOPHIE_DIR name was never honoured by
17
- // the module, so the disclosure-boundary fixtures were written here but read
18
- // from the real repo — the cause of the end-to-end failure). Set both: the
19
- // session-lock module reads __TEST_SESSION_DIR; the compiler reads
20
- // __TEST_AGENT_DIR + __TEST_SESSION_DIR.
21
- process.env.__TEST_SESSION_DIR = join(TEST_DIR, "state", "sessions");
22
- process.env.__TEST_AGENT_DIR = TEST_DIR;
23
- process.env.__TEST_SOPHIE_DIR = TEST_DIR; // kept for any legacy reader; harmless
24
-
25
- function setupTestEnv() {
26
- mkdirSync(join(TEST_DIR, "state", "sessions", "locks"), { recursive: true });
27
- mkdirSync(join(TEST_DIR, "memory", "profiles", "users"), { recursive: true });
28
- mkdirSync(join(TEST_DIR, "memory", "interactions", "slack", "dm-test-user"), { recursive: true });
29
- mkdirSync(join(TEST_DIR, "policies"), { recursive: true });
30
- writeFileSync(join(TEST_DIR, "state", "sessions", "active.json"), "{}");
31
-
32
- writeFileSync(join(TEST_DIR, "policies", "information-barriers.yaml"), `domains:
33
- internal-legal:
34
- description: "Northwind legal strategy, GC advice, privilege-protected"
35
- default_sensitivity: high
36
- strategic-initiative:
37
- description: "the strategic initiative M&A targets, deal terms, pipeline"
38
- default_sensitivity: critical
39
- public:
40
- description: "Published information, press, website"
41
- default_sensitivity: none
42
- `);
43
- }
44
-
45
- function cleanTestEnv() {
46
- rmSync(TEST_DIR, { recursive: true, force: true });
47
- }
48
-
49
- const { acquireLock, releaseLock, registerSent, checkRecentlySent } = await import("./session-lock.mjs");
50
- const { compileContext } = await import("./context-compiler.mjs");
51
-
52
- // ─── Integration tests ─────────────────────────────────────────────────
53
-
54
- describe("Integration: lock → compile → prompt pipeline", () => {
55
- beforeEach(setupTestEnv);
56
- afterEach(cleanTestEnv);
57
-
58
- it("acquireLock prevents duplicate processing of same item", () => {
59
- const first = acquireLock("slack-C001-12345", { sender: "Test", channel: "C001" });
60
- assert.equal(first.acquired, true);
61
-
62
- const second = acquireLock("slack-C001-12345", { sender: "Test", channel: "C001" });
63
- assert.equal(second.acquired, false);
64
-
65
- releaseLock("slack-C001-12345");
66
- });
67
-
68
- it("sent-registry prevents duplicate sends across quick-reply and holding paths", () => {
69
- // Holding message sent
70
- registerSent("C001", "12345.6789", "holding", "s-1", "Looking into this now");
71
-
72
- // A quick reply to the SAME channel+thread is blocked by the thread-cooldown
73
- // rule (Rule 2): once anything has been sent to a thread, the whole thread
74
- // cools down so the quick-reply path and the full-session path can't both
75
- // fire into the same thread. (The original assertion expected the different
76
- // type to be allowed — that predated Rule 2 and was stale; Rule 2 is the
77
- // whole point of this test, "prevents duplicate sends across paths".)
78
- const quickCheck = checkRecentlySent("C001", "12345.6789", "quick_reply");
79
- assert.equal(quickCheck.allowed, false);
80
- assert.match(quickCheck.reason, /thread cooldown/);
81
-
82
- // Another holding to same channel+thread is likewise blocked.
83
- const holdCheck = checkRecentlySent("C001", "12345.6789", "holding");
84
- assert.equal(holdCheck.allowed, false);
85
-
86
- // But a different, untouched thread is still free to receive a quick reply —
87
- // the cooldown is scoped to the thread, not the whole channel (non-DM).
88
- const otherThread = checkRecentlySent("C001", "99999.0000", "quick_reply");
89
- assert.equal(otherThread.allowed, true);
90
- });
91
-
92
- it("context compiler respects disclosure boundaries end-to-end", async () => {
93
- // Create a profile with restrictions
94
- writeFileSync(
95
- join(TEST_DIR, "memory", "profiles", "users", "test-user.yaml"),
96
- `name: Test User\ninformation_boundaries:\n excluded_domains:\n - strategic-initiative\n`
97
- );
98
-
99
- // Create history with mixed content
100
- const today = new Date().toISOString().split("T")[0];
101
- writeFileSync(
102
- join(TEST_DIR, "memory", "interactions", "slack", "dm-test-user", `${today}.jsonl`),
103
- [
104
- JSON.stringify({ ts: "100", from: "test-user", content: "What is the status?", received_at: "2026-04-06T10:00:00Z" }),
105
- JSON.stringify({ ts: "101", from: "robin", content: "the strategic initiative deal terms are looking good", received_at: "2026-04-06T10:01:00Z" }),
106
- JSON.stringify({ ts: "102", from: "robin", content: "The public website update is live", received_at: "2026-04-06T10:02:00Z" }),
107
- ].join("\n") + "\n"
108
- );
109
-
110
- const { contextBlock } = await compileContext(
111
- { sender: "Test User", channel: "D001", channel_id: "D001" },
112
- { priority: "normal", model: "sonnet", action: "respond" },
113
- {}
114
- );
115
-
116
- // Public content should be included
117
- assert.ok(contextBlock.includes("website update is live"));
118
- // Strategic-initiative content should be filtered. (The old assertion
119
- // checked for "Aether deal terms" — absent from the fixture — so it was a
120
- // tautology; assert against the actual excluded fixture text.)
121
- assert.ok(!contextBlock.includes("strategic initiative deal terms"));
122
- // Should have disclosure boundaries
123
- assert.ok(contextBlock.includes("DO NOT mention"));
124
- });
125
-
126
- it("active sessions are visible in compiled context", async () => {
127
- writeFileSync(
128
- join(TEST_DIR, "state", "sessions", "active.json"),
129
- JSON.stringify({
130
- "s-999": {
131
- sender: "Alice",
132
- channel: "C002",
133
- summary: "Preparing board pack",
134
- model: "opus",
135
- startTime: Date.now() - 120000,
136
- },
137
- })
138
- );
139
-
140
- const { contextBlock } = await compileContext(
141
- { sender: "Test", channel: "C001" },
142
- { priority: "normal", model: "sonnet", action: "respond" },
143
- {}
144
- );
145
-
146
- assert.ok(contextBlock.includes("Preparing board pack"));
147
- assert.ok(contextBlock.includes("Active Sessions"));
148
- });
149
- });