@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
@@ -0,0 +1,201 @@
1
+ /**
2
+ * lib/engine/mcp/http.mjs — the MCP streamable HTTP transport (client side).
3
+ *
4
+ * Per the MCP specification (2025-03-26 and 2025-06-18):
5
+ * · every client message is an HTTP POST of one JSON-RPC message to the
6
+ * endpoint, with `Accept: application/json, text/event-stream`;
7
+ * · a notification or response is answered 202 Accepted with no body;
8
+ * · a request is answered either with `application/json` (the response
9
+ * object) or with `text/event-stream`, whose `data:` frames carry JSON-RPC
10
+ * messages — possibly server requests first — ending with the response;
11
+ * · the server may assign a session in the initialize response's
12
+ * `Mcp-Session-Id` header, which the client then sends on every request;
13
+ * a 404 for a request carrying it means the session has expired;
14
+ * · after initialization the client sends `MCP-Protocol-Version`;
15
+ * · the client ends a session with an HTTP DELETE carrying the session id.
16
+ *
17
+ * The optional GET stream for server-initiated messages (`openEventStream`)
18
+ * is opened once after initialization, with `Accept: text/event-stream` and
19
+ * the session headers. A server that does not offer it answers 405 (or any
20
+ * non-stream response), which is not an error. Messages on it — typically
21
+ * `notifications/tools/list_changed` — are delivered like any other. If the
22
+ * server ends the stream it is not reopened; it is aborted when the session
23
+ * closes.
24
+ *
25
+ * @module lib/engine/mcp/http
26
+ */
27
+
28
+ import { readSse } from "../wire/sse.mjs";
29
+
30
+ /**
31
+ * @param {object} p
32
+ * @param {string} p.url
33
+ * @param {Record<string,string>} [p.headers]
34
+ * @param {typeof fetch} [p.fetchImpl]
35
+ */
36
+ export function createHttpTransport({ url, headers = {}, fetchImpl = fetch }) {
37
+ /** @type {string|null} */
38
+ let sessionId = null;
39
+ /** @type {string|null} */
40
+ let protocolVersion = null;
41
+ /** @type {(msg:unknown)=>void} */
42
+ let deliver = () => {};
43
+ let closed = false;
44
+ const inflight = new Set();
45
+ /** @type {AbortController|null} */
46
+ let streamController = null;
47
+
48
+ const requestHeaders = () => ({
49
+ ...headers,
50
+ "content-type": "application/json",
51
+ accept: "application/json, text/event-stream",
52
+ ...(sessionId ? { "mcp-session-id": sessionId } : {}),
53
+ ...(protocolVersion ? { "mcp-protocol-version": protocolVersion } : {}),
54
+ });
55
+
56
+ return {
57
+ /** @param {(msg:unknown)=>void} onMessage @param {(reason:string)=>void} _onClose */
58
+ start(onMessage, _onClose) {
59
+ deliver = onMessage;
60
+ return { ok: true };
61
+ },
62
+
63
+ /** @param {string} v */
64
+ setProtocolVersion(v) {
65
+ protocolVersion = v;
66
+ },
67
+
68
+ get sessionId() {
69
+ return sessionId;
70
+ },
71
+
72
+ /**
73
+ * @param {any} msg
74
+ * @param {{signal?:AbortSignal}} [o]
75
+ * @returns {Promise<{ok:true, completed?:boolean}|{ok:false, error:string}>}
76
+ */
77
+ async send(msg, o = {}) {
78
+ if (closed) return { ok: false, error: "the HTTP session is closed" };
79
+ const controller = new AbortController();
80
+ const onAbort = () => controller.abort();
81
+ o.signal?.addEventListener("abort", onAbort, { once: true });
82
+ inflight.add(controller);
83
+ try {
84
+ const hadSession = sessionId !== null;
85
+ let res;
86
+ try {
87
+ res = await fetchImpl(url, { method: "POST", headers: requestHeaders(), body: JSON.stringify(msg), signal: controller.signal });
88
+ } catch (e) {
89
+ return { ok: false, error: `HTTP request to ${url} failed: ${e instanceof Error ? e.message : e}` };
90
+ }
91
+ const assigned = res.headers.get("mcp-session-id");
92
+ if (assigned && msg.method === "initialize") sessionId = assigned;
93
+ if (res.status === 202) return { ok: true, completed: msg.id !== undefined };
94
+ if (res.status === 404 && hadSession) {
95
+ await res.body?.cancel().catch(() => {});
96
+ return { ok: false, error: "the MCP server's session has expired (HTTP 404)" };
97
+ }
98
+ if (!res.ok) {
99
+ const text = await res.text().catch(() => "");
100
+ return { ok: false, error: `HTTP ${res.status} from ${url}${text ? `: ${text.slice(0, 300)}` : ""}` };
101
+ }
102
+ const type = (res.headers.get("content-type") || "").toLowerCase();
103
+ if (type.includes("text/event-stream")) {
104
+ if (!res.body) return { ok: true, completed: true };
105
+ try {
106
+ for await (const frame of readSse(res.body)) {
107
+ if (!frame.data) continue;
108
+ try {
109
+ deliver(JSON.parse(frame.data));
110
+ } catch {
111
+ /* a non-JSON frame is not a message */
112
+ }
113
+ }
114
+ } catch (e) {
115
+ return { ok: false, error: `the response stream from ${url} broke: ${e instanceof Error ? e.message : e}` };
116
+ }
117
+ return { ok: true, completed: true };
118
+ }
119
+ const text = await res.text();
120
+ if (text.trim() === "") return { ok: true, completed: true };
121
+ try {
122
+ deliver(JSON.parse(text));
123
+ } catch {
124
+ return { ok: false, error: `the response from ${url} was not JSON` };
125
+ }
126
+ return { ok: true, completed: true };
127
+ } finally {
128
+ inflight.delete(controller);
129
+ o.signal?.removeEventListener("abort", onAbort);
130
+ }
131
+ },
132
+
133
+ /**
134
+ * Open the GET stream for server-initiated messages (once). Never rejects.
135
+ * @returns {Promise<{ok:true}|{ok:false, status:number|null}>}
136
+ */
137
+ async openEventStream() {
138
+ if (closed || streamController) return { ok: false, status: null };
139
+ const controller = new AbortController();
140
+ streamController = controller;
141
+ let res;
142
+ try {
143
+ res = await fetchImpl(url, {
144
+ method: "GET",
145
+ headers: {
146
+ ...headers,
147
+ accept: "text/event-stream",
148
+ ...(sessionId ? { "mcp-session-id": sessionId } : {}),
149
+ ...(protocolVersion ? { "mcp-protocol-version": protocolVersion } : {}),
150
+ },
151
+ signal: controller.signal,
152
+ });
153
+ } catch {
154
+ return { ok: false, status: null };
155
+ }
156
+ const type = (res.headers.get("content-type") || "").toLowerCase();
157
+ if (!res.ok || !type.includes("text/event-stream") || !res.body) {
158
+ await res.body?.cancel().catch(() => {});
159
+ return { ok: false, status: res.status };
160
+ }
161
+ const body = res.body;
162
+ void (async () => {
163
+ try {
164
+ for await (const frame of readSse(body)) {
165
+ if (!frame.data) continue;
166
+ try {
167
+ deliver(JSON.parse(frame.data));
168
+ } catch {
169
+ /* a non-JSON frame is not a message */
170
+ }
171
+ }
172
+ } catch {
173
+ /* the stream ended or was aborted; it is optional */
174
+ }
175
+ })();
176
+ return { ok: true };
177
+ },
178
+
179
+ stderrTail: () => "",
180
+
181
+ /** End the session (best effort) and abort anything still in flight. */
182
+ async close() {
183
+ if (closed) return;
184
+ closed = true;
185
+ streamController?.abort();
186
+ for (const c of inflight) c.abort();
187
+ if (!sessionId) return;
188
+ const controller = new AbortController();
189
+ const timer = setTimeout(() => controller.abort(), 2_000);
190
+ timer.unref?.();
191
+ try {
192
+ const res = await fetchImpl(url, { method: "DELETE", headers: { ...headers, "mcp-session-id": sessionId }, signal: controller.signal });
193
+ await res.body?.cancel().catch(() => {});
194
+ } catch {
195
+ /* the server may not allow DELETE (405) or may already be gone */
196
+ } finally {
197
+ clearTimeout(timer);
198
+ }
199
+ },
200
+ };
201
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * lib/engine/mcp/index.mjs — MCP servers → engine tools.
3
+ *
4
+ * `startMcpServers` connects every configured server in parallel and turns
5
+ * each tool it lists into an engine tool object named `mcp__<server>__<tool>`
6
+ * (see naming.mjs). A server that fails to start is reported in `statuses` and
7
+ * `errors`, and the run continues without it — the same posture as a tool
8
+ * that errors. A tool whose name is too long or collides is reported and not
9
+ * exposed.
10
+ *
11
+ * Each connection is supervised (supervisor.mjs): a stdio server that crashes
12
+ * is restarted once, on the next call. `refreshChanged` re-lists the tools of
13
+ * servers that sent `notifications/tools/list_changed` (or came back with a
14
+ * different list after a restart) and rebuilds the tool list; the run calls it
15
+ * at a turn boundary.
16
+ *
17
+ * MCP tools are marked `passthroughInput`: their input schemas are the
18
+ * server's, which the engine's small validator does not fully understand, so
19
+ * the loop checks only that the input is an object with the required keys and
20
+ * forwards it unchanged.
21
+ *
22
+ * @module lib/engine/mcp/index
23
+ */
24
+
25
+ import { connectMcpServer } from "./client.mjs";
26
+ import { mcpToolName } from "./naming.mjs";
27
+ import { superviseConnection } from "./supervisor.mjs";
28
+
29
+ /**
30
+ * @typedef {{name:string, source:string, transport:string, status:'connected'|'failed', tools:number, error?:string}} McpServerStatus
31
+ * @typedef {ReturnType<typeof superviseConnection>} SupervisedConnection
32
+ */
33
+
34
+ /**
35
+ * @param {import('./config.mjs').McpServerConfig[]} servers
36
+ * @param {Parameters<typeof connectMcpServer>[1] & {connect?:typeof connectMcpServer}} [deps]
37
+ */
38
+ export async function startMcpServers(servers, deps = {}) {
39
+ const connect = deps.connect ?? connectMcpServer;
40
+ const connected = await Promise.all(servers.map((s) => connect(s, deps).then((r) => ({ s, r }))));
41
+ /** @type {McpServerStatus[]} */
42
+ const statuses = [];
43
+ /** @type {string[]} */
44
+ const errors = [];
45
+ /** @type {Array<{s:import('./config.mjs').McpServerConfig, conn:SupervisedConnection, status:McpServerStatus}>} */
46
+ const live = [];
47
+
48
+ for (const { s, r } of connected) {
49
+ if (!r.ok) {
50
+ statuses.push({ name: s.name, source: s.source, transport: s.transport, status: "failed", tools: 0, error: r.error });
51
+ errors.push(r.error);
52
+ continue;
53
+ }
54
+ const status = { name: s.name, source: s.source, transport: s.transport, status: /** @type {'connected'} */ ("connected"), tools: 0 };
55
+ statuses.push(status);
56
+ live.push({ s, conn: superviseConnection(s, deps, r.connection, { connect }), status });
57
+ }
58
+
59
+ /** @param {string[]} sink */
60
+ const build = (sink) => {
61
+ /** @type {import('../tools/index.mjs').Tool[]} */
62
+ const tools = [];
63
+ const taken = new Set();
64
+ for (const { s, conn, status } of live) {
65
+ let count = 0;
66
+ for (const t of conn.tools) {
67
+ const named = mcpToolName(s.name, t.name);
68
+ if (!named.ok) {
69
+ sink.push(named.error);
70
+ continue;
71
+ }
72
+ if (taken.has(named.name)) {
73
+ sink.push(`MCP tool name "${named.name}" is produced by more than one tool; only the first is exposed`);
74
+ continue;
75
+ }
76
+ taken.add(named.name);
77
+ tools.push(mcpTool(named.name, s, t, conn));
78
+ count++;
79
+ }
80
+ status.tools = count;
81
+ }
82
+ return tools;
83
+ };
84
+ let tools = build(errors);
85
+
86
+ return {
87
+ get tools() {
88
+ return tools;
89
+ },
90
+ statuses,
91
+ errors,
92
+ /** @type {SupervisedConnection[]} */
93
+ connections: live.map((l) => l.conn),
94
+ /**
95
+ * Re-list servers whose tools changed; rebuild the tool list if any did.
96
+ * @returns {Promise<{changed:boolean, tools:import('../tools/index.mjs').Tool[], errors:string[]}>}
97
+ */
98
+ async refreshChanged() {
99
+ const problems = [];
100
+ let changed = false;
101
+ for (const { conn } of live) {
102
+ if (!conn.toolsChanged) continue;
103
+ const r = await conn.refreshTools();
104
+ if (r.ok) changed = true;
105
+ else problems.push(r.error);
106
+ }
107
+ if (changed) tools = build(problems);
108
+ return { changed, tools, errors: problems };
109
+ },
110
+ /** Shut every server down; never throws. */
111
+ async close() {
112
+ await Promise.all(live.map(({ conn }) => conn.close().catch(() => {})));
113
+ },
114
+ };
115
+ }
116
+
117
+ /**
118
+ * @param {string} name
119
+ * @param {import('./config.mjs').McpServerConfig} server
120
+ * @param {{name:string, description?:string, inputSchema?:any, annotations?:{readOnlyHint?:boolean}}} t
121
+ * @param {{callTool:(tool:string, args:object, o?:{signal?:AbortSignal})=>Promise<any>}} conn
122
+ * @returns {import('../tools/index.mjs').Tool & {passthroughInput:true, mcp:object}}
123
+ */
124
+ function mcpTool(name, server, t, conn) {
125
+ const schema = t.inputSchema && typeof t.inputSchema === "object" && !Array.isArray(t.inputSchema) ? { ...t.inputSchema } : {};
126
+ if (schema.type === undefined) schema.type = "object";
127
+ if (schema.properties === undefined) schema.properties = {};
128
+ return {
129
+ name,
130
+ description: typeof t.description === "string" && t.description.trim() !== "" ? t.description : `${t.name} (from the ${server.name} MCP server)`,
131
+ input_schema: schema,
132
+ // Read-only hints are the server's claim; they schedule parallel batches
133
+ // only. No permission is granted on their strength, and plan mode refuses
134
+ // every MCP tool whatever its hint.
135
+ readOnly: t.annotations?.readOnlyHint === true,
136
+ passthroughInput: true,
137
+ mcp: {
138
+ server: server.name,
139
+ tool: t.name,
140
+ transport: server.transport,
141
+ command: server.transport === "stdio" ? server.command : null,
142
+ args: server.transport === "stdio" ? server.args : [],
143
+ },
144
+ run: (input, ctx) => conn.callTool(t.name, input, { signal: ctx.signal }),
145
+ };
146
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * lib/engine/mcp/jsonrpc.mjs — a JSON-RPC 2.0 peer, transport-agnostic.
3
+ *
4
+ * The MCP client is one of these over a transport. The peer owns request ids,
5
+ * the pending-request table, per-request timeouts and cancellation, and the
6
+ * replies to the few requests a server may send the client (`ping`). It never
7
+ * rejects: every request resolves to a result union, so a dead server is a
8
+ * value the caller reports, not an exception that unwinds a run.
9
+ *
10
+ * @module lib/engine/mcp/jsonrpc
11
+ */
12
+
13
+ /**
14
+ * @typedef {{kind:'rpc', code:number, message:string, data?:unknown}
15
+ * | {kind:'timeout', message:string}
16
+ * | {kind:'closed', message:string}
17
+ * | {kind:'aborted', message:string}
18
+ * | {kind:'transport', message:string}} RpcError
19
+ * @typedef {{ok:true, result:any} | {ok:false, error:RpcError}} RpcOutcome
20
+ */
21
+
22
+ /**
23
+ * @param {object} p
24
+ * @param {(msg:object, o?:{signal?:AbortSignal}) => ({ok:true, completed?:boolean}|{ok:false, error:string}|Promise<{ok:true, completed?:boolean}|{ok:false, error:string}>)} p.send
25
+ * `completed:true` means the transport has delivered everything it will ever
26
+ * deliver for this message (an HTTP response body has ended).
27
+ * @param {(line:string) => void} [p.log]
28
+ * @param {(msg:{method:string, params?:any}) => void} [p.onNotification] server notifications
29
+ * (e.g. notifications/tools/list_changed); a throwing handler is contained
30
+ */
31
+ export function createRpcPeer({ send, log = () => {}, onNotification = () => {} }) {
32
+ let nextId = 1;
33
+ /** @type {Map<number, (o:RpcOutcome) => void>} */
34
+ const pending = new Map();
35
+ /** @type {string|null} */
36
+ let closedReason = null;
37
+
38
+ /**
39
+ * @param {string} method
40
+ * @param {object} [params]
41
+ * @param {{timeoutMs?:number, signal?:AbortSignal}} [o]
42
+ * @returns {Promise<RpcOutcome>}
43
+ */
44
+ function request(method, params, o = {}) {
45
+ if (closedReason !== null) return Promise.resolve({ ok: false, error: { kind: "closed", message: closedReason } });
46
+ const id = nextId++;
47
+ return new Promise((resolve) => {
48
+ /** @type {ReturnType<typeof setTimeout>|null} */
49
+ let timer = null;
50
+ const onAbort = () => {
51
+ finish({ ok: false, error: { kind: "aborted", message: "the request was cancelled" } });
52
+ notify("notifications/cancelled", { requestId: id, reason: "cancelled by the client" });
53
+ };
54
+ const finish = (/** @type RpcOutcome */ outcome) => {
55
+ if (!pending.has(id)) return;
56
+ pending.delete(id);
57
+ if (timer) clearTimeout(timer);
58
+ o.signal?.removeEventListener("abort", onAbort);
59
+ resolve(outcome);
60
+ };
61
+ pending.set(id, finish);
62
+ if (o.timeoutMs && o.timeoutMs > 0) {
63
+ timer = setTimeout(() => {
64
+ finish({ ok: false, error: { kind: "timeout", message: `no response to ${method} within ${o.timeoutMs} ms` } });
65
+ notify("notifications/cancelled", { requestId: id, reason: "timed out" });
66
+ }, o.timeoutMs);
67
+ // Not unref'd: a caller awaiting only this request must keep the
68
+ // process alive until it resolves. `finish` always clears it.
69
+ }
70
+ if (o.signal?.aborted) return onAbort();
71
+ o.signal?.addEventListener("abort", onAbort, { once: true });
72
+
73
+ const msg = { jsonrpc: "2.0", id, method, ...(params === undefined ? {} : { params }) };
74
+ Promise.resolve()
75
+ .then(() => send(msg, { signal: o.signal }))
76
+ .then(
77
+ (r) => {
78
+ if (!r.ok) finish({ ok: false, error: { kind: "transport", message: r.error } });
79
+ else if (r.completed) finish({ ok: false, error: { kind: "transport", message: `the server ended its response to ${method} without answering it` } });
80
+ },
81
+ (e) => finish({ ok: false, error: { kind: "transport", message: e instanceof Error ? e.message : String(e) } }),
82
+ );
83
+ });
84
+ }
85
+
86
+ /** @param {string} method @param {object} [params] */
87
+ function notify(method, params) {
88
+ if (closedReason !== null) return;
89
+ const msg = { jsonrpc: "2.0", method, ...(params === undefined ? {} : { params }) };
90
+ Promise.resolve()
91
+ .then(() => send(msg))
92
+ .catch(() => {
93
+ /* a notification has no one to report a failure to */
94
+ });
95
+ }
96
+
97
+ /** @param {unknown} msg a parsed frame from the transport (object or batch array) */
98
+ function receive(msg) {
99
+ if (Array.isArray(msg)) {
100
+ for (const m of msg) receive(m);
101
+ return;
102
+ }
103
+ if (!msg || typeof msg !== "object") return;
104
+ const m = /** @type {any} */ (msg);
105
+ if (m.method !== undefined) {
106
+ // A request or notification from the server.
107
+ if (m.id === undefined || m.id === null) {
108
+ try {
109
+ onNotification(m);
110
+ } catch {
111
+ /* a notification handler defect must not break the connection */
112
+ }
113
+ return;
114
+ }
115
+ const reply =
116
+ m.method === "ping"
117
+ ? { jsonrpc: "2.0", id: m.id, result: {} }
118
+ : { jsonrpc: "2.0", id: m.id, error: { code: -32601, message: `the client does not support ${m.method}` } };
119
+ Promise.resolve()
120
+ .then(() => send(reply))
121
+ .catch(() => {
122
+ /* the server will time out its own request */
123
+ });
124
+ return;
125
+ }
126
+ const finish = pending.get(Number(m.id));
127
+ if (!finish) {
128
+ log(`ignored a response for unknown request id ${JSON.stringify(m.id)}`);
129
+ return;
130
+ }
131
+ if (m.error) {
132
+ const e = m.error;
133
+ finish({ ok: false, error: { kind: "rpc", code: Number(e.code), message: String(e.message ?? "error"), data: e.data } });
134
+ } else {
135
+ finish({ ok: true, result: m.result });
136
+ }
137
+ }
138
+
139
+ /** Resolve every pending request as closed; later requests resolve closed at once. @param {string} reason */
140
+ function close(reason) {
141
+ if (closedReason !== null) return;
142
+ closedReason = reason;
143
+ for (const finish of [...pending.values()]) finish({ ok: false, error: { kind: "closed", message: reason } });
144
+ }
145
+
146
+ return { request, notify, receive, close, get closed() { return closedReason !== null; } };
147
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * lib/engine/mcp/naming.mjs — how an MCP tool is named to the model (pure).
3
+ *
4
+ * mcp__<server>__<tool>
5
+ *
6
+ * This name is a compatibility contract, not a detail: maestro's settings
7
+ * (`permissions.allow: ["mcp__cohort__*"]`), its PreToolUse matchers
8
+ * (`mcp__cohort__(messaging_send|…)`), its prompts and skills all spell tools
9
+ * this way. `naming.test.mjs` pins it.
10
+ *
11
+ * Rules:
12
+ * · each part (server key, tool name) has every character outside
13
+ * [A-Za-z0-9_-] replaced by "_" — the set both gateway wires accept in a
14
+ * function name;
15
+ * · the whole name may be at most 64 characters (the wire limit). A longer
16
+ * name is a RETURNED ERROR. It is never truncated or hashed: a silently
17
+ * shortened name would no longer match the permission rules and hook
18
+ * matchers written against the real one, which is how a gated tool slips
19
+ * through a gate.
20
+ *
21
+ * @module lib/engine/mcp/naming
22
+ */
23
+
24
+ export const MCP_PREFIX = "mcp__";
25
+ export const MAX_TOOL_NAME_LENGTH = 64;
26
+
27
+ /** @param {string} part */
28
+ export function sanitiseNamePart(part) {
29
+ return String(part).replace(/[^A-Za-z0-9_-]/g, "_");
30
+ }
31
+
32
+ /**
33
+ * @param {string} server the server's config key
34
+ * @param {string} tool the tool name the server reported
35
+ * @returns {{ok:true, name:string} | {ok:false, error:string}}
36
+ */
37
+ export function mcpToolName(server, tool) {
38
+ if (typeof server !== "string" || server === "") return { ok: false, error: "MCP server name is empty" };
39
+ if (typeof tool !== "string" || tool === "") return { ok: false, error: `MCP server "${server}" reported a tool with no name` };
40
+ const name = `${MCP_PREFIX}${sanitiseNamePart(server)}__${sanitiseNamePart(tool)}`;
41
+ if (name.length > MAX_TOOL_NAME_LENGTH) {
42
+ return {
43
+ ok: false,
44
+ error: `MCP tool "${tool}" on server "${server}" would be named "${name}" (${name.length} characters); the limit is ${MAX_TOOL_NAME_LENGTH}. Rename the server key in the MCP config to something shorter.`,
45
+ };
46
+ }
47
+ return { ok: true, name };
48
+ }
49
+
50
+ /**
51
+ * Is a server key usable at all? Checked when the config is read, so a key
52
+ * too long to leave room for any tool name fails before a process is spawned.
53
+ * @param {string} server
54
+ * @returns {{ok:true} | {ok:false, error:string}}
55
+ */
56
+ export function validateServerName(server) {
57
+ if (typeof server !== "string" || server.trim() === "") return { ok: false, error: "an MCP server has an empty name" };
58
+ const prefix = `${MCP_PREFIX}${sanitiseNamePart(server)}__`;
59
+ if (prefix.length + 1 > MAX_TOOL_NAME_LENGTH) {
60
+ return {
61
+ ok: false,
62
+ error: `MCP server name "${server}" is too long: its tools would be named "${prefix}…", which leaves no room under the ${MAX_TOOL_NAME_LENGTH}-character limit`,
63
+ };
64
+ }
65
+ return { ok: true };
66
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * lib/engine/mcp/resources.mjs — ListMcpResources and ReadMcpResource.
3
+ *
4
+ * MCP servers can publish resources (files, records, documents) beside their
5
+ * tools. When at least one connected server declares the `resources`
6
+ * capability, two engine tools expose them:
7
+ *
8
+ * ListMcpResources {server?} every resource, or one server's
9
+ * ReadMcpResource {server, uri} one resource's contents; text passes
10
+ * through, images are attached for the
11
+ * wire (within the caps in results.mjs)
12
+ *
13
+ * Both are read-only built-ins for permissions. A server whose tools are denied
14
+ * outright (`mcp__<server>` or `mcp__<server>__*` deny) has its resources
15
+ * withheld too: it is neither listed nor readable, and when no other server
16
+ * publishes resources the two tools are not offered. Deny `ReadMcpResource`
17
+ * itself to withhold every server's resources.
18
+ *
19
+ * Authorship: tool descriptions written for Cohort Engine by the Cohort team,
20
+ * 2026-09-14 (original text).
21
+ *
22
+ * @module lib/engine/mcp/resources
23
+ */
24
+
25
+ /**
26
+ * @typedef {{name:string, capabilities:Record<string,any>, listResources:(o?:{signal?:AbortSignal})=>Promise<any>, readResource:(uri:string, o?:{signal?:AbortSignal})=>Promise<any>}} ResourceServer
27
+ */
28
+
29
+ /** @param {ResourceServer} c */
30
+ const offersResources = (c) => Boolean(c.capabilities && typeof c.capabilities === "object" && c.capabilities.resources);
31
+
32
+ /**
33
+ * @param {() => ResourceServer[]} connections read at call time (servers can restart)
34
+ * @param {{isServerDenied?:(name:string)=>boolean}} [o] a server denied outright keeps its resources to itself
35
+ * @returns {import('../tools/index.mjs').Tool[]} empty when no permitted server offers resources
36
+ */
37
+ export function createMcpResourceTools(connections, { isServerDenied = () => false } = {}) {
38
+ const servers = () => connections().filter((c) => offersResources(c) && !isServerDenied(c.name));
39
+ if (servers().length === 0) return [];
40
+ const names = () => servers().map((c) => c.name).join(", ");
41
+
42
+ /** @type {import('../tools/index.mjs').Tool} */
43
+ const list = {
44
+ name: "ListMcpResources",
45
+ readOnly: true,
46
+ description: "List the resources (documents, files, records) that connected MCP servers publish. Pass `server` to list one server's resources. Read one with ReadMcpResource.",
47
+ input_schema: { type: "object", properties: { server: { type: "string", description: "Only this MCP server." } } },
48
+ async run(input, ctx) {
49
+ const targets = input.server ? servers().filter((c) => c.name === input.server) : servers();
50
+ if (input.server && targets.length === 0) return { ok: false, error: `No connected MCP server named "${input.server}" publishes resources. Servers with resources: ${names() || "none"}.` };
51
+ const sections = [];
52
+ for (const c of targets) {
53
+ const r = await c.listResources({ signal: ctx.signal });
54
+ if (!r.ok) {
55
+ sections.push(`${c.name}: could not list resources (${r.error})`);
56
+ continue;
57
+ }
58
+ if (r.resources.length === 0) {
59
+ sections.push(`${c.name}: no resources`);
60
+ continue;
61
+ }
62
+ const lines = r.resources.map((/** @type any */ x) => `- ${x.uri}${x.name ? ` — ${x.name}` : ""}${x.mimeType ? ` (${x.mimeType})` : ""}${x.description ? `: ${String(x.description).replace(/\s+/g, " ")}` : ""}`);
63
+ sections.push(`${c.name}:\n${lines.join("\n")}`);
64
+ }
65
+ return { ok: true, content: sections.join("\n\n") };
66
+ },
67
+ };
68
+
69
+ /** @type {import('../tools/index.mjs').Tool} */
70
+ const read = {
71
+ name: "ReadMcpResource",
72
+ readOnly: true,
73
+ description: "Read one resource from an MCP server by its URI (as listed by ListMcpResources).",
74
+ input_schema: {
75
+ type: "object",
76
+ properties: {
77
+ server: { type: "string", description: "The MCP server that publishes the resource." },
78
+ uri: { type: "string", description: "The resource URI." },
79
+ },
80
+ required: ["server", "uri"],
81
+ },
82
+ async run(input, ctx) {
83
+ const c = servers().find((s) => s.name === input.server);
84
+ if (!c) return { ok: false, error: `No connected MCP server named "${input.server}" publishes resources. Servers with resources: ${names() || "none"}.` };
85
+ return c.readResource(input.uri, { signal: ctx.signal });
86
+ },
87
+ };
88
+ return [list, read];
89
+ }