@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,533 +0,0 @@
1
- /**
2
- * session-outcomes.test.mjs — the daemon's OUTCOME DISCIPLINE.
3
- *
4
- * hq guarantees a live call's action points become real board rows and sweeps the
5
- * ones the chair never wrote. These tests assert the daemon's parity:
6
- *
7
- * CAPTURE the OUTCOMES block is read off the turn's final text, deterministically
8
- * and bounded, and ordinary prose can never be mistaken for a commitment
9
- * GATE a landed outcome is ORG-VISIBLE, so only an already-org-visible source
10
- * may produce one — fail-closed, unlike the rest of this module
11
- * LAND each commitment goes through the EXISTING org protocol (board.create +
12
- * one knowledge.append episode) — no new methods
13
- * SWEEP the post-session hook is DRIVEN, idempotent, fail-open, and OFF unless
14
- * DAEMON_SESSION_OUTCOMES=1
15
- *
16
- * The org collaborators are injected, so nothing here touches a network or a DB.
17
- */
18
-
19
- import { test, beforeEach } from "node:test";
20
- import assert from "node:assert/strict";
21
-
22
- import * as so from "./session-outcomes.mjs";
23
-
24
- const ENABLED_CFG = { org: { cohort: { enabled: true, base: "https://org.example" } } };
25
-
26
- /**
27
- * A public Slack channel message — the ONLY source whose outcomes may land.
28
- * `channel_type: "channel"` is what establishes that; the `C…` id does NOT
29
- * (Slack mints `C…` ids for private channels too).
30
- */
31
- const PUBLIC_ITEM = {
32
- service: "slack",
33
- channel: "general",
34
- channel_id: "C0GENERAL",
35
- channel_type: "channel",
36
- subject: "#general",
37
- };
38
-
39
- /** A createItem fake that records its calls and returns a success frame. */
40
- function fakeCreate(sink, behaviour) {
41
- return async (item, opts) => {
42
- sink.push({ item, opts });
43
- if (typeof behaviour === "function") return behaviour(item);
44
- return { ok: true, result: { id: item.itemId } };
45
- };
46
- }
47
-
48
- function fakeRemember(sink) {
49
- return async (text, opts) => {
50
- sink.push({ text, opts });
51
- return { ok: true, mode: "append", id: "k1" };
52
- };
53
- }
54
-
55
- beforeEach(() => so._resetSessionOutcomes());
56
-
57
- // ---------------------------------------------------------------------------
58
- // CAPTURE
59
- // ---------------------------------------------------------------------------
60
-
61
- test("CAPTURE: reads the OUTCOMES block, with owner and timing", () => {
62
- const text = [
63
- "Sent Alice the revised terms and flagged the pricing gap.",
64
- "",
65
- "OUTCOMES:",
66
- "- Send the redlined MSA to Alice — owner: Robin — due: Friday",
67
- "- Book the pricing review with Finance",
68
- "",
69
- "Let me know if anything else comes up.",
70
- ].join("\n");
71
-
72
- const out = so.parseOutcomes(text);
73
- assert.equal(out.length, 2, "two commitments");
74
- assert.equal(out[0].title, "Send the redlined MSA to Alice");
75
- assert.equal(out[0].owner, "Robin");
76
- assert.equal(out[0].due, "Friday");
77
- assert.equal(out[1].title, "Book the pricing review with Finance");
78
- assert.equal(out[1].owner, undefined, "no owner invented");
79
- });
80
-
81
- test("CAPTURE: tolerates markdown decoration around the literal heading", () => {
82
- const text = [
83
- "## Outcomes",
84
- "",
85
- "1. Draft the board update",
86
- "2. [ ] Chase the vendor SOC2",
87
- ].join("\n");
88
- const out = so.parseOutcomes(text);
89
- assert.deepEqual(out.map((o) => o.title), ["Draft the board update", "Chase the vendor SOC2"]);
90
-
91
- const bold = so.parseOutcomes(["**OUTCOMES:**", "- Ship the fix"].join("\n"));
92
- assert.deepEqual(bold.map((o) => o.title), ["Ship the fix"]);
93
- });
94
-
95
- test("CAPTURE: the capture surface is EXACTLY the contract the prompt establishes", () => {
96
- // REGRESSION. These headings used to be accepted, which made the sweep wider
97
- // than the contract: a turn that merely wrote a prose "Next steps:" section —
98
- // possibly from a prompt that never carried OUTCOME_DISCIPLINE at all, since
99
- // the prompt injects it at build time while the sweep re-reads the flag at
100
- // close time — silently minted org-visible board rows.
101
- const wider = [
102
- "## Next steps\n\n1. Draft the board update",
103
- "Next steps:\n- Draft the board update",
104
- "**Action items:**\n- Ship the fix",
105
- "Action points:\n- Ship the fix",
106
- "Commitments:\n- Ship the fix",
107
- ];
108
- for (const text of wider) {
109
- assert.deepEqual(
110
- so.parseOutcomes(text),
111
- [],
112
- `only the literal OUTCOMES heading is machine-read: ${JSON.stringify(text)}`
113
- );
114
- }
115
- });
116
-
117
- test("CAPTURE: an honest empty block stays empty", () => {
118
- assert.deepEqual(so.parseOutcomes("Answered inline.\n\nOUTCOMES: none"), []);
119
- assert.deepEqual(so.parseOutcomes("OUTCOMES:\n- none\n"), []);
120
- assert.deepEqual(so.parseOutcomes(""), []);
121
- });
122
-
123
- test("CAPTURE: prose is never mistaken for a commitment block", () => {
124
- // No colon after the keyword ⇒ not a heading. This is the false-positive that
125
- // would otherwise put junk on the org board.
126
- assert.deepEqual(so.parseOutcomes("Action items are clear from the thread."), []);
127
- assert.deepEqual(so.parseOutcomes("The outcomes were disappointing this quarter."), []);
128
- });
129
-
130
- test("CAPTURE: the block terminates at the blank line, not at the end of the reply", () => {
131
- const text = ["OUTCOMES:", "- Do the thing", "", "- this is a different list entirely"].join("\n");
132
- assert.deepEqual(so.parseOutcomes(text).map((o) => o.title), ["Do the thing"]);
133
- });
134
-
135
- test("CAPTURE: bounded and deduped — a runaway summary cannot flood the board", () => {
136
- const lines = ["OUTCOMES:"];
137
- for (let i = 0; i < 30; i += 1) lines.push(`- step ${i}`);
138
- const out = so.parseOutcomes(lines.join("\n"));
139
- assert.equal(out.length, so.MAX_OUTCOMES, `capped at ${so.MAX_OUTCOMES}`);
140
-
141
- const dupes = so.parseOutcomes(["OUTCOMES:", "- Ship it", "- ship IT", "- Ship it"].join("\n"));
142
- assert.equal(dupes.length, 1, "case-insensitively deduped");
143
-
144
- const long = so.parseOutcomes(`OUTCOMES:\n- ${"x".repeat(500)}`);
145
- assert.ok(long[0].title.length <= 200, "title bounded");
146
- });
147
-
148
- test("CAPTURE: recovers the final text from --output-format json stdout", () => {
149
- const stdout = JSON.stringify({ type: "result", result: "OUTCOMES:\n- Email the deck", usage: {} });
150
- assert.equal(so.resultTextFromStdout(stdout), "OUTCOMES:\n- Email the deck");
151
-
152
- // A leading banner line before the JSON tail (the tolerant path).
153
- assert.equal(
154
- so.resultTextFromStdout(`warning: something\n${JSON.stringify({ result: "hi" })}`),
155
- "hi"
156
- );
157
- assert.equal(so.resultTextFromStdout(""), "", "empty stdout ⇒ empty");
158
- assert.equal(so.resultTextFromStdout("{"), "{", "unparseable ⇒ treated as plain text, never throws");
159
- });
160
-
161
- // ---------------------------------------------------------------------------
162
- // GATE — a landed outcome is org-visible, so the source must already be
163
- // ---------------------------------------------------------------------------
164
-
165
- test("GATE: a public Slack channel is the only source that qualifies", () => {
166
- assert.deepEqual(so.outcomeSourceShareable(PUBLIC_ITEM), {
167
- shareable: true,
168
- reason: "public-channel",
169
- });
170
- assert.equal(
171
- so.outcomeSourceShareable({ ...PUBLIC_ITEM, channel_id: "C0ABCDEF" }).shareable,
172
- true
173
- );
174
- });
175
-
176
- test("GATE: a private DM's commitments NEVER become org-visible artifacts", () => {
177
- // The leak this closes: board.create with no workstreamId is channel-less (hq's
178
- // board query applies no channel filter to it) and knowledge.append with no
179
- // group resolves to `org` — so a DM's commitment text AND its topic line would
180
- // be readable by every member of the org.
181
- const dms = [
182
- { service: "slack", channel: "dm/alice", channel_id: "D01ALICE", subject: "DM from Alice" },
183
- { service: "slack", channel: "general", channel_id: "C0GENERAL", is_dm: true },
184
- { service: "slack", channel: "dm/alice" },
185
- { service: "slack", channel: "general", channel_id: "D01ALICE" },
186
- ];
187
- for (const item of dms) {
188
- const gate = so.outcomeSourceShareable(item);
189
- assert.equal(gate.shareable, false, `DM must not land: ${JSON.stringify(item)}`);
190
- assert.equal(gate.reason, "dm");
191
- }
192
- });
193
-
194
- test("GATE: fail-CLOSED — anything not established as public lands nothing", () => {
195
- const cases = [
196
- [undefined, "no-source"],
197
- [null, "no-source"],
198
- ["not an object", "no-source"],
199
- [{}, "unknown-source"],
200
- [{ service: "slack" }, "unknown-channel"],
201
- [{ service: "slack", channel_id: "G01GROUP" }, "private-channel"],
202
- [{ service: "slack", channel_id: "C0GENERAL", is_private: true }, "private-channel"],
203
- [{ service: "gmail", channel: "email/alice@example.com" }, "private-correspondence"],
204
- [{ service: "gmail", subject: "Deck review" }, "non-channel-source"],
205
- [{ service: "telegram", channel: "telegram" }, "non-channel-source"],
206
- [{ channel: "general", channel_id: "C0GENERAL" }, "unknown-source"],
207
- // A type we cannot interpret is not a type we may publish from.
208
- [{ service: "slack", channel_id: "C0GENERAL", channel_type: "external_shared" }, "unknown-channel-type"],
209
- ];
210
- for (const [item, reason] of cases) {
211
- const gate = so.outcomeSourceShareable(item);
212
- assert.equal(gate.shareable, false, `must not land: ${JSON.stringify(item)}`);
213
- assert.equal(gate.reason, reason, `reason for ${JSON.stringify(item)}`);
214
- }
215
- });
216
-
217
- test("GATE: a `C…` id is NOT a public signal — the conversation TYPE is", () => {
218
- // THE DEFECT THIS CLOSES. The gate used to admit on `/^C/.test(channel_id)`,
219
- // but Slack is explicit that the conversation-ID prefix is not a reliable type
220
- // indicator: private channels are minted with `C…` ids too (only `D` for a DM
221
- // is dependable). Every item below carries a `C…` id and must still be refused
222
- // — otherwise a private channel's commitment text AND its topic line become a
223
- // channel-less board row plus an `org`-group knowledge episode, readable by
224
- // every member of the org.
225
- const privateWithPublicLookingId = [
226
- // What Socket Mode actually delivers for a private channel.
227
- [{ service: "slack", channel: "leadership", channel_id: "C0PRIVATE", channel_type: "group" }, "private-channel"],
228
- // A group DM, likewise not necessarily `G…`-prefixed.
229
- [{ service: "slack", channel: "C0MPIM01", channel_id: "C0MPIM01", channel_type: "mpim" }, "dm"],
230
- [{ service: "slack", channel: "C0IM0001", channel_id: "C0IM0001", channel_type: "im" }, "dm"],
231
- // An explicit privacy verdict from conversations.list/info.
232
- [{ service: "slack", channel: "leadership", channel_id: "C0PRIVATE", is_private: true }, "private-channel"],
233
- // NO signal at all: ingest didn't resolve the type, so nothing is established.
234
- [{ service: "slack", channel: "general", channel_id: "C0GENERAL" }, "privacy-unknown"],
235
- ];
236
- for (const [item, reason] of privateWithPublicLookingId) {
237
- const gate = so.outcomeSourceShareable(item);
238
- assert.equal(gate.shareable, false, `a C-prefixed id must not admit: ${JSON.stringify(item)}`);
239
- assert.equal(gate.reason, reason, `reason for ${JSON.stringify(item)}`);
240
- }
241
- });
242
-
243
- test("GATE: admits ONLY on a positive public signal (channel_type or is_private:false)", () => {
244
- for (const item of [
245
- { service: "slack", channel: "general", channel_id: "C0GENERAL", channel_type: "channel" },
246
- { service: "slack", channel: "general", channel_id: "C0GENERAL", is_private: false },
247
- // A public channel whose id happens not to start with C is still public.
248
- { service: "slack", channel: "general", channel_id: "X0WEIRD01", channel_type: "channel" },
249
- ]) {
250
- const gate = so.outcomeSourceShareable(item);
251
- assert.equal(gate.shareable, true, `must land: ${JSON.stringify(item)}`);
252
- assert.equal(gate.reason, "public-channel");
253
- }
254
- // …and an explicit DM/private verdict still overrides a public-looking type.
255
- assert.equal(
256
- so.outcomeSourceShareable({ service: "slack", channel_id: "C0GENERAL", channel_type: "channel", is_private: true }).shareable,
257
- false,
258
- "is_private:true wins outright"
259
- );
260
- assert.equal(
261
- so.outcomeSourceShareable({ service: "slack", channel_id: "C0GENERAL", channel_type: "channel", is_dm: true }).shareable,
262
- false,
263
- "is_dm:true wins outright"
264
- );
265
- });
266
-
267
- test("GATE: the SWEEP refuses a private channel that carries a `C…` id", async () => {
268
- let touched = false;
269
- const res = await so.sweepSessionOutcomes({
270
- sessionId: "s-gate-private-c",
271
- resultText: "OUTCOMES:\n- Prepare the confidential redundancy list — due: Friday",
272
- enabled: true,
273
- cfg: ENABLED_CFG,
274
- item: {
275
- service: "slack",
276
- channel: "leadership",
277
- channel_id: "C0LEADERSHIP",
278
- channel_type: "group",
279
- subject: "Headcount plan",
280
- },
281
- classResult: { summary: "headcount plan" },
282
- createItemImpl: async () => { touched = true; return { ok: true }; },
283
- rememberImpl: async () => { touched = true; return { ok: true }; },
284
- });
285
- assert.equal(res.created, 0);
286
- assert.equal(res.captured, 0, "a private channel's commitments are never even captured");
287
- assert.equal(res.reason, "source-private-channel");
288
- assert.equal(touched, false, "no board row, no episode");
289
- });
290
-
291
- test("GATE: the WRITER refuses a non-public source, not just the sweep", async () => {
292
- let touched = false;
293
- for (const item of [undefined, { service: "slack", channel_id: "D01ALICE" }]) {
294
- const res = await so.landOutcomes({
295
- sessionId: "s-gate",
296
- outcomes: [{ title: "Send the redlined MSA" }],
297
- cfg: ENABLED_CFG,
298
- item,
299
- createItemImpl: async () => { touched = true; return { ok: true }; },
300
- rememberImpl: async () => { touched = true; return { ok: true }; },
301
- });
302
- assert.equal(res.created, 0);
303
- assert.equal(res.skipped, true);
304
- assert.match(res.reason, /^source-/, "the refusal is reported honestly");
305
- }
306
- assert.equal(touched, false, "no protocol call for a private source");
307
- });
308
-
309
- test("GATE: the SWEEP refuses a DM before it even parses the text", async () => {
310
- let touched = false;
311
- const res = await so.sweepSessionOutcomes({
312
- sessionId: "s-gate-2",
313
- resultText: "OUTCOMES:\n- Send Alice the term sheet she asked for in confidence",
314
- enabled: true,
315
- cfg: ENABLED_CFG,
316
- item: { service: "slack", channel: "dm/alice", channel_id: "D01ALICE", is_dm: true },
317
- createItemImpl: async () => { touched = true; return { ok: true }; },
318
- rememberImpl: async () => { touched = true; return { ok: true }; },
319
- });
320
- assert.equal(res.created, 0);
321
- assert.equal(res.captured, 0, "a private turn's commitments are never even captured");
322
- assert.equal(res.reason, "source-dm");
323
- assert.equal(touched, false);
324
- assert.deepEqual(so.getSessionOutcomes("s-gate-2"), [], "and nothing is retained in memory");
325
- });
326
-
327
- // ---------------------------------------------------------------------------
328
- // LAND — via the existing org protocol only
329
- // ---------------------------------------------------------------------------
330
-
331
- test("LAND: writes one board.create per commitment plus ONE knowledge episode", async () => {
332
- const created = [];
333
- const remembered = [];
334
- const res = await so.landOutcomes({
335
- sessionId: "s-1",
336
- outcomes: [
337
- { title: "Send the redlined MSA", owner: "Robin", due: "Friday" },
338
- { title: "Book the pricing review" },
339
- ],
340
- cfg: ENABLED_CFG,
341
- item: PUBLIC_ITEM,
342
- classResult: { summary: "Acme renewal" },
343
- agentName: "Robin",
344
- createItemImpl: fakeCreate(created),
345
- rememberImpl: fakeRemember(remembered),
346
- });
347
-
348
- assert.equal(res.created, 2, "both commitments landed");
349
- assert.equal(created.length, 2);
350
- assert.equal(created[0].item.title, "Send the redlined MSA");
351
- assert.equal(created[0].item.priority, "P2");
352
- assert.ok(created[0].item.itemId, "carries a caller-supplied id for replay safety");
353
- assert.ok(created[0].item.detail.includes("Robin"), "owner recorded in the detail");
354
- assert.ok(created[0].item.detail.includes("Friday"), "timing recorded in the detail");
355
- assert.ok(created[0].item.detail.includes("s-1"), "provenance names the session");
356
- // board.create is `.strict()` server-side — sending an unknown field would 400.
357
- assert.deepEqual(
358
- Object.keys(created[0].item).sort(),
359
- ["detail", "itemId", "priority", "title"],
360
- "only board.create's own fields are sent"
361
- );
362
- assert.equal(created[0].opts.base, "https://org.example", "connection from the org config");
363
-
364
- assert.equal(remembered.length, 1, "exactly one episode for the whole set");
365
- assert.equal(remembered[0].opts.kind, "episode");
366
- assert.equal(remembered[0].opts.provenance.sessionId, "s-1");
367
- assert.ok(remembered[0].text.includes("Send the redlined MSA"));
368
- assert.equal(remembered[0].opts.group, undefined, "no group hint — the server derives the ACL");
369
- });
370
-
371
- test("LAND: the board id is deterministic, so a replay cannot double a row", () => {
372
- const a = so.outcomeItemId("s-1", "Send the redlined MSA");
373
- const b = so.outcomeItemId("s-1", " send the REDLINED msa ");
374
- const c = so.outcomeItemId("s-2", "Send the redlined MSA");
375
- assert.equal(a, b, "stable under whitespace/case");
376
- assert.notEqual(a, c, "scoped to the session");
377
- assert.match(a, /^so-[0-9a-f]{24}$/);
378
- });
379
-
380
- test("LAND: fail-open when the org integration is disabled", async () => {
381
- let touched = false;
382
- const res = await so.landOutcomes({
383
- sessionId: "s-1",
384
- outcomes: [{ title: "x" }],
385
- cfg: {},
386
- item: PUBLIC_ITEM,
387
- createItemImpl: async () => { touched = true; return { ok: true }; },
388
- rememberImpl: async () => { touched = true; return { ok: true }; },
389
- });
390
- assert.equal(res.skipped, true);
391
- assert.equal(res.created, 0);
392
- assert.equal(touched, false, "no protocol call when disabled");
393
- });
394
-
395
- test("LAND: never throws, and one bad row does not strand the rest", async () => {
396
- const created = [];
397
- const res = await so.landOutcomes({
398
- sessionId: "s-1",
399
- outcomes: [{ title: "first" }, { title: "second" }, { title: "third" }],
400
- cfg: ENABLED_CFG,
401
- item: PUBLIC_ITEM,
402
- createItemImpl: async (item) => {
403
- created.push(item.title);
404
- if (item.title === "second") throw new Error("network down");
405
- if (item.title === "third") return { error: { code: "INTERNAL" } };
406
- return { ok: true };
407
- },
408
- rememberImpl: async () => { throw new Error("also down"); },
409
- });
410
- assert.equal(res.created, 1, "only the row that actually landed is counted");
411
- assert.deepEqual(created, ["first", "second", "third"], "every row was attempted");
412
- assert.equal(res.remembered, false, "a failed episode is reported honestly");
413
- });
414
-
415
- // ---------------------------------------------------------------------------
416
- // SWEEP — driven, idempotent, off by default
417
- // ---------------------------------------------------------------------------
418
-
419
- test("SWEEP: off by default — the flag must be set explicitly", async () => {
420
- assert.equal(so.outcomesEnabled({}), false, "absent flag ⇒ off");
421
- assert.equal(so.outcomesEnabled({ DAEMON_SESSION_OUTCOMES: "0" }), false);
422
- assert.equal(so.outcomesEnabled({ DAEMON_SESSION_OUTCOMES: "true" }), false, "only '1' arms it");
423
- assert.equal(so.outcomesEnabled({ DAEMON_SESSION_OUTCOMES: "1" }), true);
424
-
425
- let touched = false;
426
- const res = await so.sweepSessionOutcomes({
427
- sessionId: "s-1",
428
- resultText: "OUTCOMES:\n- Do the thing",
429
- cfg: ENABLED_CFG,
430
- item: PUBLIC_ITEM,
431
- createItemImpl: async () => { touched = true; return { ok: true }; },
432
- rememberImpl: async () => { touched = true; return { ok: true }; },
433
- // `enabled` omitted ⇒ reads the (unset) env flag
434
- });
435
- assert.equal(res.skipped, true);
436
- assert.equal(res.reason, "disabled");
437
- assert.equal(touched, false, "nothing lands while the flag is off");
438
- });
439
-
440
- test("SWEEP: lands a turn's commitments end to end", async () => {
441
- const created = [];
442
- const remembered = [];
443
- const stdout = JSON.stringify({
444
- result: "Handled it.\n\nOUTCOMES:\n- Send the deck to Alice — due: Monday\n",
445
- });
446
- const res = await so.sweepSessionOutcomes({
447
- sessionId: "s-9",
448
- stdout,
449
- enabled: true,
450
- cfg: ENABLED_CFG,
451
- item: { ...PUBLIC_ITEM, subject: "Deck review" },
452
- agentName: "Robin",
453
- createItemImpl: fakeCreate(created),
454
- rememberImpl: fakeRemember(remembered),
455
- });
456
- assert.equal(res.captured, 1);
457
- assert.equal(res.created, 1);
458
- assert.equal(created[0].item.title, "Send the deck to Alice");
459
- assert.equal(remembered.length, 1);
460
- });
461
-
462
- test("SWEEP: a turn that committed to nothing is a clean no-op", async () => {
463
- let touched = false;
464
- const res = await so.sweepSessionOutcomes({
465
- sessionId: "s-10",
466
- resultText: "Answered inline. OUTCOMES: none",
467
- enabled: true,
468
- cfg: ENABLED_CFG,
469
- item: PUBLIC_ITEM,
470
- createItemImpl: async () => { touched = true; return { ok: true }; },
471
- rememberImpl: async () => { touched = true; return { ok: true }; },
472
- });
473
- assert.equal(res.captured, 0);
474
- assert.equal(res.reason, "nothing-captured");
475
- assert.equal(touched, false);
476
- });
477
-
478
- test("SWEEP: idempotent — a second close never re-lands", async () => {
479
- const created = [];
480
- const args = {
481
- sessionId: "s-11",
482
- resultText: "OUTCOMES:\n- Do the thing",
483
- enabled: true,
484
- cfg: ENABLED_CFG,
485
- item: PUBLIC_ITEM,
486
- createItemImpl: fakeCreate(created),
487
- rememberImpl: fakeRemember([]),
488
- };
489
- const first = await so.sweepSessionOutcomes(args);
490
- assert.equal(first.created, 1);
491
-
492
- // Re-arm the in-process state as a double-fire would, then sweep again.
493
- so.noteOutcomes("s-11", [{ title: "Do the thing" }]);
494
- so.markOutcomesLanded("s-11");
495
- const second = await so.sweepSessionOutcomes(args);
496
- assert.equal(second.reason, "already-landed");
497
- assert.equal(created.length, 1, "board.create called exactly once");
498
- });
499
-
500
- test("SWEEP: never throws, whatever the session emitted", async () => {
501
- for (const stdout of [undefined, null, "", "{{{", "not json at all"]) {
502
- const res = await so.sweepSessionOutcomes({
503
- sessionId: "s-12",
504
- stdout,
505
- enabled: true,
506
- cfg: ENABLED_CFG,
507
- item: PUBLIC_ITEM,
508
- createItemImpl: async () => { throw new Error("boom"); },
509
- rememberImpl: async () => { throw new Error("boom"); },
510
- });
511
- assert.ok(res && typeof res.created === "number", `benign result for ${JSON.stringify(stdout)}`);
512
- }
513
- const noSession = await so.sweepSessionOutcomes({
514
- enabled: true,
515
- resultText: "OUTCOMES:\n- x",
516
- item: PUBLIC_ITEM,
517
- });
518
- assert.equal(noSession.reason, "no-session");
519
- });
520
-
521
- test("STATE: capture is bounded and self-evicting so a long-lived daemon cannot leak", () => {
522
- const many = Array.from({ length: 30 }, (_, i) => ({ title: `step ${i}` }));
523
- const kept = so.noteOutcomes("s-13", many);
524
- assert.equal(kept.length, so.MAX_OUTCOMES);
525
- assert.equal(so.getSessionOutcomes("s-13").length, so.MAX_OUTCOMES);
526
-
527
- so.clearSessionOutcomes("s-13");
528
- assert.deepEqual(so.getSessionOutcomes("s-13"), []);
529
-
530
- for (let i = 0; i < 400; i += 1) so.noteOutcomes(`bulk-${i}`, [{ title: "x" }]);
531
- assert.deepEqual(so.getSessionOutcomes("bulk-0"), [], "oldest sessions are evicted");
532
- assert.equal(so.getSessionOutcomes("bulk-399").length, 1, "newest is retained");
533
- });
@@ -1,102 +0,0 @@
1
- /**
2
- * typing-registry.test.mjs — node:test coverage for typing-registry.mjs
3
- *
4
- * Verifies start/stop route to the registered adapter keyed by service, that
5
- * sources are derived from items (incl. YAML-round-tripped items lacking a
6
- * structured source), and that unknown/backlog channels no-op. Hermetic.
7
- */
8
-
9
- import { test } from "node:test";
10
- import assert from "node:assert/strict";
11
-
12
- import {
13
- registerTypingAdapter,
14
- clearTypingAdapters,
15
- startTyping,
16
- stopTyping,
17
- sourceFromItem,
18
- } from "./typing-registry.mjs";
19
- import { resetCohortTypingAdapter } from "../../lib/org/typing.mjs";
20
-
21
- function fakeAdapter() {
22
- return {
23
- started: [],
24
- stopped: [],
25
- startTypingHeartbeat(source) { this.started.push(source); return "key"; },
26
- stopTypingHeartbeat(source) { this.stopped.push(source); },
27
- };
28
- }
29
-
30
- test("startTyping/stopTyping route to the adapter for the item's service", () => {
31
- clearTypingAdapters();
32
- const tg = fakeAdapter();
33
- registerTypingAdapter("telegram", tg);
34
- const item = { service: "telegram", channel_id: "987", channel: "dm/casey", thread_id: "" };
35
- startTyping(item);
36
- stopTyping(item);
37
- assert.equal(tg.started.length, 1);
38
- assert.equal(tg.started[0].chatId, "987");
39
- assert.equal(tg.started[0].userScope, "dm");
40
- assert.equal(tg.stopped.length, 1);
41
- clearTypingAdapters();
42
- });
43
-
44
- test("unknown service no-ops (no adapter registered)", () => {
45
- clearTypingAdapters();
46
- assert.doesNotThrow(() => startTyping({ service: "gmail", channel_id: "x" }));
47
- assert.doesNotThrow(() => stopTyping({ service: "gmail", channel_id: "x" }));
48
- });
49
-
50
- test("item without a chatId no-ops", () => {
51
- clearTypingAdapters();
52
- const tg = fakeAdapter();
53
- registerTypingAdapter("telegram", tg);
54
- startTyping({ service: "telegram", channel: "dm/x" }); // no channel_id
55
- assert.equal(tg.started.length, 0);
56
- clearTypingAdapters();
57
- });
58
-
59
- test("sourceFromItem prefers a structured source, else reconstructs from item", () => {
60
- const explicit = sourceFromItem({ service: "telegram", source: { channel: "telegram", chatId: "1", userScope: "group" } });
61
- assert.equal(explicit.userScope, "group");
62
-
63
- const dm = sourceFromItem({ service: "slack", channel: "dm/casey", channel_id: "DABC", thread_id: "" });
64
- assert.equal(dm.userScope, "dm");
65
- assert.equal(dm.chatId, "DABC");
66
-
67
- const group = sourceFromItem({ service: "telegram", channel: "ops", channel_id: "-100", thread_id: "77" });
68
- assert.equal(group.userScope, "group");
69
- assert.equal(group.threadRef, "77");
70
- });
71
-
72
- test("cohort resolves the BUILT-IN adapter with nothing registered (the SDK parity gap)", () => {
73
- clearTypingAdapters();
74
- resetCohortTypingAdapter();
75
- // No registerTypingAdapter("cohort", …) anywhere — nothing constructs a
76
- // cohort channel adapter, which is why the indicator was missing entirely.
77
- const item = { service: "cohort", channel_id: "ch_1", channel: "#build", thread_id: "" };
78
- assert.doesNotThrow(() => startTyping(item));
79
- assert.doesNotThrow(() => stopTyping(item));
80
- clearTypingAdapters();
81
- resetCohortTypingAdapter();
82
- });
83
-
84
- test("an explicitly registered cohort adapter still wins over the built-in", () => {
85
- clearTypingAdapters();
86
- resetCohortTypingAdapter();
87
- const fake = fakeAdapter();
88
- registerTypingAdapter("cohort", fake);
89
- startTyping({ service: "cohort", channel_id: "ch_1", channel: "#build" });
90
- assert.equal(fake.started.length, 1);
91
- assert.equal(fake.started[0].chatId, "ch_1");
92
- clearTypingAdapters();
93
- resetCohortTypingAdapter();
94
- });
95
-
96
- test("registerTypingAdapter ignores adapters lacking startTypingHeartbeat", () => {
97
- clearTypingAdapters();
98
- registerTypingAdapter("bad", { foo: 1 });
99
- // No throw, and startTyping no-ops.
100
- assert.doesNotThrow(() => startTyping({ service: "bad", channel_id: "1" }));
101
- clearTypingAdapters();
102
- });