@bevel-software/platform-core-backend 0.33.0 → 0.35.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 (602) hide show
  1. package/agent-guide/access-control.md +146 -289
  2. package/agent-guide/conventions.md +23 -25
  3. package/agent-guide/directory-structure.md +72 -120
  4. package/agent-guide/drafts-and-change-requests.md +92 -0
  5. package/agent-guide/for-git-users.md +47 -0
  6. package/agent-guide/glossary.md +36 -0
  7. package/agent-guide/how-agents-find-things.md +49 -0
  8. package/agent-guide/html-views.md +239 -8
  9. package/agent-guide/new-knowledge-base.md +9 -8
  10. package/agent-guide/skills.md +67 -42
  11. package/agent-guide/start-here.md +65 -0
  12. package/agent-guide/tool-manuals.md +133 -155
  13. package/agent-guide/tools.md +67 -0
  14. package/agent-guide/where-a-new-file-goes.md +45 -28
  15. package/dist/core/catalog-revision.d.ts +12 -1
  16. package/dist/core/catalog-revision.d.ts.map +1 -1
  17. package/dist/core/catalog-revision.js +23 -3
  18. package/dist/core/catalog-revision.js.map +1 -1
  19. package/dist/core/core-ports.d.ts +41 -0
  20. package/dist/core/core-ports.d.ts.map +1 -1
  21. package/dist/core/core-ports.js.map +1 -1
  22. package/dist/core/create-core-server.d.ts.map +1 -1
  23. package/dist/core/create-core-server.js +15 -8
  24. package/dist/core/create-core-server.js.map +1 -1
  25. package/dist/core/create-core-services.d.ts +22 -5
  26. package/dist/core/create-core-services.d.ts.map +1 -1
  27. package/dist/core/create-core-services.js +44 -16
  28. package/dist/core/create-core-services.js.map +1 -1
  29. package/dist/core/lifecycle.d.ts +16 -0
  30. package/dist/core/lifecycle.d.ts.map +1 -1
  31. package/dist/core/lifecycle.js +12 -2
  32. package/dist/core/lifecycle.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/modules/access/access-control.interface.d.ts +39 -9
  37. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  38. package/dist/modules/access/access-control.service.d.ts +15 -3
  39. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.js +239 -75
  41. package/dist/modules/access/access-control.service.js.map +1 -1
  42. package/dist/modules/access/access-mutation.service.d.ts +22 -24
  43. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  44. package/dist/modules/access/access-mutation.service.js +48 -51
  45. package/dist/modules/access/access-mutation.service.js.map +1 -1
  46. package/dist/modules/access/access-view.d.ts.map +1 -1
  47. package/dist/modules/access/access-view.js +12 -11
  48. package/dist/modules/access/access-view.js.map +1 -1
  49. package/dist/modules/access/access.routes.d.ts +3 -0
  50. package/dist/modules/access/access.routes.d.ts.map +1 -1
  51. package/dist/modules/access/access.routes.js +58 -33
  52. package/dist/modules/access/access.routes.js.map +1 -1
  53. package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
  54. package/dist/modules/access/groups-admin.routes.js +4 -2
  55. package/dist/modules/access/groups-admin.routes.js.map +1 -1
  56. package/dist/modules/access/groups-admin.service.d.ts +44 -13
  57. package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
  58. package/dist/modules/access/groups-admin.service.js +80 -38
  59. package/dist/modules/access/groups-admin.service.js.map +1 -1
  60. package/dist/modules/access/groups-edit.d.ts +1 -1
  61. package/dist/modules/access/groups-edit.d.ts.map +1 -1
  62. package/dist/modules/access/groups-edit.js +1 -1
  63. package/dist/modules/access/groups-edit.js.map +1 -1
  64. package/dist/modules/access/index.d.ts +1 -1
  65. package/dist/modules/access/index.d.ts.map +1 -1
  66. package/dist/modules/access/reference-scan.d.ts +116 -24
  67. package/dist/modules/access/reference-scan.d.ts.map +1 -1
  68. package/dist/modules/access/reference-scan.js +305 -44
  69. package/dist/modules/access/reference-scan.js.map +1 -1
  70. package/dist/modules/access/roles-admin.service.d.ts +17 -8
  71. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  72. package/dist/modules/access/roles-admin.service.js +32 -23
  73. package/dist/modules/access/roles-admin.service.js.map +1 -1
  74. package/dist/modules/access-model/access-grammar.d.ts +35 -10
  75. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  76. package/dist/modules/access-model/access-grammar.js +60 -20
  77. package/dist/modules/access-model/access-grammar.js.map +1 -1
  78. package/dist/modules/access-model/access-splice.d.ts +22 -18
  79. package/dist/modules/access-model/access-splice.d.ts.map +1 -1
  80. package/dist/modules/access-model/access-splice.js +20 -14
  81. package/dist/modules/access-model/access-splice.js.map +1 -1
  82. package/dist/modules/access-model/group-files.d.ts +1 -0
  83. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  84. package/dist/modules/access-model/group-files.js +5 -1
  85. package/dist/modules/access-model/group-files.js.map +1 -1
  86. package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
  87. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  88. package/dist/modules/agent-guide/agent-guide.js +103 -13
  89. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  90. package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
  91. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
  92. package/dist/modules/agent-guide/agent-guide.tools.js +46 -22
  93. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
  94. package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
  95. package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
  96. package/dist/modules/agent-guide/guide-budget.js +67 -0
  97. package/dist/modules/agent-guide/guide-budget.js.map +1 -0
  98. package/dist/modules/agent-guide/index.d.ts +2 -1
  99. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  100. package/dist/modules/agent-guide/index.js +2 -1
  101. package/dist/modules/agent-guide/index.js.map +1 -1
  102. package/dist/modules/agent-instructions/compose.d.ts +3 -1
  103. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  104. package/dist/modules/agent-instructions/compose.js +7 -1
  105. package/dist/modules/agent-instructions/compose.js.map +1 -1
  106. package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
  107. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  108. package/dist/modules/agent-instructions/shared-file-rules.js +26 -9
  109. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  110. package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
  111. package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
  112. package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
  113. package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
  114. package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
  115. package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
  116. package/dist/modules/agent-scope/connection-scope.js +165 -0
  117. package/dist/modules/agent-scope/connection-scope.js.map +1 -0
  118. package/dist/modules/agent-scope/index.d.ts +3 -0
  119. package/dist/modules/agent-scope/index.d.ts.map +1 -0
  120. package/dist/modules/agent-scope/index.js +3 -0
  121. package/dist/modules/agent-scope/index.js.map +1 -0
  122. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  123. package/dist/modules/audit/agent-audit.service.js +2 -0
  124. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  125. package/dist/modules/audit/audit.contract.d.ts +6 -0
  126. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  127. package/dist/modules/audit/audit.contract.js.map +1 -1
  128. package/dist/modules/auth/account-admission.d.ts +12 -0
  129. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  130. package/dist/modules/auth/account-admission.js +15 -0
  131. package/dist/modules/auth/account-admission.js.map +1 -1
  132. package/dist/modules/auth/auth.middleware.d.ts +27 -1
  133. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  134. package/dist/modules/auth/auth.middleware.js +42 -4
  135. package/dist/modules/auth/auth.middleware.js.map +1 -1
  136. package/dist/modules/auth/auth.routes.d.ts +13 -9
  137. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  138. package/dist/modules/auth/auth.routes.js +35 -29
  139. package/dist/modules/auth/auth.routes.js.map +1 -1
  140. package/dist/modules/auth/auth.service.d.ts +55 -6
  141. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  142. package/dist/modules/auth/auth.service.js +128 -34
  143. package/dist/modules/auth/auth.service.js.map +1 -1
  144. package/dist/modules/auth/index.d.ts +1 -1
  145. package/dist/modules/auth/index.d.ts.map +1 -1
  146. package/dist/modules/auth/index.js +1 -1
  147. package/dist/modules/auth/index.js.map +1 -1
  148. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  149. package/dist/modules/auth/oidc-auth-provider.js +2 -9
  150. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  151. package/dist/modules/code-mode/code-mode.tool.js +2 -2
  152. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  153. package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
  154. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  155. package/dist/modules/connection-probe/connection-probe.service.js +1 -38
  156. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  157. package/dist/modules/database/core-schema.d.ts +68 -0
  158. package/dist/modules/database/core-schema.d.ts.map +1 -1
  159. package/dist/modules/database/core-schema.js +33 -0
  160. package/dist/modules/database/core-schema.js.map +1 -1
  161. package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
  162. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  163. package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
  164. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  165. package/dist/modules/embed/embed.tools.d.ts.map +1 -1
  166. package/dist/modules/embed/embed.tools.js +5 -0
  167. package/dist/modules/embed/embed.tools.js.map +1 -1
  168. package/dist/modules/kb-fs/index.d.ts +2 -0
  169. package/dist/modules/kb-fs/index.d.ts.map +1 -1
  170. package/dist/modules/kb-fs/index.js +1 -0
  171. package/dist/modules/kb-fs/index.js.map +1 -1
  172. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  173. package/dist/modules/kb-fs/locking-filesystem.js +7 -1
  174. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  175. package/dist/modules/kb-fs/repo-path.d.ts +26 -6
  176. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  177. package/dist/modules/kb-fs/repo-path.js +22 -7
  178. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  179. package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
  180. package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
  181. package/dist/modules/kb-fs/tmp-ref.js +76 -0
  182. package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
  183. package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
  184. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  185. package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
  186. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  187. package/dist/modules/mcp/mcp.routes.d.ts +7 -1
  188. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  189. package/dist/modules/mcp/mcp.routes.js +39 -4
  190. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  191. package/dist/modules/mcp/mcp.service.d.ts +11 -0
  192. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  193. package/dist/modules/mcp/mcp.service.js +74 -48
  194. package/dist/modules/mcp/mcp.service.js.map +1 -1
  195. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
  196. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  197. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
  198. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  199. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
  200. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  201. package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
  202. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  203. package/dist/modules/plugins/index.d.ts +1 -1
  204. package/dist/modules/plugins/index.d.ts.map +1 -1
  205. package/dist/modules/plugins/index.js.map +1 -1
  206. package/dist/modules/plugins/plugin-links.d.ts +18 -2
  207. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  208. package/dist/modules/plugins/plugin-links.js.map +1 -1
  209. package/dist/modules/plugins/plugins.routes.d.ts +5 -1
  210. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  211. package/dist/modules/plugins/plugins.routes.js +32 -5
  212. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  213. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
  214. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  215. package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
  216. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  217. package/dist/modules/secrets-vault/index.d.ts +1 -1
  218. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  219. package/dist/modules/secrets-vault/index.js +1 -1
  220. package/dist/modules/secrets-vault/index.js.map +1 -1
  221. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
  222. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  223. package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
  224. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  225. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
  226. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  227. package/dist/modules/secrets-vault/secrets-vault.routes.js +100 -42
  228. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  229. package/dist/modules/settings/oidc-check.d.ts.map +1 -1
  230. package/dist/modules/settings/oidc-check.js +5 -17
  231. package/dist/modules/settings/oidc-check.js.map +1 -1
  232. package/dist/modules/skills/index.d.ts +3 -3
  233. package/dist/modules/skills/index.d.ts.map +1 -1
  234. package/dist/modules/skills/index.js +0 -2
  235. package/dist/modules/skills/index.js.map +1 -1
  236. package/dist/modules/skills/skills.contract.d.ts +16 -0
  237. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  238. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  239. package/dist/modules/skills/skills.service.js +6 -1
  240. package/dist/modules/skills/skills.service.js.map +1 -1
  241. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  242. package/dist/modules/skills/skills.tools.js +25 -13
  243. package/dist/modules/skills/skills.tools.js.map +1 -1
  244. package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
  245. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  246. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  247. package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
  248. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  249. package/dist/modules/tool-auth/external-api-key.service.js +4 -1
  250. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  251. package/dist/modules/tool-auth/index.d.ts +1 -1
  252. package/dist/modules/tool-auth/index.d.ts.map +1 -1
  253. package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
  254. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  255. package/dist/modules/tool-auth/internal-token.service.js +1 -0
  256. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  257. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
  258. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  259. package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
  260. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  261. package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
  262. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  263. package/dist/modules/tool-helpers/tool-context.js +3 -0
  264. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  265. package/dist/modules/tool-helpers/tool-def.js +1 -1
  266. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  267. package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
  268. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  269. package/dist/modules/tool-helpers/tool-handler.js +19 -0
  270. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  271. package/dist/modules/tool-helpers/tool.contract.d.ts +10 -0
  272. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  273. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  274. package/dist/modules/tool-helpers/validate-token.d.ts +3 -1
  275. package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
  276. package/dist/modules/tool-helpers/validate-token.js +1 -1
  277. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  278. package/dist/modules/tool-manuals/index.d.ts +3 -3
  279. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  280. package/dist/modules/tool-manuals/index.js +0 -2
  281. package/dist/modules/tool-manuals/index.js.map +1 -1
  282. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
  283. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  284. package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
  285. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  286. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +46 -6
  287. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  288. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
  289. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  290. package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
  291. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  292. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +24 -13
  293. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  294. package/dist/modules/tool-manuals/tool-manuals.service.js +138 -59
  295. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  296. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
  297. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  298. package/dist/modules/tool-manuals/tool-manuals.tools.js +56 -20
  299. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  300. package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
  301. package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
  302. package/dist/modules/tool-registry/manual.routes.js +27 -7
  303. package/dist/modules/tool-registry/manual.routes.js.map +1 -1
  304. package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
  305. package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
  306. package/dist/modules/tool-registry/tool.contract.js.map +1 -1
  307. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  308. package/dist/modules/workflow/agent-tools/workflow.tools.js +36 -27
  309. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  310. package/dist/modules/workflow/git/git.service.d.ts +2 -2
  311. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  312. package/dist/modules/workflow/git/git.service.js +20 -35
  313. package/dist/modules/workflow/git/git.service.js.map +1 -1
  314. package/dist/modules/workflow/workflow.service.d.ts +2 -1
  315. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  316. package/dist/modules/workflow/workflow.service.js +3 -0
  317. package/dist/modules/workflow/workflow.service.js.map +1 -1
  318. package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
  319. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
  320. package/dist/modules/workspace/agent-access.gate.js +1 -1
  321. package/dist/modules/workspace/agent-access.gate.js.map +1 -1
  322. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
  323. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  324. package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
  325. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  326. package/dist/modules/workspace/index.d.ts +8 -0
  327. package/dist/modules/workspace/index.d.ts.map +1 -1
  328. package/dist/modules/workspace/index.js +8 -0
  329. package/dist/modules/workspace/index.js.map +1 -1
  330. package/dist/modules/workspace/nul-scan.d.ts +19 -0
  331. package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
  332. package/dist/modules/workspace/nul-scan.js +55 -0
  333. package/dist/modules/workspace/nul-scan.js.map +1 -0
  334. package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
  335. package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
  336. package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
  337. package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
  338. package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
  339. package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
  340. package/dist/modules/workspace/ripgrep-search.js +657 -0
  341. package/dist/modules/workspace/ripgrep-search.js.map +1 -0
  342. package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
  343. package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
  344. package/dist/modules/workspace/ripgrep-version.js +31 -0
  345. package/dist/modules/workspace/ripgrep-version.js.map +1 -0
  346. package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
  347. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
  348. package/dist/modules/workspace/startup/beside-checkout.js +14 -5
  349. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
  350. package/dist/modules/workspace/worker-pool.d.ts +3 -4
  351. package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
  352. package/dist/modules/workspace/worker-pool.js.map +1 -1
  353. package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
  354. package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
  355. package/dist/modules/workspace/workspace-tmp.js +435 -0
  356. package/dist/modules/workspace/workspace-tmp.js.map +1 -0
  357. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  358. package/dist/modules/workspace/workspace.routes.js +5 -26
  359. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  360. package/dist/modules/workspace/workspace.service.d.ts +47 -6
  361. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  362. package/dist/modules/workspace/workspace.service.js +86 -6
  363. package/dist/modules/workspace/workspace.service.js.map +1 -1
  364. package/dist/modules/workspace/workspace.tools.d.ts +3 -2
  365. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  366. package/dist/modules/workspace/workspace.tools.js +531 -137
  367. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  368. package/dist/shared/oauth-token-error.d.ts +23 -0
  369. package/dist/shared/oauth-token-error.d.ts.map +1 -0
  370. package/dist/shared/oauth-token-error.js +47 -0
  371. package/dist/shared/oauth-token-error.js.map +1 -0
  372. package/dist/shared/path-order.d.ts +28 -8
  373. package/dist/shared/path-order.d.ts.map +1 -1
  374. package/dist/shared/path-order.js +37 -8
  375. package/dist/shared/path-order.js.map +1 -1
  376. package/dist/shared/read-capped.d.ts +19 -0
  377. package/dist/shared/read-capped.d.ts.map +1 -0
  378. package/dist/shared/read-capped.js +45 -0
  379. package/dist/shared/read-capped.js.map +1 -0
  380. package/migrations/0022_session_version.sql +6 -0
  381. package/migrations/0023_agent_path.sql +3 -0
  382. package/migrations/meta/0022_snapshot.json +2360 -0
  383. package/migrations/meta/0023_snapshot.json +2378 -0
  384. package/migrations/meta/_journal.json +14 -0
  385. package/package.json +4 -3
  386. package/src/__tests__/hexis-catalog.ts +144 -0
  387. package/src/__tests__/pg-scratch.test.ts +69 -0
  388. package/src/__tests__/pg-scratch.ts +31 -0
  389. package/src/__tests__/real-disk.ts +21 -0
  390. package/src/__tests__/slow-ripgrep.ts +65 -0
  391. package/src/__tests__/tokens.ts +19 -0
  392. package/src/__tests__/tool-manuals.ts +32 -0
  393. package/src/__tests__/tool-wiring.ts +25 -0
  394. package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
  395. package/src/core/__tests__/catalog-revision.test.ts +89 -0
  396. package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
  397. package/src/core/__tests__/gated-boot.test.ts +8 -1
  398. package/src/core/__tests__/lifecycle.test.ts +46 -13
  399. package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
  400. package/src/core/catalog-revision.ts +24 -2
  401. package/src/core/core-ports.ts +42 -0
  402. package/src/core/create-core-server.ts +14 -1
  403. package/src/core/create-core-services.ts +62 -15
  404. package/src/core/lifecycle.ts +26 -2
  405. package/src/index.ts +1 -1
  406. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
  407. package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
  408. package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
  409. package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
  410. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
  411. package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
  412. package/src/modules/access/__tests__/access-groups.test.ts +55 -22
  413. package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
  414. package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
  415. package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
  416. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
  417. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
  418. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
  419. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  420. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
  421. package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -10
  422. package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
  423. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
  424. package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
  425. package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
  426. package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
  427. package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
  428. package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
  429. package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
  430. package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
  431. package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
  432. package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
  433. package/src/modules/access/access-control.interface.ts +43 -10
  434. package/src/modules/access/access-control.service.ts +250 -72
  435. package/src/modules/access/access-mutation.service.ts +48 -53
  436. package/src/modules/access/access-view.ts +12 -10
  437. package/src/modules/access/access.routes.ts +66 -37
  438. package/src/modules/access/groups-admin.routes.ts +4 -2
  439. package/src/modules/access/groups-admin.service.ts +114 -44
  440. package/src/modules/access/groups-edit.ts +1 -1
  441. package/src/modules/access/index.ts +1 -1
  442. package/src/modules/access/reference-scan.ts +319 -46
  443. package/src/modules/access/roles-admin.service.ts +43 -26
  444. package/src/modules/access/synced-groups-writer.ts +1 -1
  445. package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
  446. package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
  447. package/src/modules/access-model/access-grammar.ts +65 -20
  448. package/src/modules/access-model/access-splice.ts +31 -23
  449. package/src/modules/access-model/group-files.ts +5 -0
  450. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +110 -25
  451. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
  452. package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
  453. package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
  454. package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
  455. package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
  456. package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
  457. package/src/modules/agent-guide/agent-guide.tools.ts +51 -22
  458. package/src/modules/agent-guide/agent-guide.ts +120 -13
  459. package/src/modules/agent-guide/guide-budget.ts +73 -0
  460. package/src/modules/agent-guide/index.ts +12 -0
  461. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +5 -4
  462. package/src/modules/agent-instructions/compose.ts +7 -1
  463. package/src/modules/agent-instructions/shared-file-rules.ts +27 -9
  464. package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
  465. package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
  466. package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
  467. package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
  468. package/src/modules/agent-scope/connection-scope.ts +199 -0
  469. package/src/modules/agent-scope/index.ts +15 -0
  470. package/src/modules/audit/agent-audit.service.ts +2 -0
  471. package/src/modules/audit/audit.contract.ts +6 -0
  472. package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
  473. package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
  474. package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
  475. package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
  476. package/src/modules/auth/account-admission.ts +19 -0
  477. package/src/modules/auth/auth.middleware.ts +45 -4
  478. package/src/modules/auth/auth.routes.ts +46 -29
  479. package/src/modules/auth/auth.service.ts +135 -34
  480. package/src/modules/auth/index.ts +2 -0
  481. package/src/modules/auth/oidc-auth-provider.ts +2 -9
  482. package/src/modules/code-mode/code-mode.tool.ts +2 -2
  483. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
  484. package/src/modules/connection-probe/connection-probe.service.ts +1 -34
  485. package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
  486. package/src/modules/database/core-schema.ts +33 -0
  487. package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
  488. package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
  489. package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
  490. package/src/modules/embed/embed.tools.ts +5 -0
  491. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
  492. package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
  493. package/src/modules/kb-fs/index.ts +2 -0
  494. package/src/modules/kb-fs/locking-filesystem.ts +7 -1
  495. package/src/modules/kb-fs/repo-path.ts +33 -7
  496. package/src/modules/kb-fs/tmp-ref.ts +81 -0
  497. package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
  498. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
  499. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
  500. package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
  501. package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
  502. package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
  503. package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
  504. package/src/modules/mcp/mcp.routes.ts +39 -3
  505. package/src/modules/mcp/mcp.service.ts +112 -52
  506. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
  507. package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
  508. package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
  509. package/src/modules/plugins/index.ts +1 -1
  510. package/src/modules/plugins/plugin-links.ts +19 -2
  511. package/src/modules/plugins/plugins.routes.ts +35 -4
  512. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
  513. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
  514. package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
  515. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
  516. package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
  517. package/src/modules/secrets-vault/index.ts +1 -1
  518. package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
  519. package/src/modules/secrets-vault/secrets-vault.routes.ts +114 -42
  520. package/src/modules/settings/oidc-check.ts +5 -18
  521. package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
  522. package/src/modules/skills/index.ts +7 -2
  523. package/src/modules/skills/skills.contract.ts +17 -0
  524. package/src/modules/skills/skills.service.ts +5 -1
  525. package/src/modules/skills/skills.tools.ts +45 -14
  526. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
  527. package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
  528. package/src/modules/tool-auth/external-api-key.service.ts +5 -2
  529. package/src/modules/tool-auth/index.ts +1 -1
  530. package/src/modules/tool-auth/internal-token.service.ts +10 -0
  531. package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
  532. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
  533. package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
  534. package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
  535. package/src/modules/tool-helpers/tool-context.ts +10 -0
  536. package/src/modules/tool-helpers/tool-def.ts +1 -1
  537. package/src/modules/tool-helpers/tool-handler.ts +34 -0
  538. package/src/modules/tool-helpers/tool.contract.ts +10 -0
  539. package/src/modules/tool-helpers/validate-token.ts +4 -2
  540. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
  541. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
  542. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
  543. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +99 -8
  544. package/src/modules/tool-manuals/index.ts +6 -2
  545. package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
  546. package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -6
  547. package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
  548. package/src/modules/tool-manuals/tool-manuals.service.ts +169 -61
  549. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -25
  550. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
  551. package/src/modules/tool-registry/manual.routes.ts +26 -5
  552. package/src/modules/tool-registry/tool.contract.ts +7 -0
  553. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
  554. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
  555. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
  556. package/src/modules/workflow/agent-tools/workflow.tools.ts +36 -27
  557. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
  558. package/src/modules/workflow/git/git.service.ts +29 -48
  559. package/src/modules/workflow/workflow.service.ts +5 -0
  560. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
  561. package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
  562. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
  563. package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
  564. package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
  565. package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
  566. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
  567. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
  568. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -12
  569. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
  570. package/src/modules/workspace/__tests__/workspace.tools.test.ts +549 -162
  571. package/src/modules/workspace/agent-access.gate.ts +1 -1
  572. package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
  573. package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
  574. package/src/modules/workspace/index.ts +20 -0
  575. package/src/modules/workspace/nul-scan.ts +50 -0
  576. package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
  577. package/src/modules/workspace/ripgrep-search.ts +715 -0
  578. package/src/modules/workspace/ripgrep-version.ts +35 -0
  579. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
  580. package/src/modules/workspace/startup/beside-checkout.ts +13 -5
  581. package/src/modules/workspace/worker-pool.ts +3 -4
  582. package/src/modules/workspace/workspace-tmp.ts +457 -0
  583. package/src/modules/workspace/workspace.routes.ts +6 -27
  584. package/src/modules/workspace/workspace.service.ts +92 -15
  585. package/src/modules/workspace/workspace.tools.ts +574 -176
  586. package/src/shared/__tests__/read-capped.test.ts +68 -0
  587. package/src/shared/oauth-token-error.ts +46 -0
  588. package/src/shared/path-order.ts +37 -8
  589. package/src/shared/read-capped.ts +40 -0
  590. package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
  591. package/starter-packs/general/Plugins/general-starter/access.md +1 -1
  592. package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
  593. package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
  594. package/starter-packs/support/Plugins/support-starter/access.md +1 -1
  595. package/agent-guide/finding-things.md +0 -7
  596. package/agent-guide/introduction.md +0 -27
  597. package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
  598. package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
  599. package/dist/modules/workspace/bounded-matcher.js +0 -200
  600. package/dist/modules/workspace/bounded-matcher.js.map +0 -1
  601. package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
  602. package/src/modules/workspace/bounded-matcher.ts +0 -271
@@ -0,0 +1,146 @@
1
+ import express from 'express';
2
+ import type { AddressInfo } from 'node:net';
3
+ import type { Server } from 'node:http';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+ import { createOAuthConsentRoutes, NOT_AN_AGENT_CHOICE } from '../oauth/oauth-consent.routes.js';
6
+ import { signAuthRequest } from '../oauth/oauth-state.js';
7
+ import { agentFile, agentScopeOver, inMemoryKb } from '../../agent-scope/__tests__/in-memory-kb.js';
8
+
9
+ /**
10
+ * The consent step's "Connect as" choice, through the two routes the
11
+ * /connect page calls: the request description offers the agents the person
12
+ * may read, and Finish hands the provider exactly the choice made — null for
13
+ * Everything — or refuses one that is not on offer, before any code is minted.
14
+ * The choice offered and checked is the REAL agent-scope service's, over a
15
+ * knowledge base held in memory.
16
+ */
17
+
18
+ const SECRET = 'state-secret';
19
+ const ADA = 'ada@example.com';
20
+
21
+ let http: Server | null = null;
22
+ afterEach(async () => {
23
+ if (http) await new Promise<void>((r) => http!.close(() => r()));
24
+ http = null;
25
+ });
26
+
27
+ const kb = inMemoryKb({
28
+ 'Agents/mechanic.agent': agentFile('name: delivery-mechanic\ndescription: Moves code around.'),
29
+ 'Agents/secret.agent': agentFile('name: secret'),
30
+ });
31
+ kb.hidden.add('Agents/secret.agent');
32
+
33
+ async function mount(opts: { email?: string; agents?: Parameters<typeof createOAuthConsentRoutes>[0]['agents'] } = {}) {
34
+ const issueAuthCode = vi.fn(async () => ({ redirectTo: 'https://agent.example.com/cb?code=c1' }));
35
+ const completeConsent = vi.fn(async () => ({ redirectTo: 'https://claude.ai/cb?code=gh' }));
36
+ const app = express();
37
+ app.use(express.json());
38
+ app.use((req, _res, next) => {
39
+ req.userId = 'u1';
40
+ req.userEmail = opts.email ?? ADA;
41
+ next();
42
+ });
43
+ app.use(
44
+ createOAuthConsentRoutes({
45
+ provider: {
46
+ issueAuthCode,
47
+ clientsStore: { getClient: async () => ({ client_name: 'Claude' }) },
48
+ } as never,
49
+ stateSecret: SECRET,
50
+ facade: {
51
+ isFacadeRequest: () => true,
52
+ clientNameFor: () => 'Claude',
53
+ completeConsent,
54
+ },
55
+ agents: opts.agents ?? agentScopeOver(kb),
56
+ }),
57
+ );
58
+ http = app.listen(0);
59
+ await new Promise<void>((r) => http!.once('listening', r));
60
+ const base = `http://127.0.0.1:${(http.address() as AddressInfo).port}`;
61
+ return { base, issueAuthCode, completeConsent };
62
+ }
63
+
64
+ const state = (extra: Record<string, unknown> = {}) =>
65
+ signAuthRequest(SECRET, { c: 'client-1', r: 'https://agent.example.com/cb', cc: 'challenge', ...extra });
66
+
67
+ async function finish(base: string, body: Record<string, unknown>) {
68
+ return fetch(`${base}/mcp/oauth/complete`, {
69
+ method: 'POST',
70
+ headers: { 'content-type': 'application/json' },
71
+ body: JSON.stringify(body),
72
+ });
73
+ }
74
+
75
+ describe('the consent step offers "Connect as"', () => {
76
+ it('lists the agents the person may read, by name with description and path — Everything is the page’s own', async () => {
77
+ const { base } = await mount();
78
+ const res = await fetch(`${base}/mcp/oauth/request?state=${encodeURIComponent(state())}`);
79
+ expect(await res.json()).toEqual({
80
+ clientName: 'Claude',
81
+ scope: null,
82
+ resource: null,
83
+ agents: [{ path: 'Agents/mechanic.agent', name: 'delivery-mechanic', description: 'Moves code around.' }],
84
+ });
85
+ });
86
+
87
+ it('says so when the agents cannot be listed, rather than passing it off as having none', async () => {
88
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
89
+ const failing = {
90
+ listChoices: async () => {
91
+ throw new Error('checkout unavailable');
92
+ },
93
+ choosable: async () => null,
94
+ };
95
+ const { base } = await mount({ agents: failing });
96
+ const res = await fetch(`${base}/mcp/oauth/request?state=${encodeURIComponent(state())}`);
97
+ expect(await res.json()).toEqual({
98
+ clientName: 'Claude',
99
+ scope: null,
100
+ resource: null,
101
+ agents: [],
102
+ agentsUnavailable: true,
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('Finish records the choice', () => {
108
+ it('Everything — no choice, null or empty — reaches the provider as null, exactly as before', async () => {
109
+ const { base, issueAuthCode } = await mount();
110
+ for (const body of [{}, { agentPath: null }, { agentPath: '' }]) {
111
+ const st = state();
112
+ const res = await finish(base, { state: st, ...body });
113
+ expect(res.status).toBe(200);
114
+ expect(await res.json()).toEqual({ redirectTo: 'https://agent.example.com/cb?code=c1' });
115
+ }
116
+ expect(issueAuthCode).toHaveBeenCalledTimes(3);
117
+ for (const call of issueAuthCode.mock.calls as unknown as unknown[][]) expect(call[2]).toBeNull();
118
+ });
119
+
120
+ it('an agent on offer reaches the provider as its normalized path', async () => {
121
+ const { base, issueAuthCode } = await mount();
122
+ const res = await finish(base, { state: state(), agentPath: '/Agents/mechanic.agent' });
123
+ expect(res.status).toBe(200);
124
+ expect(issueAuthCode).toHaveBeenCalledWith('u1', expect.objectContaining({ c: 'client-1' }), 'Agents/mechanic.agent');
125
+ });
126
+
127
+ it.each([
128
+ ['a file the person may not read', 'Agents/secret.agent'],
129
+ ['a file that does not exist', 'Agents/none.agent'],
130
+ ['a path that is not an agent', 'notes.md'],
131
+ ['a value that is not a path', 42],
132
+ ])('refuses %s with one answer, minting nothing', async (_what, agentPath) => {
133
+ const { base, issueAuthCode } = await mount();
134
+ const res = await finish(base, { state: state(), agentPath });
135
+ expect(res.status).toBe(400);
136
+ expect(await res.json()).toEqual({ error: NOT_AN_AGENT_CHOICE });
137
+ expect(issueAuthCode).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it('refuses a choice on an account linked through the GitHub facade, which has no connection to record it on', async () => {
141
+ const { base, completeConsent } = await mount();
142
+ const res = await finish(base, { state: state({ gh: true, cc: undefined }), agentPath: 'Agents/mechanic.agent' });
143
+ expect(res.status).toBe(400);
144
+ expect(completeConsent).not.toHaveBeenCalled();
145
+ });
146
+ });
@@ -24,6 +24,13 @@ declare global {
24
24
  * the page its call comes from (`X-Bevel-Page`).
25
25
  */
26
26
  mcpCallerKind?: McpCallerKind;
27
+ /**
28
+ * The agent configuration the MCP caller's credential connects as — a key's
29
+ * `api_tokens.agent_path`, an agent connection's
30
+ * `agent_connections.agent_path`, or the one an exchanged grant
31
+ * carries. Unset is Everything.
32
+ */
33
+ mcpAgentPath?: string;
27
34
  }
28
35
  }
29
36
  }
@@ -110,6 +117,7 @@ export function createMcpAuthMiddleware(
110
117
  req.userId = resolved.user.id;
111
118
  req.userEmail = resolved.user.email;
112
119
  req.externalApiKeyId = resolved.tokenId;
120
+ if (resolved.agentPath) req.mcpAgentPath = resolved.agentPath;
113
121
  req.mcpCallerKind = 'key';
114
122
  next();
115
123
  return;
@@ -137,6 +145,9 @@ export function createMcpAuthMiddleware(
137
145
  if (typeof info.extra?.connectionId === 'string') {
138
146
  req.agentConnectionId = info.extra.connectionId;
139
147
  }
148
+ if (typeof info.extra?.agentPath === 'string' && info.extra.agentPath) {
149
+ req.mcpAgentPath = info.extra.agentPath;
150
+ }
140
151
  if (!req.userId) {
141
152
  unauthorized(res, 'Invalid access token');
142
153
  return;
@@ -217,10 +228,13 @@ export function createMcpAuthMiddleware(
217
228
  // stamped too (throttled, off the request's path), so the Audit log's
218
229
  // "last used" follows the local server's calls, not only its sign-ins.
219
230
  // Carried on the request so those calls are logged under the agent.
231
+ // The same read answers what the connection is made as NOW: a
232
+ // re-consent changes the row, not the token, so the row's choice is
233
+ // the one served, never the one the token was minted with.
220
234
  if (claim.connectionId) {
221
- let live: boolean;
235
+ let live: { agentPath: string | null } | null;
222
236
  try {
223
- live = await oauthProvider.isConnectionLive(claim.connectionId);
237
+ live = await oauthProvider.liveConnectionChoice(claim.connectionId);
224
238
  } catch (err) {
225
239
  log.error('agent-connection check failed:', { err });
226
240
  res.status(500).json({ error: 'Authentication backend unavailable' });
@@ -232,6 +246,9 @@ export function createMcpAuthMiddleware(
232
246
  }
233
247
  oauthProvider.noteConnectionUse(claim.connectionId);
234
248
  req.agentConnectionId = claim.connectionId;
249
+ if (live.agentPath) req.mcpAgentPath = live.agentPath;
250
+ } else if (claim.agentPath) {
251
+ req.mcpAgentPath = claim.agentPath;
235
252
  }
236
253
  req.mcpCallerKind = 'internal';
237
254
  next();
@@ -16,6 +16,8 @@ import type { BevelOAuthProvider } from './oauth/bevel-oauth-provider.js';
16
16
  import '../tool-auth/index.js'; // req.externalApiKeyId augmentation
17
17
  import './mcp-auth.middleware.js'; // req.mcpCallerKind augmentation
18
18
  import { isPagePath } from '../audit/index.js';
19
+ import type { IAgentScopeResolver } from '../agent-scope/index.js';
20
+ import { NOT_AN_AGENT_CHOICE } from './oauth/oauth-consent.routes.js';
19
21
 
20
22
  /** Pull the raw bearer token off an already-authenticated request. */
21
23
  function extractBearer(req: Request): string {
@@ -104,6 +106,11 @@ export function createMcpRoutes(
104
106
  // RFC 9728 pointer carried on this router's own 401 challenges (the
105
107
  // local-token exchange), same value McpAuthMiddleware advertises.
106
108
  resourceMetadataUrl: string,
109
+ /**
110
+ * The "Connect as" choices a key may be minted as (`modules/agent-scope`).
111
+ * Absent: keys are minted as Everything only, and no choices are listed.
112
+ */
113
+ agents?: Pick<IAgentScopeResolver, 'listChoices' | 'choosable'>,
107
114
  ): express.Router {
108
115
  const router = express.Router();
109
116
 
@@ -153,6 +160,10 @@ export function createMcpRoutes(
153
160
  // exchanged grant; undefined for keys/JWTs) — the Audit log's other
154
161
  // principal, resolved per request like the key id.
155
162
  connectionId: req.agentConnectionId ?? null,
163
+ // The agent configuration the credential connects as (a key's, an agent
164
+ // connection's, or the one an exchanged grant carries); null is
165
+ // Everything. Resolved per request like the ids above.
166
+ ...(req.mcpAgentPath ? { agentPath: req.mcpAgentPath } : {}),
156
167
  // The proxy authenticates its loopback calls with the SAME bearer the
157
168
  // client used here, so it acts on the request exactly as the caller would.
158
169
  bearer: extractBearer(req),
@@ -233,6 +244,17 @@ export function createMcpRoutes(
233
244
 
234
245
  // ── Connection-key management (JWT-only) ───────────────────────────────
235
246
 
247
+ // The agents a key may be minted as — the same choices the OAuth consent
248
+ // step offers below Everything, for the key-mint dialog.
249
+ router.get('/mcp/agent-choices', jwtAuthMiddleware, async (req, res) => {
250
+ try {
251
+ res.json({ agents: agents && req.userEmail ? await agents.listChoices(req.userEmail) : [] });
252
+ } catch (err) {
253
+ log.error('listing the agents to connect as failed:', { err });
254
+ res.status(500).json({ error: 'Failed to list the agents' });
255
+ }
256
+ });
257
+
236
258
  router.get('/mcp/external-api-keys', jwtAuthMiddleware, async (req, res) => {
237
259
  try {
238
260
  const tokens = await externalApiKeyService.listForUser(req.userId!);
@@ -264,12 +286,23 @@ export function createMcpRoutes(
264
286
  res.status(400).json({ error: 'JSON body required' });
265
287
  return;
266
288
  }
267
- const { label } = req.body as { label?: string };
289
+ const { label, agentPath: asked } = req.body as { label?: string; agentPath?: unknown };
268
290
  if (!label) {
269
291
  res.status(400).json({ error: 'label is required' });
270
292
  return;
271
293
  }
272
- const minted = await externalApiKeyService.mint(req.userId!, label);
294
+ // "Connect as", exactly as on the consent step: absent, null or empty is
295
+ // Everything; anything else must be an agent the owner may choose now.
296
+ let agentPath: string | null = null;
297
+ if (asked !== undefined && asked !== null && asked !== '') {
298
+ agentPath =
299
+ typeof asked === 'string' && agents && req.userEmail ? await agents.choosable(req.userEmail, asked) : null;
300
+ if (agentPath === null) {
301
+ res.status(400).json({ error: NOT_AN_AGENT_CHOICE });
302
+ return;
303
+ }
304
+ }
305
+ const minted = await externalApiKeyService.mint(req.userId!, label, agentPath ? { agentPath } : {});
273
306
  // The plaintext field is the *only* read path for the raw key. The
274
307
  // frontend must store it nowhere — it shows the dialog once and then
275
308
  // discards. Subsequent fetches return only `summary`-shaped rows.
@@ -428,8 +461,11 @@ export function createMcpRoutes(
428
461
  // The grant's agent connection rides along, so the local server's
429
462
  // calls with this token stay attributed to the agent in the Audit log.
430
463
  const connectionId = typeof info.extra?.connectionId === 'string' ? info.extra.connectionId : undefined;
464
+ // So does the agent the connection was made as: the local server's
465
+ // calls see the same narrowed surface as the hosted endpoint's.
466
+ const agentPath = typeof info.extra?.agentPath === 'string' && info.extra.agentPath ? info.extra.agentPath : undefined;
431
467
  const minted = internalTokens.mint(
432
- { userId, externalProxy: true, ...(connectionId ? { connectionId } : {}) },
468
+ { userId, externalProxy: true, ...(connectionId ? { connectionId } : {}), ...(agentPath ? { agentPath } : {}) },
433
469
  ttlMs,
434
470
  );
435
471
  // The ACTUAL lifetime, not the constant — the caller schedules its
@@ -50,7 +50,7 @@ import {
50
50
  type LoadedSkill,
51
51
  } from '@bevel-software/platform-mcp-core';
52
52
  import type { KbLayout } from '@bevel-software/platform-shared';
53
- import { bevelSecretsLoaderConfig, type ForcedRefreshOutcome, type ISecretsVaultService, scopesCovered } from '../secrets-vault/index.js';
53
+ import { bevelSecretsLoaderConfig, type ForcedRefreshOutcome, type ISecretsVaultService, userSecretGap } from '../secrets-vault/index.js';
54
54
  import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/index.js';
55
55
  import type { IToolManualService } from '../tool-manuals/index.js';
56
56
  import type { SpillStore } from '../workspace/index.js';
@@ -75,6 +75,16 @@ import {
75
75
  type ComposedAgentInstructions,
76
76
  } from '../agent-instructions/index.js';
77
77
  import { guideFirstDescription } from '../tool-registry/index.js';
78
+ import {
79
+ CONNECTION_META_KEY,
80
+ connectionRefusal,
81
+ connectionWireOf,
82
+ describeConnection,
83
+ type ConnectionScopeWire,
84
+ } from '@bevel-software/platform-mcp-core';
85
+
86
+ // Still offered from here, as this module's tests and callers learnt it.
87
+ export { CONNECTION_META_KEY };
78
88
 
79
89
  /**
80
90
  * Configuration for the loopback proxy. `loopbackBaseUrl` is the backend's own
@@ -142,6 +152,14 @@ export interface McpCaller {
142
152
  * attributed in the Audit log; a request with neither records nothing.
143
153
  */
144
154
  connectionId?: string | null;
155
+ /**
156
+ * The agent configuration the caller's credential connects as (a connection
157
+ * key's, or an agent connection's), or null for Everything. It rides the
158
+ * request's loopback identity, so every agent surface behind the loopback
159
+ * narrows to that agent's scope (`modules/agent-scope`); here it only keys
160
+ * the surface and words the handshake.
161
+ */
162
+ agentPath?: string | null;
145
163
  /** The raw bearer the request carried. */
146
164
  bearer: string;
147
165
  /**
@@ -222,6 +240,24 @@ interface RequestSurface {
222
240
  * build, at the latest after an hour unused.
223
241
  */
224
242
  degraded: boolean;
243
+ /**
244
+ * The agent the catalog was narrowed to, as `all-tools` reported it —
245
+ * absent for Everything. A call to a tool not on a narrowed surface is
246
+ * refused by name and agent rather than as unknown, and a connection whose
247
+ * agent configuration cannot be used is served nothing, with the sentence that says
248
+ * why.
249
+ */
250
+ connection?: ConnectionScopeWire;
251
+ }
252
+
253
+ /**
254
+ * The `connection` a loopback answer (`all-tools`, `catalog-revision`)
255
+ * carries, as a field to spread: validated at the boundary by the MCP
256
+ * core's reader (HTTP loses the type), and absent for Everything.
257
+ */
258
+ function connectionOf(body: unknown): { connection?: ConnectionScopeWire } {
259
+ const connection = connectionWireOf(body);
260
+ return connection ? { connection } : {};
225
261
  }
226
262
 
227
263
  /**
@@ -490,13 +526,26 @@ export class McpService {
490
526
  // verifier resolves it to `source: 'external'`: the caller IS an external
491
527
  // agent and must be treated like one (admitted to `start_session`/`ask`,
492
528
  // refused from internal-only tools).
529
+ // The agent the connection was made as rides that token too, so the
530
+ // surfaces behind the loopback narrow to its scope; a connection key
531
+ // carries its own.
532
+ const agentPath = caller.agentPath || null;
493
533
  const loopbackBearer =
494
534
  tokenId == null && this.internalTokens
495
- ? this.internalTokens.mint({ userId, externalProxy: true }, MCP_LOOPBACK_TOKEN_TTL_MS)
535
+ ? this.internalTokens.mint(
536
+ { userId, externalProxy: true, ...(agentPath ? { agentPath } : {}) },
537
+ MCP_LOOPBACK_TOKEN_TTL_MS,
538
+ )
496
539
  : bearer;
497
540
 
498
541
  let instructions: Promise<ComposedAgentInstructions> | undefined;
499
542
  const agentInstructions = () => (instructions ??= this.composeAgentInstructions());
543
+ // What a narrowed connection is told about itself at the handshake: the
544
+ // agent it is connected as, or why it loads nothing. Read off the catalog
545
+ // revision (cheap), not the surface (a full discovery). Everything is
546
+ // told nothing new, so its handshake is exactly what it was.
547
+ const connectionLine = async (): Promise<string | null> =>
548
+ agentPath ? describeConnection((await this.fetchCatalogRevision(loopbackBearer)).connection) : null;
500
549
  // The Audit log's view of this request — only when the request can be
501
550
  // attributed to a key, an agent connection or a page. The catalog names
502
551
  // it classifies pooled tools by are the surface's, which is looked up
@@ -534,6 +583,10 @@ export class McpService {
534
583
  }));
535
584
 
536
585
  const initializing = (Array.isArray(messages) ? messages : [messages]).some((m) => isInitializeRequest(m));
586
+ const handshakeInstructions = async (): Promise<string> => {
587
+ const [{ instructions: text }, line] = await Promise.all([agentInstructions(), connectionLine()]);
588
+ return line ? `${text}\n\n${line}` : text;
589
+ };
537
590
  // The deployment's MCP Apps, read once per request. Declaring the
538
591
  // `resources` capability is what permits the two handlers below, and a
539
592
  // deployment with no view must not declare it — a client would then list
@@ -554,12 +607,12 @@ export class McpService {
554
607
  },
555
608
  // `instructions` rides the initialize result; clients that honour it
556
609
  // place the text in the model's system prompt without the model acting.
557
- ...(initializing ? { instructions: (await agentInstructions()).instructions } : {}),
610
+ ...(initializing ? { instructions: await handshakeInstructions() } : {}),
558
611
  },
559
612
  );
560
613
 
561
614
  server.setRequestHandler(ListToolsRequestSchema, async () => inRequest(async () => {
562
- const [{ tools, catalogNames }, { toolPrefix }, readOnly] = await Promise.all([
615
+ const [{ tools, catalogNames, connection }, { toolPrefix }, readOnly] = await Promise.all([
563
616
  requestSurface(),
564
617
  agentInstructions(),
565
618
  // A failed read lists the built-in hints rather than no listing at all.
@@ -568,6 +621,12 @@ export class McpService {
568
621
  return undefined;
569
622
  }),
570
623
  ]);
624
+ // A connection whose agent configuration cannot be used is served NOTHING — not
625
+ // Everything — and the one sentence saying why rides the listing's
626
+ // `_meta` (the handshake carries it too).
627
+ if (connection?.unavailable) {
628
+ return { tools: [], _meta: { [CONNECTION_META_KEY]: connection.unavailable } };
629
+ }
571
630
  // A discovered tool whose name collides with a meta-tool would be
572
631
  // listed but never callable (the dispatcher routes the name to the
573
632
  // meta-tool first), so drop it from the listing entirely.
@@ -694,7 +753,10 @@ export class McpService {
694
753
  }
695
754
 
696
755
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => inRequest(async () => {
697
- const { client: shared, tools, unavailable, catalogNames, hidden: hiddenTools } = await requestSurface();
756
+ const { client: shared, tools, unavailable, catalogNames, hidden: hiddenTools, connection } = await requestSurface();
757
+ // Nothing is callable on a connection that loads nothing, meta-tools
758
+ // included: every call is answered with the reason.
759
+ if (connection?.unavailable) return toolError(connection.unavailable);
698
760
  // The person's client, as this request uses it: every call it makes —
699
761
  // a chain's too, which comes back from the isolate outside any async
700
762
  // context — carries this request's bearer and audit.
@@ -769,7 +831,16 @@ export class McpService {
769
831
  if (answer && owner && audit) {
770
832
  await audit.denied(`${owner.utcpName}.${toolName.slice(owner.utcpName.length + 1)}`, args);
771
833
  }
772
- return answer ?? toolError(retiredToolMessage(toolName) ?? `Unknown tool "${toolName}".`);
834
+ // On a narrowed connection, a tool off the surface is one its agent
835
+ // does not load: refused by name and agent, never "unknown" — the tool
836
+ // may well exist, for this person, on another connection.
837
+ return (
838
+ answer ??
839
+ toolError(
840
+ retiredToolMessage(toolName) ??
841
+ (connection ? connectionRefusal(toolName, connection) : `Unknown tool "${toolName}".`),
842
+ )
843
+ );
773
844
  }
774
845
  const needsAuth = await needsAuthorization(proxied.manualName);
775
846
  if (needsAuth) {
@@ -879,9 +950,20 @@ export class McpService {
879
950
  */
880
951
  private async surfaceFor(caller: McpCaller, loopbackBearer: string): Promise<RequestSurface> {
881
952
  const { userId, tokenId } = caller;
882
- const revision = await this.fetchCatalogRevision(loopbackBearer);
953
+ const { revision } = await this.fetchCatalogRevision(loopbackBearer);
883
954
  if (revision === null) return this.buildSurface(userId, tokenId, loopbackBearer, 'uncached');
884
- const key = [userId, tokenId != null ? 'key' : 'person', caller.scope ?? 'write', revision].join(POOL_KEY_SEPARATOR);
955
+ // The agent the connection was made as is part of the key: two
956
+ // connections of one person made as different agents are two surfaces.
957
+ // (The revision already moves when the agent's FILE changes — it carries
958
+ // the file's digest — so an edit is a new build at the next request.)
959
+ // Everything adds nothing, and keeps the key it always had.
960
+ const key = [
961
+ userId,
962
+ tokenId != null ? 'key' : 'person',
963
+ caller.scope ?? 'write',
964
+ revision,
965
+ ...(caller.agentPath ? [caller.agentPath] : []),
966
+ ].join(POOL_KEY_SEPARATOR);
885
967
  const { value } = await this.surfaces.get(
886
968
  key,
887
969
  () => this.buildSurface(userId, tokenId, loopbackBearer, 'miss'),
@@ -893,10 +975,13 @@ export class McpService {
893
975
  /**
894
976
  * The caller's catalog revision (`GET /api/agent/catalog-revision`, the one
895
977
  * `hexis-mcp` polls), read with their bearer; null when it cannot be read.
978
+ * With it, the agent the caller's connection is narrowed to, when it is.
896
979
  * A deployment or a test that does not serve the route answers 404, which
897
980
  * is not worth a line in the log; anything else is.
898
981
  */
899
- private async fetchCatalogRevision(bearer: string): Promise<string | null> {
982
+ private async fetchCatalogRevision(
983
+ bearer: string,
984
+ ): Promise<{ revision: string | null; connection?: ConnectionScopeWire }> {
900
985
  try {
901
986
  const res = await fetch(`${this.opts.loopbackBaseUrl}/api/agent/catalog-revision`, {
902
987
  headers: { Authorization: `Bearer ${bearer}` },
@@ -904,13 +989,16 @@ export class McpService {
904
989
  });
905
990
  if (!res.ok) {
906
991
  if (res.status !== 404) log.warn(`catalog-revision loopback failed: HTTP ${res.status}; building this request's surface uncached`);
907
- return null;
992
+ return { revision: null };
908
993
  }
909
- const body = (await res.json()) as { revision?: unknown };
910
- return typeof body.revision === 'string' && body.revision ? body.revision : null;
994
+ const body = (await res.json()) as { revision?: unknown; connection?: unknown };
995
+ return {
996
+ revision: typeof body.revision === 'string' && body.revision ? body.revision : null,
997
+ ...connectionOf(body),
998
+ };
911
999
  } catch (err) {
912
1000
  log.warn("catalog-revision loopback threw; building this request's surface uncached:", { err });
913
- return null;
1001
+ return { revision: null };
914
1002
  }
915
1003
  }
916
1004
 
@@ -935,7 +1023,7 @@ export class McpService {
935
1023
  // The list of manuals this caller gets: the KB manual + each `.tool` they
936
1024
  // can read. Fetched over loopback with the caller's key, so ACL filtering
937
1025
  // lives ONCE behind the REST surface.
938
- const manuals = await this.fetchManualTemplates(loopbackBearer);
1026
+ const { manuals, connection } = await this.fetchManualTemplates(loopbackBearer);
939
1027
  const catalogMs = performance.now() - started;
940
1028
  const client = await this.buildClient(userId, manuals);
941
1029
  const discovered = await this.discoverTools(client, manuals, userId);
@@ -960,7 +1048,7 @@ export class McpService {
960
1048
  (decision.suppressed > 0 ? ` [+${decision.suppressed} identical rebuild(s) since last line]` : ''),
961
1049
  );
962
1050
  }
963
- return { ...discovered, client, tools };
1051
+ return { ...discovered, client, tools, ...(connection ? { connection } : {}) };
964
1052
  }
965
1053
 
966
1054
  /**
@@ -1039,11 +1127,13 @@ export class McpService {
1039
1127
  * request. HTTP transport loses the `CallTemplate` type, so we re-validate the
1040
1128
  * received JSON even though the producing endpoint already validated it.
1041
1129
  */
1042
- private async fetchManualTemplates(bearer: string): Promise<CallTemplate[]> {
1130
+ private async fetchManualTemplates(
1131
+ bearer: string,
1132
+ ): Promise<{ manuals: CallTemplate[]; connection?: ConnectionScopeWire }> {
1043
1133
  // This is the REMOTE proxy, so ask for remote-capable manuals only — local-only
1044
1134
  // `.tool`s are surfaced instead via the `list_local_tools` tool in the KB manual.
1045
1135
  const body = (await this.loopbackJson(bearer, '/api/agent/all-tools?remote=true', 'all-tools')) as
1046
- | { manuals?: unknown }
1136
+ | { manuals?: unknown; connection?: unknown }
1047
1137
  | null;
1048
1138
  const rawManuals: unknown[] = Array.isArray(body?.manuals) ? body.manuals : [];
1049
1139
 
@@ -1062,7 +1152,7 @@ export class McpService {
1062
1152
  }
1063
1153
  // Always include the KB manual, even if `all-tools` was unavailable/regressed.
1064
1154
  if (!hasKb) out.unshift(this.kbManualTemplate());
1065
- return out;
1155
+ return { manuals: out, ...connectionOf(body) };
1066
1156
  }
1067
1157
 
1068
1158
  /** The KB manual's discovery template — the fallback if `all-tools` is unavailable. */
@@ -1600,41 +1690,11 @@ export class McpService {
1600
1690
  const statusMap = new Map(status.map((s) => [s.key, s]));
1601
1691
  const missing: { name: string; label?: string | null; brokenSignIn: boolean }[] = [];
1602
1692
  for (const v of userVars) {
1603
- const st = statusMap.get(v.key);
1693
+ // The one readiness rule `list_tool_setup` reports from (`userSecretGap`).
1604
1694
  // A sign-in the caller already HAS a row for but that isn't (or is no
1605
- // longer) usable: dead/wiped grant, abandoned consent, missing scopes.
1606
- const brokenSignIn = Boolean(v.oauth && st?.userConfigured);
1607
- if (!st?.userConfigured) {
1608
- missing.push({ name: v.name, label: v.label, brokenSignIn }); // no row at all → needs a value / sign-in
1609
- continue;
1610
- }
1611
- // A row of the OTHER kind is not this credential: a `.tool` edited from
1612
- // an OAuth sign-in to a plain key (or back) leaves the old row behind,
1613
- // and serving a token where a key is declared — or a key where a
1614
- // sign-in is — fails opaquely at the provider. Not a broken sign-in
1615
- // either: nothing to reset, the user has to set the credential up as
1616
- // the manual now declares it.
1617
- const declaredKind = v.oauth ? 'oauth' : 'static';
1618
- if (st.userKind && st.userKind !== declaredKind) {
1619
- missing.push({ name: v.name, label: v.label, brokenSignIn: false });
1620
- continue;
1621
- }
1622
- // An OAuth-backed var whose row exists but has no token yet is NOT ready —
1623
- // the user has registered but not completed sign-in. Fail closed: anything
1624
- // other than a confirmed `true` (including a non-oauth row → undefined)
1625
- // counts as not-yet-authorized.
1626
- if (v.oauth && st.userAuthorized !== true) {
1627
- missing.push({ name: v.name, label: v.label, brokenSignIn });
1628
- continue;
1629
- }
1630
- // An OAuth-backed var the user signed in for, but whose token was granted
1631
- // fewer scopes than the tool now declares, is ALSO not ready — the call would
1632
- // otherwise fail opaquely at the provider. Compare the live required scopes
1633
- // against the token's recorded granted scopes; an under-scoped (or unknown)
1634
- // token needs re-authorization.
1635
- if (v.oauth && !scopesCovered(v.oauthScopes, st.grantedScopes)) {
1636
- missing.push({ name: v.name, label: v.label, brokenSignIn });
1637
- }
1695
+ // longer) usable is a broken sign-in: abandoned consent, missing scopes.
1696
+ const gap = userSecretGap(v, statusMap.get(v.key));
1697
+ if (gap) missing.push({ name: v.name, label: v.label, brokenSignIn: gap === 'unauthorized' || gap === 'under-scoped' });
1638
1698
  }
1639
1699
  return missing;
1640
1700
  }