@bevel-software/platform-core-backend 0.15.1 → 0.20.0

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 (1385) hide show
  1. package/THIRD-PARTY-NOTICES.md +4 -4
  2. package/dist/core/catalog-cache-invalidation.d.ts +16 -0
  3. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  4. package/dist/core/catalog-cache-invalidation.js +6 -1
  5. package/dist/core/catalog-cache-invalidation.js.map +1 -1
  6. package/dist/core/catalog-revision.d.ts +39 -0
  7. package/dist/core/catalog-revision.d.ts.map +1 -0
  8. package/dist/core/catalog-revision.js +94 -0
  9. package/dist/core/catalog-revision.js.map +1 -0
  10. package/dist/core/create-core-server.d.ts +7 -0
  11. package/dist/core/create-core-server.d.ts.map +1 -1
  12. package/dist/core/create-core-server.js +221 -75
  13. package/dist/core/create-core-server.js.map +1 -1
  14. package/dist/core/create-core-services.d.ts +52 -2
  15. package/dist/core/create-core-services.d.ts.map +1 -1
  16. package/dist/core/create-core-services.js +222 -87
  17. package/dist/core/create-core-services.js.map +1 -1
  18. package/dist/core/lifecycle.d.ts +138 -0
  19. package/dist/core/lifecycle.d.ts.map +1 -0
  20. package/dist/core/lifecycle.js +218 -0
  21. package/dist/core/lifecycle.js.map +1 -0
  22. package/dist/core/public-config.d.ts +82 -0
  23. package/dist/core/public-config.d.ts.map +1 -0
  24. package/dist/core/public-config.js +78 -0
  25. package/dist/core/public-config.js.map +1 -0
  26. package/dist/core/readiness.d.ts +86 -0
  27. package/dist/core/readiness.d.ts.map +1 -0
  28. package/dist/core/readiness.js +56 -0
  29. package/dist/core/readiness.js.map +1 -0
  30. package/dist/core-config.d.ts +30 -0
  31. package/dist/core-config.d.ts.map +1 -1
  32. package/dist/core-config.js +64 -2
  33. package/dist/core-config.js.map +1 -1
  34. package/dist/index.d.ts +4 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +6 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/modules/access/access-control.interface.d.ts +215 -11
  39. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.d.ts +57 -11
  41. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  42. package/dist/modules/access/access-control.service.js +616 -155
  43. package/dist/modules/access/access-control.service.js.map +1 -1
  44. package/dist/modules/access/access-mutation.service.d.ts +71 -5
  45. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  46. package/dist/modules/access/access-mutation.service.js +161 -13
  47. package/dist/modules/access/access-mutation.service.js.map +1 -1
  48. package/dist/modules/access/access-view.d.ts +147 -0
  49. package/dist/modules/access/access-view.d.ts.map +1 -0
  50. package/dist/modules/access/access-view.js +211 -0
  51. package/dist/modules/access/access-view.js.map +1 -0
  52. package/dist/modules/access/access.routes.d.ts.map +1 -1
  53. package/dist/modules/access/access.routes.js +255 -79
  54. package/dist/modules/access/access.routes.js.map +1 -1
  55. package/dist/modules/access/admin-locked-commit.d.ts +2 -3
  56. package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
  57. package/dist/modules/access/admin-locked-commit.js +9 -8
  58. package/dist/modules/access/admin-locked-commit.js.map +1 -1
  59. package/dist/modules/access/admin-route-helpers.d.ts.map +1 -1
  60. package/dist/modules/access/admin-route-helpers.js +4 -4
  61. package/dist/modules/access/admin-route-helpers.js.map +1 -1
  62. package/dist/modules/access/change-read-gate.d.ts +53 -0
  63. package/dist/modules/access/change-read-gate.d.ts.map +1 -0
  64. package/dist/modules/access/change-read-gate.js +134 -0
  65. package/dist/modules/access/change-read-gate.js.map +1 -0
  66. package/dist/modules/access/creator-access.d.ts +33 -39
  67. package/dist/modules/access/creator-access.d.ts.map +1 -1
  68. package/dist/modules/access/creator-access.js +92 -119
  69. package/dist/modules/access/creator-access.js.map +1 -1
  70. package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
  71. package/dist/modules/access/directory-sync-bot.js +3 -1
  72. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  73. package/dist/modules/access/groups-admin.service.d.ts +3 -3
  74. package/dist/modules/access/groups-admin.service.js +2 -2
  75. package/dist/modules/access/reference-scan.d.ts +1 -1
  76. package/dist/modules/access/reference-scan.d.ts.map +1 -1
  77. package/dist/modules/access/reference-scan.js +5 -4
  78. package/dist/modules/access/reference-scan.js.map +1 -1
  79. package/dist/modules/access/roles-admin.service.d.ts +72 -13
  80. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  81. package/dist/modules/access/roles-admin.service.js +71 -13
  82. package/dist/modules/access/roles-admin.service.js.map +1 -1
  83. package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
  84. package/dist/modules/access/synced-groups-committer.js +8 -6
  85. package/dist/modules/access/synced-groups-committer.js.map +1 -1
  86. package/dist/modules/access/synced-groups-writer.d.ts.map +1 -1
  87. package/dist/modules/access/synced-groups-writer.js +3 -8
  88. package/dist/modules/access/synced-groups-writer.js.map +1 -1
  89. package/dist/modules/access/user-access-removal.service.d.ts +209 -0
  90. package/dist/modules/access/user-access-removal.service.d.ts.map +1 -0
  91. package/dist/modules/access/user-access-removal.service.js +662 -0
  92. package/dist/modules/access/user-access-removal.service.js.map +1 -0
  93. package/dist/modules/access-model/access-errors.d.ts +32 -1
  94. package/dist/modules/access-model/access-errors.d.ts.map +1 -1
  95. package/dist/modules/access-model/access-errors.js +38 -11
  96. package/dist/modules/access-model/access-errors.js.map +1 -1
  97. package/dist/modules/access-model/access-grammar.d.ts +49 -24
  98. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  99. package/dist/modules/access-model/access-grammar.js +68 -48
  100. package/dist/modules/access-model/access-grammar.js.map +1 -1
  101. package/dist/modules/access-model/access-splice.d.ts.map +1 -1
  102. package/dist/modules/access-model/access-splice.js +18 -18
  103. package/dist/modules/access-model/access-splice.js.map +1 -1
  104. package/dist/modules/access-model/access-template.d.ts +22 -0
  105. package/dist/modules/access-model/access-template.d.ts.map +1 -0
  106. package/dist/modules/access-model/access-template.js +42 -0
  107. package/dist/modules/access-model/access-template.js.map +1 -0
  108. package/dist/modules/access-model/change-gate.d.ts +65 -0
  109. package/dist/modules/access-model/change-gate.d.ts.map +1 -0
  110. package/dist/modules/access-model/change-gate.js +25 -0
  111. package/dist/modules/access-model/change-gate.js.map +1 -0
  112. package/dist/modules/access-model/creator.d.ts +13 -16
  113. package/dist/modules/access-model/creator.d.ts.map +1 -1
  114. package/dist/modules/access-model/creator.js.map +1 -1
  115. package/dist/modules/access-model/frontmatter-lines.d.ts +27 -0
  116. package/dist/modules/access-model/frontmatter-lines.d.ts.map +1 -0
  117. package/dist/modules/access-model/frontmatter-lines.js +74 -0
  118. package/dist/modules/access-model/frontmatter-lines.js.map +1 -0
  119. package/dist/modules/access-model/plugin-principals.d.ts +5 -3
  120. package/dist/modules/access-model/plugin-principals.d.ts.map +1 -1
  121. package/dist/modules/access-model/plugin-principals.js +11 -17
  122. package/dist/modules/access-model/plugin-principals.js.map +1 -1
  123. package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
  124. package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
  125. package/dist/modules/access-model/roles-yaml-guard.js +153 -3
  126. package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
  127. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  128. package/dist/modules/admin/admin-access.service.js +5 -2
  129. package/dist/modules/admin/admin-access.service.js.map +1 -1
  130. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
  131. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
  132. package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
  133. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
  134. package/dist/modules/agent-instructions/compose.d.ts +71 -0
  135. package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
  136. package/dist/modules/agent-instructions/compose.js +234 -0
  137. package/dist/modules/agent-instructions/compose.js.map +1 -0
  138. package/dist/modules/agent-instructions/index.d.ts +4 -0
  139. package/dist/modules/agent-instructions/index.d.ts.map +1 -0
  140. package/dist/modules/agent-instructions/index.js +4 -0
  141. package/dist/modules/agent-instructions/index.js.map +1 -0
  142. package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
  143. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
  144. package/dist/modules/agent-instructions/read-preamble.js +81 -0
  145. package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
  146. package/dist/modules/audit/agent-audit.service.d.ts +54 -0
  147. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -0
  148. package/dist/modules/audit/agent-audit.service.js +290 -0
  149. package/dist/modules/audit/agent-audit.service.js.map +1 -0
  150. package/dist/modules/audit/agent-rest-audit.middleware.d.ts +40 -0
  151. package/dist/modules/audit/agent-rest-audit.middleware.d.ts.map +1 -0
  152. package/dist/modules/audit/agent-rest-audit.middleware.js +75 -0
  153. package/dist/modules/audit/agent-rest-audit.middleware.js.map +1 -0
  154. package/dist/modules/audit/audit.contract.d.ts +145 -0
  155. package/dist/modules/audit/audit.contract.d.ts.map +1 -0
  156. package/dist/modules/audit/audit.contract.js +35 -0
  157. package/dist/modules/audit/audit.contract.js.map +1 -0
  158. package/dist/modules/audit/audit.routes.d.ts +21 -0
  159. package/dist/modules/audit/audit.routes.d.ts.map +1 -0
  160. package/dist/modules/audit/audit.routes.js +157 -0
  161. package/dist/modules/audit/audit.routes.js.map +1 -0
  162. package/dist/modules/audit/event-classifier.d.ts +52 -0
  163. package/dist/modules/audit/event-classifier.d.ts.map +1 -0
  164. package/dist/modules/audit/event-classifier.js +123 -0
  165. package/dist/modules/audit/event-classifier.js.map +1 -0
  166. package/dist/modules/audit/index.d.ts +7 -0
  167. package/dist/modules/audit/index.d.ts.map +1 -0
  168. package/dist/modules/audit/index.js +7 -0
  169. package/dist/modules/audit/index.js.map +1 -0
  170. package/dist/modules/audit/request-audit.d.ts +57 -0
  171. package/dist/modules/audit/request-audit.d.ts.map +1 -0
  172. package/dist/modules/audit/request-audit.js +117 -0
  173. package/dist/modules/audit/request-audit.js.map +1 -0
  174. package/dist/modules/auth/account-erasure.service.d.ts +30 -4
  175. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
  176. package/dist/modules/auth/account-erasure.service.js +96 -10
  177. package/dist/modules/auth/account-erasure.service.js.map +1 -1
  178. package/dist/modules/auth/account.routes.d.ts +6 -3
  179. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  180. package/dist/modules/auth/account.routes.js +109 -10
  181. package/dist/modules/auth/account.routes.js.map +1 -1
  182. package/dist/modules/auth/auth.routes.d.ts +10 -3
  183. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  184. package/dist/modules/auth/auth.routes.js +9 -4
  185. package/dist/modules/auth/auth.routes.js.map +1 -1
  186. package/dist/modules/auth/auth.service.d.ts +89 -5
  187. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  188. package/dist/modules/auth/auth.service.js +160 -25
  189. package/dist/modules/auth/auth.service.js.map +1 -1
  190. package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
  191. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  192. package/dist/modules/auth/oidc-auth-provider.js +99 -21
  193. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  194. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  195. package/dist/modules/code-mode/code-mode.tool.js +14 -2
  196. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  197. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  198. package/dist/modules/connection-probe/connection-probe.service.js +56 -7
  199. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  200. package/dist/modules/database/advisory-lock.d.ts +103 -0
  201. package/dist/modules/database/advisory-lock.d.ts.map +1 -0
  202. package/dist/modules/database/advisory-lock.js +320 -0
  203. package/dist/modules/database/advisory-lock.js.map +1 -0
  204. package/dist/modules/database/connection.d.ts.map +1 -1
  205. package/dist/modules/database/connection.js +9 -1
  206. package/dist/modules/database/connection.js.map +1 -1
  207. package/dist/modules/database/core-schema.d.ts +859 -99
  208. package/dist/modules/database/core-schema.d.ts.map +1 -1
  209. package/dist/modules/database/core-schema.js +218 -0
  210. package/dist/modules/database/core-schema.js.map +1 -1
  211. package/dist/modules/database/migrate.d.ts.map +1 -1
  212. package/dist/modules/database/migrate.js +26 -6
  213. package/dist/modules/database/migrate.js.map +1 -1
  214. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  215. package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
  216. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  217. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  218. package/dist/modules/diff/diff.routes.js +2 -4
  219. package/dist/modules/diff/diff.routes.js.map +1 -1
  220. package/dist/modules/diff/diff.service.d.ts +4 -1
  221. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  222. package/dist/modules/diff/diff.service.js +84 -81
  223. package/dist/modules/diff/diff.service.js.map +1 -1
  224. package/dist/modules/{workspace → kb-fs}/bevel-ignore.d.ts +14 -9
  225. package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
  226. package/dist/modules/{workspace → kb-fs}/bevel-ignore.js +24 -14
  227. package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
  228. package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
  229. package/dist/modules/kb-fs/file-change-notifier.js +3 -1
  230. package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
  231. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +45 -0
  232. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
  233. package/dist/modules/kb-fs/git-guarded-filesystem.js +117 -0
  234. package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
  235. package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
  236. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  237. package/dist/modules/kb-fs/locking-filesystem.js +353 -76
  238. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  239. package/dist/modules/kb-fs/node-fs.d.ts +23 -0
  240. package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
  241. package/dist/modules/kb-fs/node-fs.js +158 -0
  242. package/dist/modules/kb-fs/node-fs.js.map +1 -0
  243. package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
  244. package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
  245. package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
  246. package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
  247. package/dist/modules/kb-fs/repo-path.d.ts +100 -3
  248. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  249. package/dist/modules/kb-fs/repo-path.js +195 -3
  250. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  251. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  252. package/dist/modules/kb-sync/kb-sync.routes.js +4 -2
  253. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  254. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  255. package/dist/modules/kb-sync/kb-sync.service.js +7 -5
  256. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  257. package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -1
  258. package/dist/modules/marketplace/git-http.routes.js +6 -4
  259. package/dist/modules/marketplace/git-http.routes.js.map +1 -1
  260. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +12 -2
  261. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -1
  262. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +32 -3
  263. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -1
  264. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +27 -0
  265. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -1
  266. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +45 -0
  267. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -1
  268. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -1
  269. package/dist/modules/marketplace/github-facade/github-facade.routes.js +30 -6
  270. package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -1
  271. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +21 -9
  272. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -1
  273. package/dist/modules/marketplace/github-facade/github-facade.service.js +60 -19
  274. package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -1
  275. package/dist/modules/marketplace/github-facade/index.d.ts +1 -1
  276. package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -1
  277. package/dist/modules/marketplace/github-facade/index.js +1 -1
  278. package/dist/modules/marketplace/github-facade/index.js.map +1 -1
  279. package/dist/modules/marketplace/marketplace-repo.service.d.ts +14 -1
  280. package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -1
  281. package/dist/modules/marketplace/marketplace-repo.service.js +26 -8
  282. package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -1
  283. package/dist/modules/mcp/downstream-pool.d.ts +107 -0
  284. package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
  285. package/dist/modules/mcp/downstream-pool.js +221 -0
  286. package/dist/modules/mcp/downstream-pool.js.map +1 -0
  287. package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
  288. package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
  289. package/dist/modules/mcp/downstream-token-refresh.js +166 -0
  290. package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
  291. package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
  292. package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
  293. package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
  294. package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
  295. package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
  296. package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
  297. package/dist/modules/mcp/first-call-probe.cli.js +62 -0
  298. package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
  299. package/dist/modules/mcp/first-call-probe.d.ts +165 -0
  300. package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
  301. package/dist/modules/mcp/first-call-probe.js +298 -0
  302. package/dist/modules/mcp/first-call-probe.js.map +1 -0
  303. package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
  304. package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
  305. package/dist/modules/mcp/manual-failure-memo.js +79 -23
  306. package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
  307. package/dist/modules/mcp/mcp-auth.middleware.d.ts +10 -5
  308. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  309. package/dist/modules/mcp/mcp-auth.middleware.js +56 -16
  310. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  311. package/dist/modules/mcp/mcp.routes.d.ts +4 -4
  312. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  313. package/dist/modules/mcp/mcp.routes.js +91 -180
  314. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  315. package/dist/modules/mcp/mcp.service.d.ts +226 -61
  316. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  317. package/dist/modules/mcp/mcp.service.js +758 -161
  318. package/dist/modules/mcp/mcp.service.js.map +1 -1
  319. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +59 -0
  320. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  321. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +184 -10
  322. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  323. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  324. package/dist/modules/mcp/oauth/oauth-consent.routes.js +3 -1
  325. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  326. package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
  327. package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
  328. package/dist/modules/mcp/surface-log-throttle.js +94 -0
  329. package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
  330. package/dist/modules/plugins/compile/compile-marketplace.d.ts +13 -6
  331. package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -1
  332. package/dist/modules/plugins/compile/compile-marketplace.js +119 -36
  333. package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -1
  334. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +8 -1
  335. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  336. package/dist/modules/plugins/compile/marketplace-compiler.service.js +9 -6
  337. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  338. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  339. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +32 -9
  340. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  341. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +26 -6
  342. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -1
  343. package/dist/modules/plugins/discovery/bundle-dialect/registry.js +71 -14
  344. package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -1
  345. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +4 -2
  346. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  347. package/dist/modules/plugins/discovery/kb-plugin-source.js +9 -6
  348. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  349. package/dist/modules/plugins/discovery/native.source.d.ts +2 -1
  350. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  351. package/dist/modules/plugins/discovery/native.source.js +22 -4
  352. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  353. package/dist/modules/plugins/discovery/plugin-source.d.ts +17 -3
  354. package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -1
  355. package/dist/modules/plugins/index.d.ts +6 -3
  356. package/dist/modules/plugins/index.d.ts.map +1 -1
  357. package/dist/modules/plugins/index.js +6 -3
  358. package/dist/modules/plugins/index.js.map +1 -1
  359. package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
  360. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
  361. package/dist/modules/plugins/join-request-jobs.service.js +602 -0
  362. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
  363. package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
  364. package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
  365. package/dist/modules/plugins/join-request-records.store.js +179 -0
  366. package/dist/modules/plugins/join-request-records.store.js.map +1 -0
  367. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  368. package/dist/modules/plugins/join-requests.service.js +4 -2
  369. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  370. package/dist/modules/plugins/plugin-links.d.ts +4 -1
  371. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  372. package/dist/modules/plugins/plugin-links.js +6 -3
  373. package/dist/modules/plugins/plugin-links.js.map +1 -1
  374. package/dist/modules/plugins/plugin-links.service.d.ts +2 -5
  375. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  376. package/dist/modules/plugins/plugin-links.service.js +4 -13
  377. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  378. package/dist/modules/plugins/plugin-provision.service.d.ts +91 -19
  379. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  380. package/dist/modules/plugins/plugin-provision.service.js +183 -38
  381. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  382. package/dist/modules/plugins/plugin-rename.service.d.ts +3 -1
  383. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  384. package/dist/modules/plugins/plugin-rename.service.js +20 -11
  385. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  386. package/dist/modules/plugins/plugins.contract.d.ts +52 -4
  387. package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
  388. package/dist/modules/plugins/plugins.routes.d.ts +38 -5
  389. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  390. package/dist/modules/plugins/plugins.routes.js +190 -116
  391. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  392. package/dist/modules/plugins/plugins.service.d.ts +38 -6
  393. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  394. package/dist/modules/plugins/plugins.service.js +86 -17
  395. package/dist/modules/plugins/plugins.service.js.map +1 -1
  396. package/dist/modules/plugins/plugins.tools.d.ts +25 -0
  397. package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
  398. package/dist/modules/plugins/plugins.tools.js +82 -0
  399. package/dist/modules/plugins/plugins.tools.js.map +1 -0
  400. package/dist/modules/plugins/teams.routes.d.ts +42 -0
  401. package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
  402. package/dist/modules/plugins/teams.routes.js +79 -0
  403. package/dist/modules/plugins/teams.routes.js.map +1 -0
  404. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
  405. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  406. package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
  407. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  408. package/dist/modules/secrets-vault/index.d.ts +1 -1
  409. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  410. package/dist/modules/secrets-vault/index.js.map +1 -1
  411. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
  412. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
  413. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
  414. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  415. package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
  416. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  417. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
  418. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  419. package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
  420. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  421. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  422. package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
  423. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  424. package/dist/modules/settings/connection-check.d.ts +118 -0
  425. package/dist/modules/settings/connection-check.d.ts.map +1 -0
  426. package/dist/modules/settings/connection-check.js +287 -0
  427. package/dist/modules/settings/connection-check.js.map +1 -0
  428. package/dist/modules/settings/deployment-settings.service.d.ts +162 -8
  429. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  430. package/dist/modules/settings/deployment-settings.service.js +375 -52
  431. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  432. package/dist/modules/settings/git-root-folders.d.ts +54 -0
  433. package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
  434. package/dist/modules/settings/git-root-folders.js +130 -0
  435. package/dist/modules/settings/git-root-folders.js.map +1 -0
  436. package/dist/modules/settings/oidc-check.d.ts +85 -0
  437. package/dist/modules/settings/oidc-check.d.ts.map +1 -0
  438. package/dist/modules/settings/oidc-check.js +172 -0
  439. package/dist/modules/settings/oidc-check.js.map +1 -0
  440. package/dist/modules/settings/setup.routes.d.ts +31 -1
  441. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  442. package/dist/modules/settings/setup.routes.js +400 -108
  443. package/dist/modules/settings/setup.routes.js.map +1 -1
  444. package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
  445. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
  446. package/dist/modules/skills/allowed-tools-check.js +260 -0
  447. package/dist/modules/skills/allowed-tools-check.js.map +1 -0
  448. package/dist/modules/skills/index.d.ts +3 -1
  449. package/dist/modules/skills/index.d.ts.map +1 -1
  450. package/dist/modules/skills/index.js +1 -0
  451. package/dist/modules/skills/index.js.map +1 -1
  452. package/dist/modules/skills/pending-skills.service.d.ts +6 -11
  453. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  454. package/dist/modules/skills/pending-skills.service.js +18 -70
  455. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  456. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  457. package/dist/modules/skills/skill-access-requests.routes.js +9 -6
  458. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  459. package/dist/modules/skills/skills.contract.d.ts +44 -10
  460. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  461. package/dist/modules/skills/skills.routes.d.ts +8 -1
  462. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  463. package/dist/modules/skills/skills.routes.js +16 -2
  464. package/dist/modules/skills/skills.routes.js.map +1 -1
  465. package/dist/modules/skills/skills.service.d.ts +31 -7
  466. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  467. package/dist/modules/skills/skills.service.js +191 -81
  468. package/dist/modules/skills/skills.service.js.map +1 -1
  469. package/dist/modules/skills/skills.tools.d.ts +4 -1
  470. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  471. package/dist/modules/skills/skills.tools.js +41 -6
  472. package/dist/modules/skills/skills.tools.js.map +1 -1
  473. package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
  474. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
  475. package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
  476. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
  477. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
  478. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
  479. package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
  480. package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
  481. package/dist/modules/tool-auth/external-api-key.interface.d.ts +72 -8
  482. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  483. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  484. package/dist/modules/tool-auth/external-api-key.service.d.ts +17 -6
  485. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  486. package/dist/modules/tool-auth/external-api-key.service.js +83 -27
  487. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  488. package/dist/modules/tool-auth/internal-token.service.d.ts +15 -2
  489. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  490. package/dist/modules/tool-auth/internal-token.service.js +6 -2
  491. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  492. package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
  493. package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
  494. package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
  495. package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
  496. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +15 -1
  497. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  498. package/dist/modules/tool-auth/tool-auth.middleware.js +17 -6
  499. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  500. package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
  501. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  502. package/dist/modules/tool-helpers/tool-context.js +28 -6
  503. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  504. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  505. package/dist/modules/tool-helpers/tool-handler.js +22 -5
  506. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  507. package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
  508. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  509. package/dist/modules/tool-helpers/tool.contract.js +7 -1
  510. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  511. package/dist/modules/tool-manuals/index.d.ts +2 -1
  512. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  513. package/dist/modules/tool-manuals/index.js +1 -0
  514. package/dist/modules/tool-manuals/index.js.map +1 -1
  515. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  516. package/dist/modules/tool-manuals/mcp-json-discovery.js +8 -6
  517. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  518. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
  519. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  520. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
  521. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  522. package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
  523. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
  524. package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
  525. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
  526. package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
  527. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
  528. package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
  529. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
  530. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
  531. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  532. package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
  533. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
  534. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
  535. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  536. package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
  537. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  538. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +119 -5
  539. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  540. package/dist/modules/tool-manuals/tool-manuals.service.js +404 -74
  541. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  542. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  543. package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
  544. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  545. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  546. package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
  547. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  548. package/dist/modules/workflow/event-bus.d.ts +10 -4
  549. package/dist/modules/workflow/event-bus.d.ts.map +1 -1
  550. package/dist/modules/workflow/event-bus.js +14 -11
  551. package/dist/modules/workflow/event-bus.js.map +1 -1
  552. package/dist/modules/workflow/events.routes.d.ts +17 -2
  553. package/dist/modules/workflow/events.routes.d.ts.map +1 -1
  554. package/dist/modules/workflow/events.routes.js +31 -12
  555. package/dist/modules/workflow/events.routes.js.map +1 -1
  556. package/dist/modules/workflow/file-lock.service.d.ts +22 -4
  557. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  558. package/dist/modules/workflow/file-lock.service.js +27 -4
  559. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  560. package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
  561. package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
  562. package/dist/modules/workflow/git/change-request-link.js +34 -0
  563. package/dist/modules/workflow/git/change-request-link.js.map +1 -0
  564. package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
  565. package/dist/modules/workflow/git/git-version.js +3 -1
  566. package/dist/modules/workflow/git/git-version.js.map +1 -1
  567. package/dist/modules/workflow/git/git.service.d.ts +263 -4
  568. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  569. package/dist/modules/workflow/git/git.service.js +528 -118
  570. package/dist/modules/workflow/git/git.service.js.map +1 -1
  571. package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
  572. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
  573. package/dist/modules/workflow/git/node-git-runner.js +273 -0
  574. package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
  575. package/dist/modules/workflow/git/pull-request.service.d.ts +51 -2
  576. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  577. package/dist/modules/workflow/git/pull-request.service.js +157 -25
  578. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  579. package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
  580. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  581. package/dist/modules/workflow/pending-commits.service.js +24 -4
  582. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  583. package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
  584. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  585. package/dist/modules/workflow/pending-commits.worker.js +107 -58
  586. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  587. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
  588. package/dist/modules/workflow/recovery-bot.js +3 -1
  589. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  590. package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
  591. package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
  592. package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
  593. package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
  594. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
  595. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
  596. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -0
  597. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  598. package/dist/modules/workflow/review-workflow/review-workflow.service.js +269 -90
  599. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  600. package/dist/modules/workflow/sanitize-error.d.ts +5 -2
  601. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  602. package/dist/modules/workflow/sanitize-error.js +8 -4
  603. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  604. package/dist/modules/workflow/workflow.routes.d.ts +2 -0
  605. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  606. package/dist/modules/workflow/workflow.routes.js +259 -29
  607. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  608. package/dist/modules/workflow/workflow.service.d.ts +166 -14
  609. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  610. package/dist/modules/workflow/workflow.service.js +906 -106
  611. package/dist/modules/workflow/workflow.service.js.map +1 -1
  612. package/dist/modules/workspace/empty-dirs.d.ts +15 -0
  613. package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
  614. package/dist/modules/workspace/empty-dirs.js +90 -0
  615. package/dist/modules/workspace/empty-dirs.js.map +1 -0
  616. package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
  617. package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
  618. package/dist/modules/workspace/file-readers/content-mode.js +55 -0
  619. package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
  620. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
  621. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  622. package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
  623. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  624. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
  625. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
  626. package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
  627. package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
  628. package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
  629. package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
  630. package/dist/modules/workspace/file-readers/document-reader.js +5 -0
  631. package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
  632. package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
  633. package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
  634. package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
  635. package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
  636. package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
  637. package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
  638. package/dist/modules/workspace/file-readers/file-reader.js +11 -6
  639. package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
  640. package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
  641. package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
  642. package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
  643. package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
  644. package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
  645. package/dist/modules/workspace/file-readers/image-reader.js +9 -5
  646. package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
  647. package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
  648. package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
  649. package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
  650. package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
  651. package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
  652. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  653. package/dist/modules/workspace/file-readers/text-reader.js +57 -3
  654. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  655. package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
  656. package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
  657. package/dist/modules/workspace/git-internals.middleware.js +93 -0
  658. package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
  659. package/dist/modules/workspace/not-found.d.ts +80 -0
  660. package/dist/modules/workspace/not-found.d.ts.map +1 -0
  661. package/dist/modules/workspace/not-found.js +112 -0
  662. package/dist/modules/workspace/not-found.js.map +1 -0
  663. package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
  664. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  665. package/dist/modules/workspace/session-ontology.gate.js +1 -1
  666. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  667. package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
  668. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
  669. package/dist/modules/workspace/startup/beside-checkout.js +110 -0
  670. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
  671. package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
  672. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  673. package/dist/modules/workspace/startup/kb-git.js +34 -35
  674. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  675. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
  676. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  677. package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
  678. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  679. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
  680. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  681. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
  682. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  683. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
  684. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
  685. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
  686. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
  687. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
  688. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
  689. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
  690. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
  691. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
  692. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
  693. package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
  694. package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
  695. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  696. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  697. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +45 -81
  698. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  699. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
  700. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
  701. package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
  702. package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
  703. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
  704. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  705. package/dist/modules/workspace/startup/steps/seed-tree.js +154 -78
  706. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  707. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
  708. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  709. package/dist/modules/workspace/startup/steps/template-files.step.js +446 -138
  710. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  711. package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
  712. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
  713. package/dist/modules/workspace/startup/steps/template-source.js +160 -0
  714. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
  715. package/dist/modules/workspace/workspace.routes.d.ts +14 -2
  716. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  717. package/dist/modules/workspace/workspace.routes.js +649 -238
  718. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  719. package/dist/modules/workspace/workspace.service.d.ts +298 -12
  720. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  721. package/dist/modules/workspace/workspace.service.js +933 -269
  722. package/dist/modules/workspace/workspace.service.js.map +1 -1
  723. package/dist/modules/workspace/workspace.tools.d.ts +42 -1
  724. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  725. package/dist/modules/workspace/workspace.tools.js +1626 -146
  726. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  727. package/dist/modules/workspace/write-denial.d.ts +76 -0
  728. package/dist/modules/workspace/write-denial.d.ts.map +1 -0
  729. package/dist/modules/workspace/write-denial.js +138 -0
  730. package/dist/modules/workspace/write-denial.js.map +1 -0
  731. package/dist/shared/canonical-file-identity.d.ts +64 -0
  732. package/dist/shared/canonical-file-identity.d.ts.map +1 -0
  733. package/dist/shared/canonical-file-identity.js +92 -0
  734. package/dist/shared/canonical-file-identity.js.map +1 -0
  735. package/dist/shared/domain-errors.d.ts +108 -0
  736. package/dist/shared/domain-errors.d.ts.map +1 -1
  737. package/dist/shared/domain-errors.js +137 -0
  738. package/dist/shared/domain-errors.js.map +1 -1
  739. package/dist/shared/email-identity.d.ts +32 -0
  740. package/dist/shared/email-identity.d.ts.map +1 -0
  741. package/dist/shared/email-identity.js +37 -0
  742. package/dist/shared/email-identity.js.map +1 -0
  743. package/dist/shared/fs.contract.d.ts +206 -0
  744. package/dist/shared/fs.contract.d.ts.map +1 -0
  745. package/dist/shared/fs.contract.js +79 -0
  746. package/dist/shared/fs.contract.js.map +1 -0
  747. package/dist/shared/git-failure.d.ts +80 -0
  748. package/dist/shared/git-failure.d.ts.map +1 -0
  749. package/dist/shared/git-failure.js +139 -0
  750. package/dist/shared/git-failure.js.map +1 -0
  751. package/dist/shared/git-internals.d.ts +23 -0
  752. package/dist/shared/git-internals.d.ts.map +1 -0
  753. package/dist/shared/git-internals.js +181 -0
  754. package/dist/shared/git-internals.js.map +1 -0
  755. package/dist/shared/git.contract.d.ts +157 -0
  756. package/dist/shared/git.contract.d.ts.map +1 -0
  757. package/dist/shared/git.contract.js +103 -0
  758. package/dist/shared/git.contract.js.map +1 -0
  759. package/dist/shared/http-errors.d.ts +33 -0
  760. package/dist/shared/http-errors.d.ts.map +1 -0
  761. package/dist/shared/http-errors.js +35 -0
  762. package/dist/shared/http-errors.js.map +1 -0
  763. package/dist/shared/logger.contract.d.ts +63 -0
  764. package/dist/shared/logger.contract.d.ts.map +1 -0
  765. package/dist/shared/logger.contract.js +126 -0
  766. package/dist/shared/logger.contract.js.map +1 -0
  767. package/dist/shared/logging.d.ts +31 -0
  768. package/dist/shared/logging.d.ts.map +1 -0
  769. package/dist/shared/logging.js +92 -0
  770. package/dist/shared/logging.js.map +1 -0
  771. package/dist/shared/path-containment.d.ts +21 -0
  772. package/dist/shared/path-containment.d.ts.map +1 -0
  773. package/dist/shared/path-containment.js +34 -0
  774. package/dist/shared/path-containment.js.map +1 -0
  775. package/dist/shared/pending-proposals.d.ts +71 -0
  776. package/dist/shared/pending-proposals.d.ts.map +1 -0
  777. package/dist/shared/pending-proposals.js +98 -0
  778. package/dist/shared/pending-proposals.js.map +1 -0
  779. package/dist/shared/printable.d.ts +22 -0
  780. package/dist/shared/printable.d.ts.map +1 -0
  781. package/dist/shared/printable.js +65 -0
  782. package/dist/shared/printable.js.map +1 -0
  783. package/dist/shared/redact-secret.d.ts +30 -0
  784. package/dist/shared/redact-secret.d.ts.map +1 -0
  785. package/dist/shared/redact-secret.js +114 -0
  786. package/dist/shared/redact-secret.js.map +1 -0
  787. package/dist/shared/rename-no-replace.d.ts +119 -0
  788. package/dist/shared/rename-no-replace.d.ts.map +1 -0
  789. package/dist/shared/rename-no-replace.js +323 -0
  790. package/dist/shared/rename-no-replace.js.map +1 -0
  791. package/dist/shared/ssrf.d.ts.map +1 -1
  792. package/dist/shared/ssrf.js +49 -18
  793. package/dist/shared/ssrf.js.map +1 -1
  794. package/dist/version.d.ts +10 -6
  795. package/dist/version.d.ts.map +1 -1
  796. package/dist/version.js +10 -6
  797. package/dist/version.js.map +1 -1
  798. package/kb-template/.bevelignore +4 -3
  799. package/kb-template/AGENTS.md +281 -36
  800. package/kb-template/KnowledgeBase/How to get started.md +1 -1
  801. package/kb-template/access.md +5 -3
  802. package/kb-template/mcp-description.md +27 -0
  803. package/migrations/0006_api_tokens_revoked_by.sql +1 -0
  804. package/migrations/0007_github_facade_registered.sql +1 -0
  805. package/migrations/0008_change_request_apply_failure.sql +4 -0
  806. package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
  807. package/migrations/0010_plugin_join_requests.sql +15 -0
  808. package/migrations/0011_plugin_join_request_claim.sql +11 -0
  809. package/migrations/0012_agent_audit.sql +41 -0
  810. package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -0
  811. package/migrations/meta/0006_snapshot.json +1723 -0
  812. package/migrations/meta/0007_snapshot.json +1729 -0
  813. package/migrations/meta/0008_snapshot.json +1753 -0
  814. package/migrations/meta/0009_snapshot.json +1759 -0
  815. package/migrations/meta/0010_snapshot.json +1872 -0
  816. package/migrations/meta/0011_snapshot.json +1884 -0
  817. package/migrations/meta/0012_snapshot.json +2247 -0
  818. package/migrations/meta/0013_snapshot.json +2259 -0
  819. package/migrations/meta/_journal.json +56 -0
  820. package/package.json +7 -6
  821. package/src/__tests__/core-config.domain.test.ts +67 -2
  822. package/src/__tests__/kb-layout-config.test.ts +186 -1
  823. package/src/__tests__/open-change-gate.ts +15 -0
  824. package/src/core/__tests__/catalog-cache-invalidation.test.ts +83 -3
  825. package/src/core/__tests__/catalog-revision.test.ts +190 -0
  826. package/src/core/__tests__/lifecycle.test.ts +369 -0
  827. package/src/core/__tests__/public-config.test.ts +30 -0
  828. package/src/core/__tests__/readiness.test.ts +109 -0
  829. package/src/core/catalog-cache-invalidation.ts +20 -1
  830. package/src/core/catalog-revision.ts +110 -0
  831. package/src/core/create-core-server.ts +286 -79
  832. package/src/core/create-core-services.ts +315 -81
  833. package/src/core/lifecycle.ts +312 -0
  834. package/src/core/public-config.ts +86 -0
  835. package/src/core/readiness.ts +123 -0
  836. package/src/core-config.ts +70 -4
  837. package/src/index.ts +15 -0
  838. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
  839. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
  840. package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
  841. package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
  842. package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
  843. package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
  844. package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
  845. package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +2 -0
  846. package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
  847. package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
  848. package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
  849. package/src/modules/access/__tests__/access-control.service.test.ts +123 -78
  850. package/src/modules/access/__tests__/access-groups.test.ts +47 -14
  851. package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
  852. package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
  853. package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
  854. package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
  855. package/src/modules/access/__tests__/access-plugin-principals.test.ts +18 -1
  856. package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
  857. package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
  858. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
  859. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
  860. package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
  861. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
  862. package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
  863. package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
  864. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
  865. package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
  866. package/src/modules/access/__tests__/creator-access.test.ts +81 -83
  867. package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
  868. package/src/modules/access/__tests__/groups-admin.service.test.ts +6 -5
  869. package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
  870. package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
  871. package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
  872. package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
  873. package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
  874. package/src/modules/access/__tests__/users-db-double.ts +63 -0
  875. package/src/modules/access/access-control.interface.ts +251 -11
  876. package/src/modules/access/access-control.service.ts +719 -164
  877. package/src/modules/access/access-mutation.service.ts +182 -13
  878. package/src/modules/access/access-view.ts +316 -0
  879. package/src/modules/access/access.routes.ts +293 -95
  880. package/src/modules/access/admin-locked-commit.ts +12 -14
  881. package/src/modules/access/admin-route-helpers.ts +4 -4
  882. package/src/modules/access/change-read-gate.ts +148 -0
  883. package/src/modules/access/creator-access.ts +89 -124
  884. package/src/modules/access/directory-sync-bot.ts +4 -1
  885. package/src/modules/access/groups-admin.service.ts +3 -3
  886. package/src/modules/access/reference-scan.ts +5 -5
  887. package/src/modules/access/roles-admin.service.ts +94 -15
  888. package/src/modules/access/synced-groups-committer.ts +10 -15
  889. package/src/modules/access/synced-groups-writer.ts +3 -12
  890. package/src/modules/access/user-access-removal.service.ts +711 -0
  891. package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
  892. package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
  893. package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
  894. package/src/modules/access-model/access-errors.ts +56 -15
  895. package/src/modules/access-model/access-grammar.ts +85 -48
  896. package/src/modules/access-model/access-splice.ts +18 -18
  897. package/src/modules/access-model/access-template.ts +44 -0
  898. package/src/modules/access-model/change-gate.ts +80 -0
  899. package/src/modules/access-model/creator.ts +18 -21
  900. package/src/modules/access-model/frontmatter-lines.ts +98 -0
  901. package/src/modules/access-model/plugin-principals.ts +12 -19
  902. package/src/modules/access-model/roles-yaml-guard.ts +182 -4
  903. package/src/modules/admin/admin-access.service.ts +6 -2
  904. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
  905. package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
  906. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
  907. package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
  908. package/src/modules/agent-instructions/compose.ts +271 -0
  909. package/src/modules/agent-instructions/index.ts +14 -0
  910. package/src/modules/agent-instructions/read-preamble.ts +88 -0
  911. package/src/modules/audit/__tests__/agent-audit.service.test.ts +370 -0
  912. package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -0
  913. package/src/modules/audit/__tests__/audit.routes.test.ts +201 -0
  914. package/src/modules/audit/__tests__/event-classifier.test.ts +121 -0
  915. package/src/modules/audit/agent-audit.service.ts +316 -0
  916. package/src/modules/audit/agent-rest-audit.middleware.ts +83 -0
  917. package/src/modules/audit/audit.contract.ts +164 -0
  918. package/src/modules/audit/audit.routes.ts +168 -0
  919. package/src/modules/audit/event-classifier.ts +155 -0
  920. package/src/modules/audit/index.ts +14 -0
  921. package/src/modules/audit/request-audit.ts +141 -0
  922. package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
  923. package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
  924. package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
  925. package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
  926. package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
  927. package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
  928. package/src/modules/auth/account-erasure.service.ts +117 -10
  929. package/src/modules/auth/account.routes.ts +115 -14
  930. package/src/modules/auth/auth.routes.ts +20 -7
  931. package/src/modules/auth/auth.service.ts +187 -28
  932. package/src/modules/auth/oidc-auth-provider.ts +120 -22
  933. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
  934. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  935. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
  936. package/src/modules/connection-probe/connection-probe.service.ts +59 -7
  937. package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
  938. package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
  939. package/src/modules/database/advisory-lock.ts +346 -0
  940. package/src/modules/database/connection.ts +9 -1
  941. package/src/modules/database/core-schema.ts +227 -0
  942. package/src/modules/database/migrate.ts +27 -6
  943. package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
  944. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
  945. package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
  946. package/src/modules/diff/diff.routes.ts +2 -4
  947. package/src/modules/diff/diff.service.ts +80 -79
  948. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
  949. package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
  950. package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
  951. package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
  952. package/src/modules/kb-fs/file-change-notifier.ts +4 -1
  953. package/src/modules/kb-fs/git-guarded-filesystem.ts +136 -0
  954. package/src/modules/kb-fs/locking-filesystem.ts +399 -109
  955. package/src/modules/kb-fs/node-fs.ts +165 -0
  956. package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
  957. package/src/modules/kb-fs/repo-path.ts +213 -3
  958. package/src/modules/kb-sync/kb-sync.routes.ts +5 -2
  959. package/src/modules/kb-sync/kb-sync.service.ts +8 -5
  960. package/src/modules/marketplace/__tests__/github-facade.test.ts +134 -2
  961. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +2 -1
  962. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
  963. package/src/modules/marketplace/git-http.routes.ts +7 -4
  964. package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +35 -3
  965. package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +55 -0
  966. package/src/modules/marketplace/github-facade/github-facade.routes.ts +32 -6
  967. package/src/modules/marketplace/github-facade/github-facade.service.ts +80 -19
  968. package/src/modules/marketplace/github-facade/index.ts +1 -0
  969. package/src/modules/marketplace/marketplace-repo.service.ts +23 -8
  970. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +183 -13
  971. package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
  972. package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
  973. package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
  974. package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
  975. package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
  976. package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
  977. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +68 -7
  978. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
  979. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
  980. package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +24 -4
  981. package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
  982. package/src/modules/mcp/__tests__/mcp.service.test.ts +391 -6
  983. package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
  984. package/src/modules/mcp/downstream-pool.ts +256 -0
  985. package/src/modules/mcp/downstream-token-refresh.ts +170 -0
  986. package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
  987. package/src/modules/mcp/first-call-probe.cli.ts +60 -0
  988. package/src/modules/mcp/first-call-probe.ts +418 -0
  989. package/src/modules/mcp/manual-failure-memo.ts +81 -23
  990. package/src/modules/mcp/mcp-auth.middleware.ts +56 -16
  991. package/src/modules/mcp/mcp.routes.ts +386 -482
  992. package/src/modules/mcp/mcp.service.ts +905 -177
  993. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +189 -8
  994. package/src/modules/mcp/oauth/oauth-consent.routes.ts +4 -4
  995. package/src/modules/mcp/surface-log-throttle.ts +120 -0
  996. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +10 -7
  997. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
  998. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +139 -10
  999. package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
  1000. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
  1001. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
  1002. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +148 -7
  1003. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +10 -13
  1004. package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
  1005. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +129 -19
  1006. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +91 -29
  1007. package/src/modules/plugins/__tests__/plugins.routes.test.ts +370 -19
  1008. package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
  1009. package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
  1010. package/src/modules/plugins/compile/compile-marketplace.ts +143 -46
  1011. package/src/modules/plugins/compile/marketplace-compiler.service.ts +8 -7
  1012. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +56 -20
  1013. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +30 -9
  1014. package/src/modules/plugins/discovery/bundle-dialect/registry.ts +93 -18
  1015. package/src/modules/plugins/discovery/kb-plugin-source.ts +10 -7
  1016. package/src/modules/plugins/discovery/native.source.ts +21 -3
  1017. package/src/modules/plugins/discovery/plugin-source.ts +17 -3
  1018. package/src/modules/plugins/index.ts +16 -3
  1019. package/src/modules/plugins/join-request-jobs.service.ts +675 -0
  1020. package/src/modules/plugins/join-request-records.store.ts +353 -0
  1021. package/src/modules/plugins/join-requests.service.ts +7 -4
  1022. package/src/modules/plugins/plugin-links.service.ts +4 -17
  1023. package/src/modules/plugins/plugin-links.ts +6 -3
  1024. package/src/modules/plugins/plugin-provision.service.ts +209 -37
  1025. package/src/modules/plugins/plugin-rename.service.ts +21 -9
  1026. package/src/modules/plugins/plugins.contract.ts +52 -4
  1027. package/src/modules/plugins/plugins.routes.ts +204 -124
  1028. package/src/modules/plugins/plugins.service.ts +92 -18
  1029. package/src/modules/plugins/plugins.tools.ts +88 -0
  1030. package/src/modules/plugins/teams.routes.ts +129 -0
  1031. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
  1032. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
  1033. package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
  1034. package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
  1035. package/src/modules/secrets-vault/index.ts +1 -0
  1036. package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
  1037. package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
  1038. package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
  1039. package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
  1040. package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
  1041. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +223 -6
  1042. package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
  1043. package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
  1044. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
  1045. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
  1046. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
  1047. package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
  1048. package/src/modules/settings/connection-check.ts +351 -0
  1049. package/src/modules/settings/deployment-settings.service.ts +444 -59
  1050. package/src/modules/settings/git-root-folders.ts +171 -0
  1051. package/src/modules/settings/oidc-check.ts +229 -0
  1052. package/src/modules/settings/setup.routes.ts +456 -110
  1053. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
  1054. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
  1055. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
  1056. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
  1057. package/src/modules/skills/__tests__/skills.service.test.ts +223 -2
  1058. package/src/modules/skills/allowed-tools-check.ts +352 -0
  1059. package/src/modules/skills/index.ts +4 -0
  1060. package/src/modules/skills/pending-skills.service.ts +21 -82
  1061. package/src/modules/skills/skill-access-requests.routes.ts +10 -6
  1062. package/src/modules/skills/skills.contract.ts +43 -11
  1063. package/src/modules/skills/skills.routes.ts +16 -1
  1064. package/src/modules/skills/skills.service.ts +214 -84
  1065. package/src/modules/skills/skills.tools.ts +43 -5
  1066. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
  1067. package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
  1068. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +153 -8
  1069. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +6 -0
  1070. package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
  1071. package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
  1072. package/src/modules/tool-auth/external-api-key.interface.ts +78 -8
  1073. package/src/modules/tool-auth/external-api-key.service.ts +98 -33
  1074. package/src/modules/tool-auth/internal-token.service.ts +16 -2
  1075. package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
  1076. package/src/modules/tool-auth/tool-auth.middleware.ts +33 -8
  1077. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
  1078. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
  1079. package/src/modules/tool-helpers/tool-context.ts +46 -7
  1080. package/src/modules/tool-helpers/tool-handler.ts +23 -5
  1081. package/src/modules/tool-helpers/tool.contract.ts +5 -0
  1082. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  1083. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
  1084. package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
  1085. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
  1086. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
  1087. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +9 -4
  1088. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
  1089. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
  1090. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
  1091. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
  1092. package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
  1093. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
  1094. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
  1095. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
  1096. package/src/modules/tool-manuals/index.ts +3 -0
  1097. package/src/modules/tool-manuals/mcp-json-discovery.ts +13 -10
  1098. package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
  1099. package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
  1100. package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
  1101. package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
  1102. package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
  1103. package/src/modules/tool-manuals/tool-manuals.service.ts +432 -87
  1104. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
  1105. package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
  1106. package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
  1107. package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
  1108. package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
  1109. package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
  1110. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
  1111. package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
  1112. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -0
  1113. package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
  1114. package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
  1115. package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
  1116. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
  1117. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
  1118. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -0
  1119. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
  1120. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +294 -25
  1121. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
  1122. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
  1123. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
  1124. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +14 -0
  1125. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -0
  1126. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
  1127. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
  1128. package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
  1129. package/src/modules/workflow/event-bus.ts +26 -28
  1130. package/src/modules/workflow/events.routes.ts +56 -21
  1131. package/src/modules/workflow/file-lock.service.ts +27 -4
  1132. package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
  1133. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
  1134. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
  1135. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
  1136. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
  1137. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
  1138. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
  1139. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
  1140. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
  1141. package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
  1142. package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
  1143. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
  1144. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
  1145. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
  1146. package/src/modules/workflow/git/change-request-link.ts +35 -0
  1147. package/src/modules/workflow/git/git-version.ts +5 -2
  1148. package/src/modules/workflow/git/git.service.ts +684 -144
  1149. package/src/modules/workflow/git/node-git-runner.ts +315 -0
  1150. package/src/modules/workflow/git/pull-request.service.ts +179 -27
  1151. package/src/modules/workflow/pending-commits.service.ts +26 -7
  1152. package/src/modules/workflow/pending-commits.worker.ts +114 -69
  1153. package/src/modules/workflow/recovery-bot.ts +4 -1
  1154. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
  1155. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
  1156. package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
  1157. package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
  1158. package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
  1159. package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
  1160. package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
  1161. package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
  1162. package/src/modules/workflow/sanitize-error.ts +9 -4
  1163. package/src/modules/workflow/workflow.routes.ts +273 -29
  1164. package/src/modules/workflow/workflow.service.ts +1087 -171
  1165. package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
  1166. package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
  1167. package/src/modules/workspace/__tests__/git-internals.security.test.ts +883 -0
  1168. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
  1169. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
  1170. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
  1171. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
  1172. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
  1173. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
  1174. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
  1175. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
  1176. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
  1177. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +25 -6
  1178. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
  1179. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  1180. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
  1181. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +56 -1
  1182. package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
  1183. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
  1184. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
  1185. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
  1186. package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
  1187. package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
  1188. package/src/modules/workspace/empty-dirs.ts +79 -0
  1189. package/src/modules/workspace/file-readers/README.md +32 -0
  1190. package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
  1191. package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
  1192. package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
  1193. package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
  1194. package/src/modules/workspace/file-readers/content-mode.ts +87 -0
  1195. package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
  1196. package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
  1197. package/src/modules/workspace/file-readers/document-reader.ts +6 -0
  1198. package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
  1199. package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
  1200. package/src/modules/workspace/file-readers/file-reader.ts +27 -6
  1201. package/src/modules/workspace/file-readers/image-reader.ts +10 -5
  1202. package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
  1203. package/src/modules/workspace/file-readers/text-reader.ts +61 -4
  1204. package/src/modules/workspace/git-internals.middleware.ts +86 -0
  1205. package/src/modules/workspace/not-found.ts +119 -0
  1206. package/src/modules/workspace/session-ontology.gate.ts +1 -1
  1207. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
  1208. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
  1209. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
  1210. package/src/modules/workspace/startup/beside-checkout.ts +112 -0
  1211. package/src/modules/workspace/startup/kb-git.ts +43 -36
  1212. package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
  1213. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1215 -73
  1214. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
  1215. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
  1216. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
  1217. package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
  1218. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +46 -85
  1219. package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
  1220. package/src/modules/workspace/startup/steps/seed-tree.ts +159 -78
  1221. package/src/modules/workspace/startup/steps/template-files.step.ts +492 -141
  1222. package/src/modules/workspace/startup/steps/template-source.ts +168 -0
  1223. package/src/modules/workspace/workspace.routes.ts +687 -257
  1224. package/src/modules/workspace/workspace.service.ts +1000 -303
  1225. package/src/modules/workspace/workspace.tools.ts +1823 -180
  1226. package/src/modules/workspace/write-denial.ts +189 -0
  1227. package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
  1228. package/src/shared/__tests__/email-identity.test.ts +35 -0
  1229. package/src/shared/__tests__/filename.test.ts +0 -0
  1230. package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
  1231. package/src/shared/__tests__/git-failure.test.ts +198 -0
  1232. package/src/shared/__tests__/git-internals.test.ts +156 -0
  1233. package/src/shared/__tests__/http-errors.test.ts +42 -0
  1234. package/src/shared/__tests__/logging.test.ts +185 -0
  1235. package/src/shared/__tests__/path-containment.test.ts +60 -0
  1236. package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
  1237. package/src/shared/__tests__/printable.test.ts +64 -0
  1238. package/src/shared/__tests__/redact-secret.test.ts +122 -0
  1239. package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
  1240. package/src/shared/__tests__/ssrf.test.ts +20 -0
  1241. package/src/shared/canonical-file-identity.ts +92 -0
  1242. package/src/shared/domain-errors.ts +149 -0
  1243. package/src/shared/email-identity.ts +38 -0
  1244. package/src/shared/fs.contract.ts +225 -0
  1245. package/src/shared/git-failure.ts +196 -0
  1246. package/src/shared/git-internals.ts +179 -0
  1247. package/src/shared/git.contract.ts +170 -0
  1248. package/src/shared/http-errors.ts +36 -0
  1249. package/src/shared/logger.contract.ts +141 -0
  1250. package/src/shared/logging.ts +96 -0
  1251. package/src/shared/path-containment.ts +34 -0
  1252. package/src/shared/pending-proposals.ts +161 -0
  1253. package/src/shared/printable.ts +68 -0
  1254. package/src/shared/redact-secret.ts +118 -0
  1255. package/src/shared/rename-no-replace.ts +386 -0
  1256. package/src/shared/ssrf.ts +44 -16
  1257. package/src/version.ts +10 -6
  1258. package/dist/modules/access/access-errors.d.ts +0 -34
  1259. package/dist/modules/access/access-errors.d.ts.map +0 -1
  1260. package/dist/modules/access/access-errors.js +0 -40
  1261. package/dist/modules/access/access-errors.js.map +0 -1
  1262. package/dist/modules/access/access-splice.d.ts +0 -140
  1263. package/dist/modules/access/access-splice.d.ts.map +0 -1
  1264. package/dist/modules/access/access-splice.js +0 -389
  1265. package/dist/modules/access/access-splice.js.map +0 -1
  1266. package/dist/modules/access/group-files.d.ts +0 -83
  1267. package/dist/modules/access/group-files.d.ts.map +0 -1
  1268. package/dist/modules/access/group-files.js +0 -167
  1269. package/dist/modules/access/group-files.js.map +0 -1
  1270. package/dist/modules/access/kb-read-filter.d.ts +0 -41
  1271. package/dist/modules/access/kb-read-filter.d.ts.map +0 -1
  1272. package/dist/modules/access/kb-read-filter.js +0 -60
  1273. package/dist/modules/access/kb-read-filter.js.map +0 -1
  1274. package/dist/modules/access/render-roles-yaml.d.ts +0 -22
  1275. package/dist/modules/access/render-roles-yaml.d.ts.map +0 -1
  1276. package/dist/modules/access/render-roles-yaml.js +0 -56
  1277. package/dist/modules/access/render-roles-yaml.js.map +0 -1
  1278. package/dist/modules/access/roles-yaml-guard.d.ts +0 -56
  1279. package/dist/modules/access/roles-yaml-guard.d.ts.map +0 -1
  1280. package/dist/modules/access/roles-yaml-guard.js +0 -79
  1281. package/dist/modules/access/roles-yaml-guard.js.map +0 -1
  1282. package/dist/modules/diff/diff-paths.d.ts +0 -2
  1283. package/dist/modules/diff/diff-paths.d.ts.map +0 -1
  1284. package/dist/modules/diff/diff-paths.js +0 -9
  1285. package/dist/modules/diff/diff-paths.js.map +0 -1
  1286. package/dist/modules/groups/group-provision.service.d.ts +0 -130
  1287. package/dist/modules/groups/group-provision.service.d.ts.map +0 -1
  1288. package/dist/modules/groups/group-provision.service.js +0 -288
  1289. package/dist/modules/groups/group-provision.service.js.map +0 -1
  1290. package/dist/modules/groups/groups.contract.d.ts +0 -106
  1291. package/dist/modules/groups/groups.contract.d.ts.map +0 -1
  1292. package/dist/modules/groups/groups.contract.js +0 -36
  1293. package/dist/modules/groups/groups.contract.js.map +0 -1
  1294. package/dist/modules/groups/groups.routes.d.ts +0 -42
  1295. package/dist/modules/groups/groups.routes.d.ts.map +0 -1
  1296. package/dist/modules/groups/groups.routes.js +0 -379
  1297. package/dist/modules/groups/groups.routes.js.map +0 -1
  1298. package/dist/modules/groups/groups.service.d.ts +0 -60
  1299. package/dist/modules/groups/groups.service.d.ts.map +0 -1
  1300. package/dist/modules/groups/groups.service.js +0 -172
  1301. package/dist/modules/groups/groups.service.js.map +0 -1
  1302. package/dist/modules/groups/index.d.ts +0 -7
  1303. package/dist/modules/groups/index.d.ts.map +0 -1
  1304. package/dist/modules/groups/index.js +0 -6
  1305. package/dist/modules/groups/index.js.map +0 -1
  1306. package/dist/modules/groups/join-proposals.d.ts +0 -53
  1307. package/dist/modules/groups/join-proposals.d.ts.map +0 -1
  1308. package/dist/modules/groups/join-proposals.js +0 -67
  1309. package/dist/modules/groups/join-proposals.js.map +0 -1
  1310. package/dist/modules/groups/join-requests.service.d.ts +0 -81
  1311. package/dist/modules/groups/join-requests.service.d.ts.map +0 -1
  1312. package/dist/modules/groups/join-requests.service.js +0 -135
  1313. package/dist/modules/groups/join-requests.service.js.map +0 -1
  1314. package/dist/modules/mcp/mcp-session-store.d.ts +0 -74
  1315. package/dist/modules/mcp/mcp-session-store.d.ts.map +0 -1
  1316. package/dist/modules/mcp/mcp-session-store.js +0 -131
  1317. package/dist/modules/mcp/mcp-session-store.js.map +0 -1
  1318. package/dist/modules/workflow/file-change-notifier.d.ts +0 -38
  1319. package/dist/modules/workflow/file-change-notifier.d.ts.map +0 -1
  1320. package/dist/modules/workflow/file-change-notifier.js +0 -22
  1321. package/dist/modules/workflow/file-change-notifier.js.map +0 -1
  1322. package/dist/modules/workflow/git/branch-name.d.ts +0 -10
  1323. package/dist/modules/workflow/git/branch-name.d.ts.map +0 -1
  1324. package/dist/modules/workflow/git/branch-name.js +0 -76
  1325. package/dist/modules/workflow/git/branch-name.js.map +0 -1
  1326. package/dist/modules/workflow/git/clone-config.d.ts +0 -61
  1327. package/dist/modules/workflow/git/clone-config.d.ts.map +0 -1
  1328. package/dist/modules/workflow/git/clone-config.js +0 -69
  1329. package/dist/modules/workflow/git/clone-config.js.map +0 -1
  1330. package/dist/modules/workflow/git/mutex.d.ts +0 -11
  1331. package/dist/modules/workflow/git/mutex.d.ts.map +0 -1
  1332. package/dist/modules/workflow/git/mutex.js +0 -23
  1333. package/dist/modules/workflow/git/mutex.js.map +0 -1
  1334. package/dist/modules/workflow/locking-filesystem.d.ts +0 -137
  1335. package/dist/modules/workflow/locking-filesystem.d.ts.map +0 -1
  1336. package/dist/modules/workflow/locking-filesystem.js +0 -553
  1337. package/dist/modules/workflow/locking-filesystem.js.map +0 -1
  1338. package/dist/modules/workflow/read-only-filesystem.d.ts +0 -24
  1339. package/dist/modules/workflow/read-only-filesystem.d.ts.map +0 -1
  1340. package/dist/modules/workflow/read-only-filesystem.js +0 -39
  1341. package/dist/modules/workflow/read-only-filesystem.js.map +0 -1
  1342. package/dist/modules/workflow/workflow.errors.d.ts +0 -197
  1343. package/dist/modules/workflow/workflow.errors.d.ts.map +0 -1
  1344. package/dist/modules/workflow/workflow.errors.js +0 -298
  1345. package/dist/modules/workflow/workflow.errors.js.map +0 -1
  1346. package/dist/modules/workspace/bevel-ignore.d.ts.map +0 -1
  1347. package/dist/modules/workspace/bevel-ignore.js.map +0 -1
  1348. package/dist/modules/workspace/kb-seed.interface.d.ts +0 -36
  1349. package/dist/modules/workspace/kb-seed.interface.d.ts.map +0 -1
  1350. package/dist/modules/workspace/kb-seed.interface.js +0 -2
  1351. package/dist/modules/workspace/kb-seed.interface.js.map +0 -1
  1352. package/dist/modules/workspace/kb-seed.service.d.ts +0 -125
  1353. package/dist/modules/workspace/kb-seed.service.d.ts.map +0 -1
  1354. package/dist/modules/workspace/kb-seed.service.js +0 -535
  1355. package/dist/modules/workspace/kb-seed.service.js.map +0 -1
  1356. package/dist/modules/workspace/plugins-migration.d.ts +0 -50
  1357. package/dist/modules/workspace/plugins-migration.d.ts.map +0 -1
  1358. package/dist/modules/workspace/plugins-migration.js +0 -379
  1359. package/dist/modules/workspace/plugins-migration.js.map +0 -1
  1360. package/dist/shared/fs-errors.d.ts +0 -8
  1361. package/dist/shared/fs-errors.d.ts.map +0 -1
  1362. package/dist/shared/fs-errors.js +0 -11
  1363. package/dist/shared/fs-errors.js.map +0 -1
  1364. package/dist/shared/fs-walk.d.ts +0 -23
  1365. package/dist/shared/fs-walk.d.ts.map +0 -1
  1366. package/dist/shared/fs-walk.js +0 -42
  1367. package/dist/shared/fs-walk.js.map +0 -1
  1368. package/dist/shared/hash-email.d.ts +0 -11
  1369. package/dist/shared/hash-email.d.ts.map +0 -1
  1370. package/dist/shared/hash-email.js +0 -14
  1371. package/dist/shared/hash-email.js.map +0 -1
  1372. package/dist/shared/kb-walk.d.ts +0 -52
  1373. package/dist/shared/kb-walk.d.ts.map +0 -1
  1374. package/dist/shared/kb-walk.js +0 -76
  1375. package/dist/shared/kb-walk.js.map +0 -1
  1376. package/src/modules/diff/diff-paths.ts +0 -9
  1377. package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
  1378. package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
  1379. package/src/modules/mcp/mcp-session-store.ts +0 -162
  1380. package/src/shared/__tests__/fs-walk.test.ts +0 -63
  1381. package/src/shared/__tests__/kb-walk.test.ts +0 -107
  1382. package/src/shared/fs-errors.ts +0 -10
  1383. package/src/shared/fs-walk.ts +0 -45
  1384. package/src/shared/hash-email.ts +0 -14
  1385. package/src/shared/kb-walk.ts +0 -95
@@ -1,30 +1,35 @@
1
- import { randomUUID } from 'node:crypto';
1
+ import { createHash } from 'node:crypto';
2
+ import { logger } from '../../shared/logging.js';
3
+ const log = logger('mcp');
2
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
- import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, McpError, ErrorCode, isInitializeRequest, } from '@modelcontextprotocol/sdk/types.js';
5
6
  import '@utcp/http'; // side effect: registers the 'http' UTCP communication protocol
6
7
  import '@utcp/mcp'; // side effect: registers the 'mcp' protocol (native MCP-server `.tool` sources)
7
- import { UtcpClientConfigSerializer, CallTemplateSerializer, } from '@utcp/sdk';
8
+ import { UtcpClientConfigSerializer, CallTemplateSerializer, UtcpManualSchema, } from '@utcp/sdk';
8
9
  import { CodeModeUtcpClient } from '@utcp/code-mode';
9
- import { CODE_MODE_META_TOOLS, META_TOOL_NAMES, dispatchMetaTool, dispatchToolCall, registerManual, flattenManualTool, toListedTool, toolError, needsAuthorizationResult, skillPromptText, } from '@bevel-software/platform-mcp-core';
10
+ import { CODE_MODE_META_TOOLS, META_TOOL_NAMES, dispatchMetaTool, dispatchToolCall, registerManual, installSessionRecovery, flattenManualTool, toListedTool, toolError, retiredToolMessage, needsAuthorizationResult, skillPromptText, } from '@bevel-software/platform-mcp-core';
10
11
  import { bevelSecretsLoaderConfig } from '../secrets-vault/index.js';
11
- import { scopesCovered } from '../secrets-vault/secrets-vault.contract.js';
12
+ import { scopesCovered, } from '../secrets-vault/secrets-vault.contract.js';
12
13
  import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/tool-manuals.contract.js';
13
14
  import { seedBevelHostedManualVars } from '../../shared/utcp-namespace.js';
15
+ import { RequestAudit } from '../audit/request-audit.js';
14
16
  import { ManualFailureMemo } from './manual-failure-memo.js';
17
+ import { DownstreamPool, POOL_KEY_SEPARATOR } from './downstream-pool.js';
18
+ import { SurfaceLogThrottle } from './surface-log-throttle.js';
19
+ import { DownstreamRefreshGuard, isDownstreamTokenRejection } from './downstream-token-refresh.js';
20
+ import { printable } from '../../shared/printable.js';
21
+ import { composeAgentInstructions, prefixToolDescription, PREFIXED_TOOLS, } from '../agent-instructions/index.js';
15
22
  /**
16
23
  * Upper bound on one `loopbackJson` round-trip (manual list, skill fetch) so a
17
- * hung loopback can't stall `createSession`. Generous: these endpoints answer
18
- * in milliseconds; only a wedged process ever comes near it.
24
+ * hung loopback can't stall a request. Generous: these endpoints answer in
25
+ * milliseconds; only a wedged process ever comes near it.
19
26
  */
20
27
  const LOOPBACK_TIMEOUT_MS = 15_000;
21
28
  /**
22
- * Lifetime of the internal token minted as an OAuth/JWT session's loopback
23
- * bearer. Longer than the session store's 4h idle eviction (see
24
- * `DEFAULT_IDLE_TTL_MS` in mcp-session-store.ts) so the credential is not the
25
- * first thing to die under a session's normal lifecycle. A continuously-active
26
- * session CAN outlive it — its tool calls then fail at the loopback and the
27
- * client recovers by re-initializing, which mints a fresh token.
29
+ * Lifetime of the internal token minted as an OAuth/JWT request's loopback
30
+ * bearer. A request only needs it for as long as it runs, but one `tools/call`
31
+ * can legitimately run for a long time (`ask`, a long `call_tool_chain`), so
32
+ * the lifetime is generous rather than tight.
28
33
  *
29
34
  * Exported because `POST /api/mcp/local-token` (mcp.routes.ts) performs the
30
35
  * same OAuth-access-token → internal-token exchange for the LOCAL MCP server,
@@ -33,133 +38,213 @@ const LOOPBACK_TIMEOUT_MS = 15_000;
33
38
  */
34
39
  export const MCP_LOOPBACK_TOKEN_TTL_MS = 5 * 60 * 60 * 1000;
35
40
  const callTemplateSerializer = new CallTemplateSerializer();
41
+ /** The {@link DownstreamRoute.afterFailure} answer that means "refreshed — retry once". */
42
+ const RETRY_WITH_REFRESHED_TOKEN = Symbol('retry-with-refreshed-token');
36
43
  /**
37
- * The MCP server is a GENERIC proxy over the UTCP tool surface. It owns no tool
38
- * logic: per MCP session it stands up a `UtcpClient` pointed at the backend's
39
- * own `GET /api/agent/utcp` manual (over loopback, authenticated with the
40
- * caller's connection key), discovers every tool, and re-exposes each one to
41
- * the MCP client. A tool call is dispatched straight back through the same REST
42
- * endpoint the tool's UTCP `tool_call_template` names — so the agent logic,
44
+ * The MCP server is a GENERIC, STATELESS proxy over the UTCP tool surface. It
45
+ * owns no tool logic and no session: every HTTP request gets its own MCP
46
+ * `Server`, built from that request's bearer. When the request needs tools it
47
+ * stands up a `UtcpClient` pointed at the backend's own `GET /api/agent/all-tools`
48
+ * catalog (over loopback, authenticated as the caller, so ACL filtering lives
49
+ * ONCE behind the REST surface), discovers every tool, and re-exposes each one
50
+ * to the MCP client. A tool call is dispatched straight back through the same
51
+ * REST endpoint the tool's UTCP `tool_call_template` names — so the agent logic,
43
52
  * thread continuity, and per-key token metering all live ONCE behind that REST
44
53
  * endpoint (`AgentAskService` for `ask`), never duplicated here.
45
54
  *
55
+ * Because nothing is held between requests, a platform restart between two
56
+ * requests of one client conversation is undetectable by the client.
57
+ *
58
+ * There is NO catalog cache at this layer, deliberately: the catalog is
59
+ * re-derived per request, and if that ever proves too slow the fix belongs in
60
+ * the source path (all-tools/ACL), which serves every other caller too.
61
+ *
62
+ * The one exception to "nothing between requests" is proxied third-party MCP
63
+ * servers (`mcp` manuals): those are session-ful by nature, so their
64
+ * connections live in a {@link DownstreamPool} keyed by (user, manual) — lazy,
65
+ * single-flight, idle-evicted, bounded, and healed by session recovery. Losing
66
+ * any of it costs a reconnect, never correctness.
67
+ *
46
68
  * Dispatch always uses `callToolStreaming`, which is uniform across tool kinds:
47
69
  * a plain `http` tool yields exactly one chunk (its final result, emitted as
48
70
  * the tool result with no progress), while a `streamable_http` tool yields many
49
- * (the all-but-last become `notifications/progress`, the last is the result).
50
- * Because of that, adding — or later upgrading a tool to streaming — never
51
- * touches this file.
71
+ * (the all-but-last become `notifications/progress` on the request's own
72
+ * response stream, the last is the result).
52
73
  *
53
74
  * Auth note: `/api/agent/*` accepts connection keys and internal tokens only.
54
- * Connection-key sessions pass the caller's own key through to the loopback;
55
- * OAuth/JWT sessions can't (their bearer would 401 there), so `createSession`
56
- * mints a least-privilege internal token for the resolved user and uses THAT
57
- * as the session's loopback bearer instead.
75
+ * Connection-key requests pass the caller's own key through to the loopback;
76
+ * OAuth/JWT requests can't (their bearer would 401 there), so the proxy mints a
77
+ * least-privilege internal token for the resolved user and uses THAT as the
78
+ * request's loopback bearer instead.
58
79
  */
59
80
  export class McpService {
60
- sessionStore;
61
81
  opts;
62
82
  secretsVault;
63
83
  toolManuals;
64
84
  internalTokens;
65
85
  revokeOAuthAccess;
86
+ auditRecorder;
66
87
  // Circuit breaker for manuals whose credentials just failed — see the memo.
88
+ // Retry policy, not session state: it survives the move to statelessness.
67
89
  manualFailures = new ManualFailureMemo();
90
+ surfaceLog = new SurfaceLogThrottle();
91
+ // One forced token refresh per (user, manual) per minute — see downstream-token-refresh.
92
+ tokenRefreshes;
68
93
  /**
69
- * Invalidate remembered manual failures — wired to the secrets vault's
70
- * mutation listener, so a just-repaired credential is retried on the very
71
- * next session build. `null` = a shared secret changed (affects everyone).
94
+ * The tools each pooled manual last advertised, by manual definition. A
95
+ * downstream can only be asked what its tools are over a connection, and a
96
+ * caller whose sign-in is missing or broken has no connection — so their
97
+ * tools would silently vanish from the surface, and a call would answer
98
+ * "Unknown tool" instead of "sign in again on /connect". These definitions
99
+ * keep the manual on the surface for such a caller; the call-time credential
100
+ * check then answers with the link, and an interactive client is sent back
101
+ * through authorization. Definitions, never credentials or results. Keyed
102
+ * by catalog name — one entry per manual, replaced when the manual is next
103
+ * discovered — and used only while the manual's definition is the one the
104
+ * tools were discovered from, so an edited `mcp.json` never serves the old
105
+ * server's tools.
72
106
  */
73
- clearManualFailures(userId) {
74
- if (userId === null)
75
- this.manualFailures.clearAll();
76
- else
77
- this.manualFailures.clearUser(userId);
78
- }
79
- constructor(sessionStore, opts,
107
+ knownDownstreamTools = new Map();
108
+ // The downstream connection pool for `mcp` manuals — see the class doc.
109
+ downstream;
110
+ constructor(opts,
80
111
  // The vault + manual catalog, used to check a caller's per-user credentials
81
112
  // before dispatching a tool. Optional so existing constructions/tests that
82
113
  // don't exercise the check keep working (the check is skipped when absent).
83
114
  secretsVault, toolManuals,
84
- // Mints the loopback bearer for OAuth/JWT sessions (see class doc).
85
- // Optional for the same test-compat reason; without it those sessions
115
+ // Mints the loopback bearer for OAuth/JWT requests (see class doc).
116
+ // Optional for the same test-compat reason; without it those requests
86
117
  // fall back to the old pass-through (and 401 at the loopback hop).
87
118
  internalTokens,
88
119
  // Revokes an MCP OAuth access token (BevelOAuthProvider.revokeByAccessToken)
89
- // — the reset that sends an interactive session back through the browser
120
+ // — the reset that sends an interactive client back through the browser
90
121
  // authorization when one of its TOOL sign-ins breaks. Optional: without
91
122
  // it, broken sign-ins surface only as the /connect link in the result.
92
- revokeOAuthAccess) {
93
- this.sessionStore = sessionStore;
123
+ revokeOAuthAccess,
124
+ // The Audit log's recorder: every tool call, skill read and meta-tool run
125
+ // of a request that arrived with a key or an agent connection goes
126
+ // through it. Optional like the others; without it nothing is recorded.
127
+ auditRecorder) {
94
128
  this.opts = opts;
95
129
  this.secretsVault = secretsVault;
96
130
  this.toolManuals = toolManuals;
97
131
  this.internalTokens = internalTokens;
98
132
  this.revokeOAuthAccess = revokeOAuthAccess;
133
+ this.auditRecorder = auditRecorder;
134
+ this.downstream = new DownstreamPool({
135
+ ...opts.downstreamPool,
136
+ // Since @utcp/sdk 1.2.0 a client closes only the protocol instances it
137
+ // created, and @utcp/mcp registers as a factory — so this closes exactly
138
+ // this entry's MCP sessions and touches no other client's.
139
+ dispose: (entry) => entry.client.close(),
140
+ });
141
+ // Paced on the pool's clock, so a test that moves one moves both.
142
+ this.tokenRefreshes = new DownstreamRefreshGuard(undefined, opts.downstreamPool?.now);
99
143
  }
100
144
  /**
101
- * Subscribe to store-driven evictions (idle-TTL sweep, size-cap eviction) so
102
- * the route layer can drop its transport mirror. Returns an unsubscribe fn.
145
+ * A secret changed — wired to the secrets vault's mutation listener.
146
+ * `null` = a shared secret changed (affects everyone).
147
+ *
148
+ * Two things were built from the old value: remembered manual failures (so a
149
+ * just-repaired credential is retried on the very next request) and pooled
150
+ * downstream connections (dialed and discovered with the old credential).
151
+ * Both are dropped; the next request rebuilds whatever it needs.
103
152
  */
104
- onSessionEvicted(listener) {
105
- return this.sessionStore.onEvict(listener);
153
+ onSecretsChanged(userId) {
154
+ this.forgetStaleRefreshWindows(userId);
155
+ if (userId === null) {
156
+ this.manualFailures.clearAll();
157
+ this.downstream.closeAll();
158
+ return;
159
+ }
160
+ this.manualFailures.clearUser(userId);
161
+ this.downstream.evictWhere((key) => key.startsWith(`${userId}${POOL_KEY_SEPARATOR}`));
106
162
  }
107
163
  /**
108
- * Build a fresh per-session (transport, server) pair. Async because tool
109
- * discovery (a loopback round-trip authenticated with `bearer`) happens here,
110
- * before the server is returned, so `tools/list` is ready the moment the
111
- * client asks. The route awaits this, then `server.connect(transport)`.
164
+ * A credential changed, so a refresh window that FAILED to produce one no
165
+ * longer describes anything: a token the user has just re-authorized (or an
166
+ * admin has just re-keyed) must be tried on the very next rejection, not sit
167
+ * out the remainder of a minute earned by the credential it replaced.
168
+ *
169
+ * Only settled, non-`refreshed` windows are forgotten. A refresh of our own
170
+ * is itself a secrets mutation, and clearing on that would hand a provider
171
+ * that keeps minting tokens the downstream keeps refusing one refresh per
172
+ * call — precisely the loop the once-a-minute guard exists to bound. An
173
+ * in-flight attempt (no outcome yet) is left alone for the same reason: it
174
+ * may be the very refresh that raised this notification.
112
175
  */
113
- async createSession(userId, tokenId, bearer, onSessionInitialized) {
176
+ forgetStaleRefreshWindows(userId) {
177
+ const mine = (key) => userId === null || key.startsWith(`${userId}${POOL_KEY_SEPARATOR}`);
178
+ this.tokenRefreshes.clearWhere((key, outcome) => mine(key) && outcome !== undefined && outcome !== 'refreshed');
179
+ }
180
+ /**
181
+ * Build the MCP `Server` for ONE request. The route connects it to a fresh
182
+ * stateless transport, lets it answer, and closes both with the response.
183
+ *
184
+ * Cheap by construction: nothing is fetched up front except what the
185
+ * request's own messages need. The agent instructions are read only when the
186
+ * request carries an `initialize` (the only place they ride); the tool
187
+ * catalog is built on the first handler that needs it and shared by every
188
+ * message of the request (a JSON-RPC batch), never beyond it.
189
+ */
190
+ async createRequestServer(caller, messages) {
191
+ const { userId, tokenId, bearer } = caller;
192
+ const connectionId = caller.connectionId ?? null;
114
193
  // The loopback surface (`/api/agent/*`) accepts connection keys and
115
- // internal tokens only. A connection-key session passes the caller's own
116
- // key through (per-key metering rides on it); an OAuth/JWT session's
194
+ // internal tokens only. A connection-key request passes the caller's own
195
+ // key through (per-key metering rides on it); an OAuth/JWT request's
117
196
  // bearer would 401 there, so mint a least-privilege internal token for
118
197
  // the resolved user instead — flagged `externalProxy` so the tool-auth
119
198
  // verifier resolves it to `source: 'external'`: the caller IS an external
120
199
  // agent and must be treated like one (admitted to `start_session`/`ask`,
121
- // refused from internal-only tools). TTL covers the session store's 4h
122
- // idle eviction — an evicted session re-initializes and re-mints.
200
+ // refused from internal-only tools).
123
201
  const loopbackBearer = tokenId == null && this.internalTokens
124
202
  ? this.internalTokens.mint({ userId, externalProxy: true }, MCP_LOOPBACK_TOKEN_TTL_MS)
125
203
  : bearer;
126
- // The list of manuals this caller gets: the KB manual + each `.tool` they
127
- // can read. Fetched over loopback with the caller's key, so ACL filtering
128
- // lives ONCE behind the REST surface.
129
- const manuals = await this.fetchManualTemplates(loopbackBearer);
130
- const client = await this.buildClient(loopbackBearer, userId, manuals);
131
- const tools = await this.discoverTools(client, manuals, userId);
132
- // Confirms the MCP session was built for a connecting client (vs. an
133
- // in-process agent code-mode client, which never runs createSession) and
134
- // how many tools discovery yielded before listing/filtering.
135
- console.log(`[mcp] createSession: user=${userId} tokenId=${tokenId ?? 'none'} — ` +
136
- `discovered ${tools.length} tool(s) across ${manuals.length} manual(s)`);
137
- const transport = new StreamableHTTPServerTransport({
138
- sessionIdGenerator: () => randomUUID(),
139
- onsessioninitialized: (sessionId) => {
140
- this.sessionStore.create(sessionId, userId, tokenId);
141
- onSessionInitialized(sessionId);
142
- },
143
- onsessionclosed: (sessionId) => {
144
- this.sessionStore.delete(sessionId);
145
- },
204
+ let instructions;
205
+ const agentInstructions = () => (instructions ??= this.composeAgentInstructions());
206
+ // The Audit log's view of this request — only when the request can be
207
+ // attributed to a key or an agent connection. The catalog names it
208
+ // classifies pooled tools by are the surface's, which is built lazily
209
+ // below, so it reads them through a holder the surface fills in.
210
+ const catalogNamesHolder = { current: new Map() };
211
+ const principal = tokenId != null ? { kind: 'key', id: tokenId } : connectionId ? { kind: 'agent', id: connectionId } : null;
212
+ const audit = this.auditRecorder && principal
213
+ ? new RequestAudit(this.auditRecorder, userId, principal, {
214
+ kbManualName: EXTERNAL_KB_MANUAL_NAME,
215
+ catalogNames: () => catalogNamesHolder.current,
216
+ loadSkills: () => this.fetchSkillList(loopbackBearer).then((skills) => skills.flatMap((s) => (s.path ? [{ name: s.name, path: s.path }] : []))),
217
+ })
218
+ : null;
219
+ let surface;
220
+ const requestSurface = () => (surface ??= this.buildSurface(userId, tokenId, loopbackBearer).then((built) => {
221
+ catalogNamesHolder.current = built.catalogNames;
222
+ audit?.instrumentChainCalls(built.client);
223
+ return built;
224
+ }));
225
+ const initializing = (Array.isArray(messages) ? messages : [messages]).some((m) => isInitializeRequest(m));
226
+ const server = new Server({ name: 'bevel-mcp', version: '0.1.0' }, {
227
+ capabilities: { tools: {}, prompts: {} },
228
+ // `instructions` rides the initialize result; clients that honour it
229
+ // place the text in the model's system prompt without the model acting.
230
+ ...(initializing ? { instructions: (await agentInstructions()).instructions } : {}),
146
231
  });
147
- const server = new Server({ name: 'bevel-mcp', version: '0.1.0' }, { capabilities: { tools: {}, prompts: {} } });
148
232
  server.setRequestHandler(ListToolsRequestSchema, async () => {
233
+ const [{ tools, catalogNames }, { toolPrefix }] = await Promise.all([requestSurface(), agentInstructions()]);
149
234
  // A discovered tool whose name collides with a meta-tool would be
150
235
  // listed but never callable (the dispatcher routes the name to the
151
236
  // meta-tool first), so drop it from the listing entirely.
152
237
  let listed = tools.filter((t) => !META_TOOL_NAMES.has(t.mcpName));
153
- // Connection-key sessions are autonomous pipelines — nobody is present
238
+ // Connection-key callers are autonomous pipelines — nobody is present
154
239
  // to complete a sign-in mid-run, so register ONLY the tools whose
155
240
  // per-user credentials are already satisfied. Interactive (OAuth/JWT)
156
- // sessions keep the full listing: their caller can configure a tool on
241
+ // callers keep the full listing: they can configure a tool on
157
242
  // /connect when the call-time check points them there.
158
243
  if (tokenId != null) {
159
244
  const ready = await Promise.all(
160
245
  // A per-tool check that throws must NOT reject the whole list (which
161
246
  // would blank every tool) — fail that one tool closed and move on.
162
- listed.map((t) => this.missingUserSecrets(userId, t).then((missing) => missing.length === 0, () => false)));
247
+ listed.map((t) => this.missingUserSecrets(userId, catalogNames.get(t.manualName) ?? t.manualName).then((missing) => missing.length === 0, () => false)));
163
248
  listed = listed.filter((_, i) => ready[i]);
164
249
  }
165
250
  // Validate + dedupe each discovered tool so ONE non-conforming or
@@ -180,14 +265,20 @@ export class McpService {
180
265
  continue;
181
266
  }
182
267
  seen.add(entry.name);
183
- direct.push(entry);
268
+ // The four knowledge-base tools carry the purpose prefix: the one
269
+ // pre-call channel every client shows the model, for the clients that
270
+ // drop the handshake's `instructions`. Applied AFTER the credential
271
+ // filter above, so a connection-key caller's listing carries it too.
272
+ // Every other tool, meta-tools included, keeps its description as is.
273
+ direct.push(PREFIXED_TOOLS.has(entry.name)
274
+ ? { ...entry, description: prefixToolDescription(toolPrefix, entry.description) }
275
+ : entry);
184
276
  }
185
277
  // Log only when a tool was dropped (name/schema/duplicate) — that's the
186
278
  // anomaly worth surfacing, since a downstream client would otherwise hide
187
- // it by rejecting the whole response. The steady-state served count is
188
- // already visible once per session in the createSession log above.
279
+ // it by rejecting the whole response.
189
280
  if (dropped.length) {
190
- console.warn(`[mcp] tools/list: serving ${CODE_MODE_META_TOOLS.length + direct.length} tool(s); ` +
281
+ log.warn(`tools/list: serving ${CODE_MODE_META_TOOLS.length + direct.length} tool(s); ` +
191
282
  `dropped ${dropped.length} non-listable: ${dropped.join(', ')}`);
192
283
  }
193
284
  return {
@@ -196,44 +287,77 @@ export class McpService {
196
287
  };
197
288
  });
198
289
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
199
- if (META_TOOL_NAMES.has(request.params.name)) {
200
- return this.dispatchMetaTool(client, request.params.name, request.params.arguments ?? {});
201
- }
202
- const proxied = tools.find((t) => t.mcpName === request.params.name);
203
- if (!proxied) {
204
- return toolError(`Unknown tool "${request.params.name}".`);
290
+ const { client, tools, unavailable, catalogNames } = await requestSurface();
291
+ const toolName = request.params.name;
292
+ const args = request.params.arguments ?? {};
293
+ if (META_TOOL_NAMES.has(toolName)) {
294
+ // A meta-tool is the platform's own; its inner tool calls (a chain's)
295
+ // are recorded one by one through the instrumented client.
296
+ const run = () => this.dispatchMetaTool(client, toolName, args);
297
+ return audit ? audit.call(toolName, args, run, isErrorResult) : run();
205
298
  }
206
- // Stop before running a tool whose personal (user-scoped) credentials the
207
- // caller hasn't provided — return a setup link instead of a blank-credential
208
- // request that would fail opaquely at the provider.
209
- const needsAuth = await this.checkUserSecrets(userId, proxied);
210
- if (needsAuth) {
299
+ /**
300
+ * The needs-authorization answer, when the caller's sign-in for the
301
+ * manual registered as `utcpName` is missing or broken; `null` to
302
+ * proceed. Stops before running a tool whose personal (user-scoped)
303
+ * credentials the caller hasn't provided — a setup link instead of a
304
+ * blank-credential request that would fail opaquely at the provider.
305
+ * The credential catalog keys manuals by their CATALOG name; a tool
306
+ * carries the registered (rewritten) one, so it is translated back.
307
+ */
308
+ const needsAuthorization = async (utcpName) => {
309
+ const needsAuth = await this.checkUserSecrets(userId, toolName, catalogNames.get(utcpName) ?? utcpName);
310
+ if (!needsAuth)
311
+ return null;
211
312
  // A sign-in that EXISTS but is broken (expired grant, abandoned
212
- // consent, missing scopes) on an interactive OAuth session: revoke the
313
+ // consent, missing scopes) on an interactive OAuth caller: revoke the
213
314
  // agent's own grant too. Its next request then 401s, its refresh
214
315
  // fails, and it re-runs the browser authorization — landing the user
215
316
  // on /connect where the broken sign-in shows as not-connected, to
216
317
  // re-authorize or deselect. Never-configured tools keep the plain
217
318
  // link (revoking for those would loop on every poke at a tool the
218
- // user simply hasn't set up). Connection-key sessions have no grant
319
+ // user simply hasn't set up). Connection-key callers have no grant
219
320
  // to reset; the revoke no-ops on non-OAuth bearers anyway.
220
321
  if (needsAuth.brokenSignIn && tokenId == null && this.revokeOAuthAccess) {
221
322
  try {
222
323
  await this.revokeOAuthAccess(bearer);
223
324
  }
224
325
  catch (err) {
225
- console.warn('[mcp] failed to reset the session grant for re-auth:', err);
326
+ log.warn('failed to reset the caller grant for re-auth:', { err });
226
327
  }
227
328
  }
228
329
  return needsAuth.result;
330
+ };
331
+ const proxied = tools.find((t) => t.mcpName === toolName);
332
+ if (!proxied) {
333
+ // Not on the surface — but if the name belongs to a manual that is off
334
+ // it only because this caller's sign-in is gone (and nothing in this
335
+ // process has seen its tools yet), the honest answer is the sign-in
336
+ // link, exactly as if the tool were listed. Longest matching prefix:
337
+ // manual names may themselves contain underscores.
338
+ const owner = unavailable
339
+ .filter((m) => toolName.startsWith(`${m.utcpName}_`))
340
+ .sort((a, b) => b.utcpName.length - a.utcpName.length)[0];
341
+ const answer = owner ? await needsAuthorization(owner.utcpName) : null;
342
+ if (answer && owner && audit) {
343
+ await audit.denied(`${owner.utcpName}.${toolName.slice(owner.utcpName.length + 1)}`, args);
344
+ }
345
+ return answer ?? toolError(retiredToolMessage(toolName) ?? `Unknown tool "${toolName}".`);
229
346
  }
230
- return this.dispatch(client, proxied, request, extra);
347
+ const needsAuth = await needsAuthorization(proxied.manualName);
348
+ if (needsAuth) {
349
+ if (audit)
350
+ await audit.denied(proxied.utcpName, args);
351
+ return needsAuth;
352
+ }
353
+ const run = () => this.dispatch(client, proxied, request, extra);
354
+ return audit ? audit.call(proxied.utcpName, args, run, isErrorResult) : run();
231
355
  });
232
356
  // Prompts = skills. Each skill becomes a user-callable prompt (slash command
233
357
  // in MCP clients). Backed by the same skill endpoints the tools use, over
234
358
  // loopback with the caller's key — so the default-branch catalog, access
235
359
  // filtering, and progressive disclosure all live ONCE behind that REST
236
- // surface, never duplicated here.
360
+ // surface, never duplicated here. Neither handler needs the tool catalog.
237
361
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
238
362
  const skills = await this.fetchSkillList(loopbackBearer);
239
363
  const prompts = skills.map((s) => ({
@@ -244,7 +368,10 @@ export class McpService {
244
368
  return { prompts };
245
369
  });
246
370
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
371
+ const started = performance.now();
247
372
  const skill = await this.fetchSkill(loopbackBearer, request.params.name);
373
+ // A prompt IS a skill read — the same event `get_skill` produces.
374
+ audit?.emit({ kind: 'skill', manual: skill?.path ?? null, name: request.params.name }, skill ? 'ok' : 'error', Math.round(performance.now() - started));
248
375
  if (!skill) {
249
376
  throw new McpError(ErrorCode.InvalidParams, `Unknown skill "${request.params.name}".`);
250
377
  }
@@ -253,7 +380,57 @@ export class McpService {
253
380
  messages: [{ role: 'user', content: { type: 'text', text: skillPromptText(skill) } }],
254
381
  };
255
382
  });
256
- return { transport, server };
383
+ return server;
384
+ }
385
+ /**
386
+ * The request's tool surface: catalog over loopback, a fresh UTCP client,
387
+ * every manual registered (pooled for `mcp` ones), tools flattened.
388
+ *
389
+ * Logs one line with the time each half took — the per-request overhead
390
+ * the stateless design trades for restart-invisibility, kept visible so it
391
+ * is measured rather than assumed.
392
+ */
393
+ async buildSurface(userId, tokenId, loopbackBearer) {
394
+ const started = performance.now();
395
+ // The list of manuals this caller gets: the KB manual + each `.tool` they
396
+ // can read. Fetched over loopback with the caller's key, so ACL filtering
397
+ // lives ONCE behind the REST surface.
398
+ const manuals = await this.fetchManualTemplates(loopbackBearer);
399
+ const catalogMs = performance.now() - started;
400
+ const client = await this.buildClient(loopbackBearer, userId, manuals);
401
+ const { tools, unavailable, catalogNames } = await this.discoverTools(client, manuals, userId);
402
+ const totalMs = performance.now() - started;
403
+ // Per user: on a shape change or once per interval, never per request —
404
+ // see SurfaceLogThrottle for why both halves matter.
405
+ const decision = this.surfaceLog.decide(userId, { tools: tools.length, manuals: manuals.length });
406
+ if (decision.log) {
407
+ log.info(
408
+ // Both ids come from the request (an identity provider's subject, a
409
+ // connection key's id), so neither is interpolated raw — see printable.
410
+ `request surface: user=${printable(userId)} tokenId=${tokenId ? printable(tokenId) : 'none'} — ` +
411
+ `${tools.length} tool(s) across ${manuals.length} manual(s) in ${totalMs.toFixed(0)}ms ` +
412
+ `(catalog ${catalogMs.toFixed(0)}ms, registration ${(totalMs - catalogMs).toFixed(0)}ms)` +
413
+ (decision.suppressed > 0 ? ` [+${decision.suppressed} identical rebuild(s) since last line]` : ''));
414
+ }
415
+ return { client, tools, unavailable, catalogNames };
416
+ }
417
+ /**
418
+ * The instructions and tool prefix. A reader failure (a disk fault; ENOENT
419
+ * is not one, the reader answers null for that) falls back to the header
420
+ * alone and the fixed prefix line with a logged warning: a request never
421
+ * fails over its preamble.
422
+ */
423
+ async composeAgentInstructions() {
424
+ const read = this.opts.readAgentPreamble;
425
+ if (!read)
426
+ return composeAgentInstructions(null);
427
+ try {
428
+ return composeAgentInstructions(await read());
429
+ }
430
+ catch (err) {
431
+ log.warn('could not read mcp-description.md; this request gets the platform header alone:', { err });
432
+ return composeAgentInstructions(null);
433
+ }
257
434
  }
258
435
  /** Loopback GET of the default-branch skill catalog (via the `list_skills` tool). */
259
436
  async fetchSkillList(bearer) {
@@ -272,10 +449,9 @@ export class McpService {
272
449
  * The ONE loopback round-trip: fetch `path` on our own REST surface with the
273
450
  * caller's bearer, parse JSON. Failures are logged under `label` (never the
274
451
  * bearer) and degrade to null — a loopback hiccup must not throw into the
275
- * MCP session. GET when `json` is absent, POST with a JSON body when present.
276
- * Bounded: a hung loopback (e.g. mid-restart) must not block `createSession`
277
- * or a prompts handler indefinitely; a timeout aborts and degrades to null
278
- * like any other failure.
452
+ * MCP request. GET when `json` is absent, POST with a JSON body when present.
453
+ * Bounded: a hung loopback (e.g. mid-restart) must not block a request
454
+ * indefinitely; a timeout aborts and degrades to null like any other failure.
279
455
  */
280
456
  async loopbackJson(bearer, path, label, json) {
281
457
  try {
@@ -288,13 +464,13 @@ export class McpService {
288
464
  signal: AbortSignal.timeout(LOOPBACK_TIMEOUT_MS),
289
465
  });
290
466
  if (!res.ok) {
291
- console.error(`[mcp] ${label} loopback failed: HTTP ${res.status} ${await res.text().catch(() => '')}`);
467
+ log.error(`${label} loopback failed: HTTP ${res.status} ${await res.text().catch(() => '')}`);
292
468
  return null;
293
469
  }
294
470
  return await res.json();
295
471
  }
296
472
  catch (err) {
297
- console.error(`[mcp] ${label} loopback threw:`, err instanceof Error ? err.message : err);
473
+ log.error(`${label} loopback threw:`, { err });
298
474
  return null;
299
475
  }
300
476
  }
@@ -307,7 +483,7 @@ export class McpService {
307
483
  * validated into `CallTemplate`s at the boundary. The KB manual is guaranteed
308
484
  * present (a fallback covers an unavailable endpoint); a user manual that
309
485
  * fails validation is dropped + logged so one bad `.tool` can't break the
310
- * session. HTTP transport loses the `CallTemplate` type, so we re-validate the
486
+ * request. HTTP transport loses the `CallTemplate` type, so we re-validate the
311
487
  * received JSON even though the producing endpoint already validated it.
312
488
  */
313
489
  async fetchManualTemplates(bearer) {
@@ -328,7 +504,7 @@ export class McpService {
328
504
  const name = String(raw?.name ?? '');
329
505
  if (isKb)
330
506
  throw err; // the KB manual must be valid — the core toolset depends on it
331
- console.warn(`[mcp] skipping manual "${name}": ${err instanceof Error ? err.message : String(err)}`);
507
+ log.warn(`skipping manual "${name}": ${err instanceof Error ? err.message : String(err)}`);
332
508
  }
333
509
  }
334
510
  // Always include the KB manual, even if `all-tools` was unavailable/regressed.
@@ -348,75 +524,360 @@ export class McpService {
348
524
  });
349
525
  }
350
526
  /**
351
- * One `CodeModeUtcpClient` per session. Reserved `API_URL`/`CONNECTION_KEY` are
527
+ * One `CodeModeUtcpClient` per request. Reserved `API_URL`/`CONNECTION_KEY` are
352
528
  * seeded (namespaced) ONLY for Bevel-hosted manuals — the KB manual and inline
353
529
  * `.tool` sub-manuals, whose discovery template targets the loopback (`${API_URL}`).
354
530
  * Third-party http/mcp `.tool`s point at arbitrary user URLs, so we must NOT seed
355
531
  * them the caller's bearer: a malicious `.tool` referencing `${CONNECTION_KEY}`
356
532
  * would otherwise exfiltrate the caller's token to its own endpoint. Tool
357
533
  * `${SECRET}` refs resolve lazily via the per-user `bevel-secrets` loader.
534
+ *
535
+ * Not `close()`d: a request's client holds no connection of its own (its
536
+ * `mcp` manuals route to the pool, so its own `mcp` protocol instance never
537
+ * opens a session), and dropping the reference is the whole teardown.
358
538
  */
359
539
  async buildClient(bearer, userId, manuals) {
360
- // Bevel-hosted manuals (KB + inline `.tool` sub-manuals, discovery url
361
- // `${API_URL}/…`) get the loopback creds; third-party `.tool` URLs are never
362
- // seeded so the bearer can't leak. Same rule the in-process agent factory uses.
363
540
  const variables = seedBevelHostedManualVars(manuals, this.opts.loopbackBaseUrl, bearer);
364
- const config = new UtcpClientConfigSerializer().validateDict({
365
- variables,
366
- load_variables_from: [bevelSecretsLoaderConfig(userId)],
367
- });
368
- return CodeModeUtcpClient.create(process.cwd(), config);
541
+ return CodeModeUtcpClient.create(process.cwd(), utcpClientConfig(userId, variables));
369
542
  }
370
543
  /**
371
- * Register EVERY manual on the client, then flatten each discovered tool into
372
- * the proxy's advertised shape. A failure registering a user `.tool` is
373
- * isolated (logged + skipped) so one broken manual never breaks the session;
374
- * the KB manual failing is fatal (the core toolset is unusable).
544
+ * Register EVERY manual for this request, then flatten each discovered tool
545
+ * into the proxy's advertised shape. Manuals register concurrently — they are
546
+ * independent, and a request pays for the slowest one rather than the sum. A
547
+ * failure registering a user `.tool` is isolated (logged + skipped) so one
548
+ * broken manual never breaks the request; the KB manual failing is fatal (the
549
+ * core toolset is unusable).
550
+ *
551
+ * `mcp` manuals never dial from here: they are attached from the downstream
552
+ * pool (see {@link attachDownstream}).
375
553
  *
376
- * Failures are memoized per (user, manual) for a few minutes (see
377
- * {@link ManualFailureMemo}): sessions rebuild constantly, and a manual with
378
- * a broken credential (expired OAuth, revoked key) would otherwise re-dial
379
- * its provider on every single build.
554
+ * Failures are memoized per (user, manual definition) for a few minutes (see
555
+ * {@link ManualFailureMemo}): the catalog is rebuilt on every request, and a
556
+ * manual with a broken credential (expired OAuth, revoked key) would
557
+ * otherwise re-dial its provider on every single one. The definition's
558
+ * fingerprint is part of the key, so a manual edited after a failure (a
559
+ * corrected `mcp.json` URL) is tried on the very next request.
380
560
  */
381
561
  async discoverTools(client, manuals, userId) {
562
+ const routes = new Map();
563
+ const unavailable = [];
564
+ const catalogNames = new Map();
382
565
  for (const m of manuals) {
566
+ if (m.call_template_type === 'mcp')
567
+ catalogNames.set(utcpManualName(m), String(m.name));
568
+ }
569
+ // The shared layer rewrites every manual name (`[^\w]` → `_`) and tools
570
+ // route by the rewritten prefix, so two manuals whose names rewrite to one
571
+ // identifier would silently share it. Sequential registration used to
572
+ // throw "already registered" for the second; concurrent registration
573
+ // (below) would let both pass the shared layer's pre-check, one
574
+ // overwriting the other in the repository while `routes` kept whichever
575
+ // resolved last — tools/list from one server, tools/call to the other.
576
+ // Decided here, over the whole list, before anything is registered: every
577
+ // manual in a colliding group fails with a message naming the others. The
578
+ // KB manual always keeps its name — a `.tool` colliding with it fails,
579
+ // the KB manual does not.
580
+ const byRewrittenName = new Map();
581
+ for (const m of manuals) {
582
+ const rewritten = utcpManualName(m);
583
+ byRewrittenName.set(rewritten, [...(byRewrittenName.get(rewritten) ?? []), String(m.name)]);
584
+ }
585
+ const outcomes = await Promise.all(manuals.map(async (m) => {
383
586
  const isKb = m.name === EXTERNAL_KB_MANUAL_NAME;
384
587
  const name = String(m.name);
588
+ // Both taken before registration, which renames the template in place.
589
+ const rewritten = utcpManualName(m);
590
+ const siblings = (byRewrittenName.get(rewritten) ?? []).filter((s) => s !== name);
591
+ // A collision is a property of the LIST, not of this manual's
592
+ // definition, so the memo key carries the siblings: the moment one is
593
+ // renamed away, the key changes and the survivor is tried on the very
594
+ // next request instead of sitting out the memo's TTL.
595
+ const memoKey = `${name}${POOL_KEY_SEPARATOR}${templateFingerprint(m)}` +
596
+ (siblings.length > 0 ? `${POOL_KEY_SEPARATOR}collides:${[...siblings].sort().join(',')}` : '');
385
597
  if (!isKb) {
386
- const recent = this.manualFailures.recentFailure(userId, name);
598
+ const recent = this.manualFailures.recentFailure(userId, memoKey);
387
599
  if (recent !== undefined) {
388
- console.warn(`[mcp] skipping manual "${name}" (recent failure, not retried): ${recent}`);
389
- continue;
600
+ log.warn(`skipping manual "${name}" (recent failure, not retried): ${recent}`);
601
+ return { isKb, ok: true };
390
602
  }
391
603
  }
392
- // Captured BEFORE the awaited attempt: if a secrets change clears the
393
- // memo while registration is in flight, the stale failure from the OLD
394
- // credential must not resurrect an entry the clear removed.
395
- const generation = this.manualFailures.currentGeneration;
396
- // `registerManual` never throws: a discovery/network failure and a
397
- // validation failure both come back as `{ ok: false }`, because the retry
398
- // policy — this memo — is ours, not the shared layer's.
399
- const result = await registerManual(client, m);
400
- if (!result.ok) {
401
- if (isKb)
402
- throw new Error(`Bevel tool discovery failed: ${result.error}`);
403
- this.manualFailures.recordFailure(userId, name, result.error, generation);
404
- console.warn(`[mcp] skipping manual "${name}": ${result.error}`);
604
+ // Begun BEFORE the awaited attempt: if a secrets change (or a
605
+ // concurrent success of this same manual) clears the memo while
606
+ // registration is in flight, the stale failure must not resurrect an
607
+ // entry the clear removed.
608
+ const generation = this.manualFailures.beginAttempt();
609
+ try {
610
+ // Neither path throws: a discovery/network failure and a validation
611
+ // failure both come back as `{ ok: false }`, because the retry
612
+ // policy — this memo — is ours, not the shared layer's.
613
+ const result = !isKb && siblings.length > 0
614
+ ? {
615
+ ok: false,
616
+ error: `manual name "${name}" rewrites to "${rewritten}", the same identifier as ` +
617
+ `${siblings.map((s) => `"${s}"`).join(', ')} — rename one; a colliding manual is not registered`,
618
+ }
619
+ : m.call_template_type === 'mcp'
620
+ ? await this.attachDownstream(client, m, userId, routes)
621
+ : await registerManual(client, m);
622
+ if (!result.ok) {
623
+ if (isKb)
624
+ return { isKb, ok: false, error: result.error };
625
+ // A failure the manual may recover from on its own schedule (a
626
+ // token refresh that hasn't settled) is reported but NOT
627
+ // remembered: the memo's five minutes would outlast the refresh
628
+ // policy's one and hold the manual down after the provider is back.
629
+ if (!result.retryable)
630
+ this.manualFailures.recordFailure(userId, memoKey, result.error, generation);
631
+ if (result.signInMissing)
632
+ unavailable.push({ utcpName: rewritten });
633
+ log.warn(`skipping manual "${name}": ${result.error}`);
634
+ }
635
+ else if (!isKb) {
636
+ this.manualFailures.clear(userId, memoKey);
637
+ }
405
638
  }
406
- else if (!isKb) {
407
- this.manualFailures.clear(userId, name);
639
+ finally {
640
+ this.manualFailures.endAttempt(generation);
408
641
  }
409
- }
642
+ return { isKb, ok: true };
643
+ }));
644
+ const kbFailure = outcomes.find((o) => o.isKb && !o.ok);
645
+ if (kbFailure && !kbFailure.ok)
646
+ throw new Error(`Bevel tool discovery failed: ${kbFailure.error}`);
647
+ if (routes.size > 0)
648
+ routeToDownstream(client, routes);
410
649
  const utcpTools = await client.getTools();
411
- return utcpTools.map((tool) => flattenManualTool(tool, EXTERNAL_KB_MANUAL_NAME));
650
+ return {
651
+ tools: utcpTools.map((tool) => flattenManualTool(tool, EXTERNAL_KB_MANUAL_NAME)),
652
+ unavailable,
653
+ catalogNames,
654
+ };
412
655
  }
413
656
  /**
414
- * Run one tool call through `callToolStreaming` with a one-chunk lookahead:
415
- * every chunk except the last becomes a progress notification, the last is
416
- * the result. Continuity is the caller's: a tool that supports it (e.g.
417
- * `ask`) returns its `sessionId` in the result verbatim, and the caller
418
- * echoes it back per the tool's own schema — the proxy never rewrites args.
657
+ * Make one `mcp` manual's tools part of this request's client, backed by the
658
+ * pooled connection for (user, manual).
659
+ *
660
+ * The pooled client's discovered tools are copied into the request client's
661
+ * repository — so `tools/list`, `list_tools`/`tools_info` and the TypeScript
662
+ * interfaces `call_tool_chain` generates all see them exactly as if they had
663
+ * been registered here — and `routes` records that calls to this manual go to
664
+ * the pool. The route re-acquires at CALL time rather than holding the pooled
665
+ * client, so an entry evicted between discovery and the call is transparently
666
+ * re-created instead of used after its connection was closed. Each use holds
667
+ * a pool lease only for its own duration, so the pool never closes a
668
+ * connection an operation is still using.
419
669
  */
670
+ async attachDownstream(client, template, userId, routes) {
671
+ const key = downstreamPoolKey(userId, template);
672
+ // The catalog name, which is what the manual's per-user variables are keyed by.
673
+ const catalogName = String(template.name ?? '');
674
+ const manualName = utcpManualName(template);
675
+ const definition = templateFingerprint(template);
676
+ const acquire = () => this.downstream.acquire(key, () => this.connectDownstream(userId, template));
677
+ const afterFailure = (err) => this.afterDownstreamFailure(userId, catalogName, key, err);
678
+ const attach = async (tools) => {
679
+ await client.config.tool_repository.saveManual({ ...template, name: manualName }, UtcpManualSchema.parse({ tools }));
680
+ routes.set(manualName, { acquire, afterFailure });
681
+ };
682
+ // Set when the handshake failed on a credential the provider may yet
683
+ // renew: the caller must NOT remember that as a dead manual (see below).
684
+ let credentialTransient = false;
685
+ try {
686
+ let lease;
687
+ try {
688
+ lease = await acquire();
689
+ }
690
+ catch (err) {
691
+ // A connection dialed with a token the server refuses fails right here,
692
+ // at the handshake — the same refresh-and-retry applies.
693
+ const verdict = await this.classifyDownstreamFailure(userId, catalogName, key, err);
694
+ if (!verdict.retry) {
695
+ credentialTransient = verdict.credentialTransient;
696
+ throw verdict.error;
697
+ }
698
+ lease = await acquire();
699
+ }
700
+ let tools;
701
+ try {
702
+ tools = (await lease.value.client.getTools()).filter((t) => t.name.startsWith(`${manualName}.`));
703
+ }
704
+ finally {
705
+ lease.release();
706
+ }
707
+ this.knownDownstreamTools.set(catalogName, { definition, tools });
708
+ await attach(tools);
709
+ return { ok: true };
710
+ }
711
+ catch (err) {
712
+ const error = err instanceof Error ? err.message : String(err);
713
+ // No connection because this CALLER has no usable sign-in for the manual
714
+ // (never signed in, or the grant was just found dead and wiped): the
715
+ // manual is not broken, the sign-in is. Keep its tools on the surface
716
+ // from what the downstream last advertised, so a call answers with the
717
+ // sign-in link — and never remember the failure, since re-authorizing is
718
+ // one visit to /connect away and the next attempt costs no network.
719
+ if (await this.signInMissing(userId, catalogName)) {
720
+ const known = this.knownDownstreamTools.get(catalogName);
721
+ if (known && known.definition === definition) {
722
+ await attach(known.tools);
723
+ return { ok: true };
724
+ }
725
+ return { ok: false, error, retryable: true, signInMissing: true };
726
+ }
727
+ // `retryable` keeps a transient credential failure out of the caller's
728
+ // five-minute failure memo: the refresh-and-retry policy for a rejected
729
+ // token is one minute, and a memo entry would silently outlast it.
730
+ return { ok: false, error, retryable: credentialTransient };
731
+ }
732
+ }
733
+ /** Whether the caller's sign-in for the manual named `catalogName` is missing or broken — a check that must never throw here. */
734
+ async signInMissing(userId, catalogName) {
735
+ try {
736
+ return (await this.missingUserSecrets(userId, catalogName)).length > 0;
737
+ }
738
+ catch {
739
+ return false;
740
+ }
741
+ }
742
+ /**
743
+ * Create one pooled downstream connection: a client registering just this
744
+ * manual.
745
+ *
746
+ * `@utcp/mcp` (1.2.0+) registers its protocol as a FACTORY, so this client
747
+ * gets an `mcp` protocol instance of its own: the session it opens lives in
748
+ * that instance, is reachable from no other client, and ends with
749
+ * `client.close()`. That is what makes a pool entry per (user, server) an
750
+ * actual isolation boundary — two users with byte-identical server
751
+ * definitions still never share a connection — with nothing reached into.
752
+ *
753
+ * SESSION RECOVERY is installed on the pooled client: a downstream server
754
+ * that restarts (or expires a session) answers our next call with the spec's
755
+ * 404/`-32001`, and recovery re-registers in place and retries once. It is
756
+ * deliberately clear of {@link ManualFailureMemo}: that memo is a circuit
757
+ * breaker for manuals whose REGISTRATION failed, so a recovery neither
758
+ * consults it nor records into it.
759
+ *
760
+ * A registration that fails closes the client it built (ending whatever the
761
+ * attempt opened) and throws, so the pool caches nothing and the caller's
762
+ * memo records the failure.
763
+ */
764
+ async connectDownstream(userId, template) {
765
+ // Third-party manuals are never seeded loopback credentials (see
766
+ // `buildClient`), so a pooled client carries no request-scoped bearer.
767
+ const client = await CodeModeUtcpClient.create(process.cwd(), utcpClientConfig(userId, {}));
768
+ // Our own copy: registration renames the template in place, and recovery
769
+ // re-registers from exactly what discovery used.
770
+ const own = structuredClone(template);
771
+ const manualName = utcpManualName(own);
772
+ installSessionRecovery(client, { manualTemplate: (name) => (name === manualName ? own : undefined) });
773
+ const result = await registerManual(client, own);
774
+ if (!result.ok) {
775
+ await client.close().catch(() => { });
776
+ throw new Error(result.error);
777
+ }
778
+ return { client };
779
+ }
780
+ /**
781
+ * A pooled manual's operation failed: may it be retried with a refreshed
782
+ * token? Answers {@link RETRY_WITH_REFRESHED_TOKEN} when so, otherwise the
783
+ * error the caller should see.
784
+ *
785
+ * Only a token rejection (401 / `invalid_token`) qualifies. Then the
786
+ * caller's OAuth token for this manual is refreshed once, ignoring its
787
+ * stored expiry, and:
788
+ * - refreshed → the pooled connection (dialed with the dead token) is
789
+ * dropped and the operation retried once, on a fresh one;
790
+ * - rejected → the grant is gone and the vault has wiped it; the caller is
791
+ * told to re-authorize on /connect, where it now shows as not connected;
792
+ * - transient, or no refresh allowed / possible → the original error.
793
+ */
794
+ async afterDownstreamFailure(userId, manualName, poolKey, err) {
795
+ const verdict = await this.classifyDownstreamFailure(userId, manualName, poolKey, err);
796
+ return verdict.retry ? RETRY_WITH_REFRESHED_TOKEN : verdict.error;
797
+ }
798
+ /** {@link afterDownstreamFailure}, keeping the one distinction its symbol-or-error answer drops. */
799
+ async classifyDownstreamFailure(userId, manualName, poolKey, err) {
800
+ if (!isDownstreamTokenRejection(err))
801
+ return { retry: false, error: err, credentialTransient: false };
802
+ const outcome = await this.refreshDownstreamToken(userId, manualName);
803
+ if (outcome === 'refreshed') {
804
+ // The vault's mutation signal evicts this user's connections too; doing it
805
+ // here as well keeps the retry correct whether or not that is wired.
806
+ this.downstream.evictWhere((k) => k === poolKey);
807
+ return { retry: true };
808
+ }
809
+ if (outcome === 'rejected') {
810
+ // The connection was dialed with a grant that no longer exists; the
811
+ // vault's mutation signal drops it too, this keeps the next request
812
+ // honest whether or not that is wired.
813
+ this.downstream.evictWhere((k) => k === poolKey);
814
+ return {
815
+ retry: false,
816
+ credentialTransient: false,
817
+ error: new Error(`Your sign-in for "${manualName}" was rejected and has been disconnected. ` +
818
+ `Re-authorize it on ${this.opts.publicFrontendUrl}/connect, then run the tool again.`),
819
+ };
820
+ }
821
+ // The token stands and so does the call's own error. Whether the NEXT
822
+ // call may try again depends on why: a refresh that failed transiently, or
823
+ // one the window refused, will be worth attempting shortly — while
824
+ // "nothing here to refresh" (no vault, no OAuth variable, no stored token)
825
+ // is a standing condition, and the failure memo should hold the manual
826
+ // down exactly as it does for any other broken credential.
827
+ const mayRecoverSoon = outcome === 'transient' || outcome === 'guarded';
828
+ return { retry: false, error: err, credentialTransient: mayRecoverSoon };
829
+ }
830
+ /**
831
+ * Force-refresh the caller's OAuth token(s) for `manualName`, at most once per
832
+ * (user, manual) per minute. Logs ONE line per attempt — manual, user,
833
+ * outcome, never token material.
834
+ *
835
+ * `'guarded'` when the window refused this one (a refresh just ran, or is
836
+ * running, for this pair); `undefined` when there was nothing to refresh at
837
+ * all: no vault wired, or no OAuth variable on the manual. The two are not
838
+ * the same thing to the caller — see {@link classifyDownstreamFailure}.
839
+ */
840
+ async refreshDownstreamToken(userId, manualName) {
841
+ const vault = this.secretsVault;
842
+ if (!vault || !this.toolManuals || !manualName)
843
+ return undefined;
844
+ let keys;
845
+ try {
846
+ keys = (await this.toolManuals.userScopedKeysForManual(manualName)).filter((v) => v.oauth).map((v) => v.key);
847
+ }
848
+ catch (err) {
849
+ // The error's own message is caller-controlled as well (it quotes the
850
+ // manual, and may quote a provider's reply), and a raw Error handed to
851
+ // the logger renders its message and stack verbatim — so it goes through
852
+ // the same escaper rather than travelling as an object.
853
+ log.warn(`downstream token refresh: could not read the variables of manual=${printable(manualName)}: ` +
854
+ printable(err instanceof Error ? err.message : String(err)));
855
+ return undefined;
856
+ }
857
+ if (keys.length === 0)
858
+ return undefined;
859
+ const attempt = this.tokenRefreshes.run(`${userId}${POOL_KEY_SEPARATOR}${manualName}`, async () => {
860
+ const outcomes = [];
861
+ for (const key of keys) {
862
+ // A vault fault is not a verdict on the grant: keep the token, try later.
863
+ outcomes.push(await vault.forceRefresh(userId, key).catch(() => 'transient'));
864
+ }
865
+ const outcome = outcomes.includes('rejected')
866
+ ? 'rejected'
867
+ : outcomes.includes('transient')
868
+ ? 'transient'
869
+ : outcomes.includes('refreshed')
870
+ ? 'refreshed'
871
+ : 'skipped';
872
+ if (outcome !== 'skipped') {
873
+ // Both values reach the log from outside (a catalog name, an identity
874
+ // provider's id), so both are escaped: one event stays one line.
875
+ log.info(`downstream token refresh: manual=${printable(manualName)} user=${printable(userId)} outcome=${outcome}`);
876
+ }
877
+ return outcome;
878
+ });
879
+ return attempt ? await attempt : 'guarded';
880
+ }
420
881
  /**
421
882
  * If the tool declares per-user credentials the caller hasn't set, return a
422
883
  * needs-authorization result (naming the tool + a setup link); otherwise null
@@ -426,15 +887,15 @@ export class McpService {
426
887
  * services aren't wired, or the tool's manual declares no per-user credential
427
888
  * (e.g. the built-in KB tools).
428
889
  */
429
- async checkUserSecrets(userId, tool) {
430
- const missing = await this.missingUserSecrets(userId, tool);
890
+ async checkUserSecrets(userId, toolName, manualName) {
891
+ const missing = await this.missingUserSecrets(userId, manualName);
431
892
  if (missing.length === 0)
432
893
  return null;
433
894
  return {
434
- result: needsAuthorizationResult(tool.mcpName, missing.map((v) => v.label ?? v.name), `${this.opts.publicFrontendUrl}/connect`),
895
+ result: needsAuthorizationResult(toolName, missing.map((v) => v.label ?? v.name), `${this.opts.publicFrontendUrl}/connect`),
435
896
  // At least one missing item is a sign-in the caller already HAS a row
436
897
  // for (dead grant, abandoned consent, or missing scopes) — the state
437
- // that warrants resetting an interactive session for re-auth, as
898
+ // that warrants resetting an interactive client for re-auth, as
438
899
  // opposed to a tool the caller never set up at all.
439
900
  brokenSignIn: missing.some((v) => v.brokenSignIn),
440
901
  };
@@ -442,14 +903,14 @@ export class McpService {
442
903
  /**
443
904
  * The per-user variables of `tool`'s manual the caller has NOT satisfied.
444
905
  * Shared by the call-time needs-authorization check above and the
445
- * listing-time filter for connection-key sessions (which registers only
906
+ * listing-time filter for connection-key callers (which registers only
446
907
  * ready tools). Empty when the vault/manual services aren't wired or the
447
908
  * manual declares no per-user credential.
448
909
  */
449
- async missingUserSecrets(userId, tool) {
450
- if (!this.secretsVault || !this.toolManuals || !tool.manualName)
910
+ async missingUserSecrets(userId, manualName) {
911
+ if (!this.secretsVault || !this.toolManuals || !manualName)
451
912
  return [];
452
- const userVars = await this.toolManuals.userScopedKeysForManual(tool.manualName);
913
+ const userVars = await this.toolManuals.userScopedKeysForManual(manualName);
453
914
  if (userVars.length === 0)
454
915
  return [];
455
916
  const status = await this.secretsVault.statusFor(userId, userVars.map((v) => v.key));
@@ -464,6 +925,17 @@ export class McpService {
464
925
  missing.push({ name: v.name, label: v.label, brokenSignIn }); // no row at all → needs a value / sign-in
465
926
  continue;
466
927
  }
928
+ // A row of the OTHER kind is not this credential: a `.tool` edited from
929
+ // an OAuth sign-in to a plain key (or back) leaves the old row behind,
930
+ // and serving a token where a key is declared — or a key where a
931
+ // sign-in is — fails opaquely at the provider. Not a broken sign-in
932
+ // either: nothing to reset, the user has to set the credential up as
933
+ // the manual now declares it.
934
+ const declaredKind = v.oauth ? 'oauth' : 'static';
935
+ if (st.userKind && st.userKind !== declaredKind) {
936
+ missing.push({ name: v.name, label: v.label, brokenSignIn: false });
937
+ continue;
938
+ }
467
939
  // An OAuth-backed var whose row exists but has no token yet is NOT ready —
468
940
  // the user has registered but not completed sign-in. Fail closed: anything
469
941
  // other than a confirmed `true` (including a non-oauth row → undefined)
@@ -483,6 +955,14 @@ export class McpService {
483
955
  }
484
956
  return missing;
485
957
  }
958
+ /**
959
+ * Run one tool call through `callToolStreaming` with a one-chunk lookahead:
960
+ * every chunk except the last becomes a progress notification — sent on THIS
961
+ * request's own response stream — and the last is the result. Continuity is
962
+ * the caller's: a tool that supports it (e.g. `ask`) returns its `sessionId`
963
+ * in the result verbatim, and the caller echoes it back per the tool's own
964
+ * schema — the proxy never rewrites args.
965
+ */
486
966
  async dispatch(client, tool, request,
487
967
  // `sendNotification` typed loosely (`any`) so a `notifications/progress`
488
968
  // payload without a `progressToken` is accepted — same approach the prior
@@ -500,8 +980,8 @@ export class McpService {
500
980
  }
501
981
  /**
502
982
  * Handle a code-mode meta-tool. The shared implementation reflects on this
503
- * session's client, so `list_tools`/`tools_info` describe exactly the catalog
504
- * this session discovered and `call_tool_chain` runs in that client's
983
+ * request's client, so `list_tools`/`tools_info` describe exactly the catalog
984
+ * this request discovered and `call_tool_chain` runs in that client's
505
985
  * isolated-vm — resolving over loopback with the caller's key. The workspace
506
986
  * spill store is passed in so an oversized chain result comes back as a
507
987
  * `read_file`-able ref rather than a wall of JSON.
@@ -510,6 +990,123 @@ export class McpService {
510
990
  return dispatchMetaTool(client, name, args, this.opts.spillStore);
511
991
  }
512
992
  }
993
+ /** Whether a tool result is the tool saying it failed — what the Audit log records as `error`. */
994
+ function isErrorResult(result) {
995
+ return result.isError === true;
996
+ }
997
+ /**
998
+ * The UTCP client config for `userId`: the given variables, plus the per-user
999
+ * `bevel-secrets` loader that resolves every `${SECRET}` ref lazily. Shared by
1000
+ * request clients and pooled downstream clients, so both resolve a manual's
1001
+ * variables through exactly the same tiers.
1002
+ */
1003
+ function utcpClientConfig(userId, variables) {
1004
+ return new UtcpClientConfigSerializer().validateDict({
1005
+ variables,
1006
+ load_variables_from: [bevelSecretsLoaderConfig(userId)],
1007
+ });
1008
+ }
1009
+ /**
1010
+ * The name a manual is registered under. `UtcpClient.registerManual` rewrites
1011
+ * every non-word character to `_`, and tool names are prefixed with the result,
1012
+ * so routing has to key on the same rewrite.
1013
+ */
1014
+ function utcpManualName(template) {
1015
+ return String(template.name ?? '').replace(/[^\w]/g, '_');
1016
+ }
1017
+ /**
1018
+ * The pool key for (user, manual). The template's fingerprint is part of it, so
1019
+ * an edited `mcp.json` (new URL, new headers) gets a new connection rather than
1020
+ * the one dialed for the old definition, which simply idles out. Hashed: the
1021
+ * template can carry credentials and the key must stay log-safe.
1022
+ */
1023
+ function downstreamPoolKey(userId, template) {
1024
+ return [userId, utcpManualName(template), templateFingerprint(template)].join(POOL_KEY_SEPARATOR);
1025
+ }
1026
+ /** A short, log-safe hash of a manual's whole definition: changes whenever the definition does. */
1027
+ function templateFingerprint(template) {
1028
+ return createHash('sha256').update(JSON.stringify(template)).digest('hex').slice(0, 16);
1029
+ }
1030
+ /**
1031
+ * Route tool calls for pooled manuals to their pooled client, in place on the
1032
+ * request client. Both entry points are wrapped — `callToolStreaming` is the
1033
+ * MCP dispatch path, `callTool` is what `call_tool_chain` bridges every
1034
+ * in-isolate tool function to — so the two can never disagree. Every other
1035
+ * manual's calls go through the request client unchanged.
1036
+ *
1037
+ * Each routed call holds its pool lease until the call ends — for a stream,
1038
+ * until the consumer finishes or abandons it (`for await` returns the
1039
+ * generator, which runs the `finally`).
1040
+ *
1041
+ * A call the downstream refuses for its token (401 / `invalid_token`) goes to
1042
+ * the route's `afterFailure`, which may refresh the token and ask for ONE retry
1043
+ * on a fresh connection. Retrying is safe for the same reason session recovery
1044
+ * is: authentication is decided before the request reaches a tool, so the
1045
+ * refused attempt ran nothing.
1046
+ */
1047
+ function routeToDownstream(client, routes) {
1048
+ const callTool = client.callTool.bind(client);
1049
+ const callToolStreaming = client.callToolStreaming.bind(client);
1050
+ const routeOf = (toolName) => routes.get(toolName.split('.')[0] ?? '');
1051
+ client.callTool = async function routedCallTool(toolName, toolArgs) {
1052
+ const route = routeOf(toolName);
1053
+ if (!route)
1054
+ return callTool(toolName, toolArgs);
1055
+ const once = async () => {
1056
+ const lease = await route.acquire();
1057
+ try {
1058
+ return await lease.value.client.callTool(toolName, toolArgs);
1059
+ }
1060
+ finally {
1061
+ lease.release();
1062
+ }
1063
+ };
1064
+ try {
1065
+ return await once();
1066
+ }
1067
+ catch (err) {
1068
+ const next = await route.afterFailure(err);
1069
+ if (next !== RETRY_WITH_REFRESHED_TOKEN)
1070
+ throw next;
1071
+ // Exactly one retry: whatever it produces is what the caller sees.
1072
+ return await once();
1073
+ }
1074
+ };
1075
+ client.callToolStreaming = async function* routedCallToolStreaming(toolName, toolArgs) {
1076
+ const route = routeOf(toolName);
1077
+ if (!route) {
1078
+ yield* callToolStreaming(toolName, toolArgs);
1079
+ return;
1080
+ }
1081
+ const once = async function* () {
1082
+ const lease = await route.acquire();
1083
+ try {
1084
+ yield* lease.value.client.callToolStreaming(toolName, toolArgs);
1085
+ }
1086
+ finally {
1087
+ lease.release();
1088
+ }
1089
+ };
1090
+ let yielded = false;
1091
+ try {
1092
+ for await (const chunk of once()) {
1093
+ yielded = true;
1094
+ yield chunk;
1095
+ }
1096
+ return;
1097
+ }
1098
+ catch (err) {
1099
+ // A stream that already produced output was accepted — a rejection can
1100
+ // only come before the first chunk, and replaying would duplicate output.
1101
+ if (yielded)
1102
+ throw err;
1103
+ const next = await route.afterFailure(err);
1104
+ if (next !== RETRY_WITH_REFRESHED_TOKEN)
1105
+ throw next;
1106
+ }
1107
+ yield* once();
1108
+ };
1109
+ }
513
1110
  /**
514
1111
  * The pieces of the proxy that are shared with the local MCP server now live in
515
1112
  * `@bevel-software/platform-mcp-core`. Re-exported here because this module is