@bevel-software/platform-core-backend 0.19.0 → 0.21.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 (799) hide show
  1. package/dist/core/catalog-cache-invalidation.d.ts +7 -2
  2. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  3. package/dist/core/catalog-cache-invalidation.js +8 -9
  4. package/dist/core/catalog-cache-invalidation.js.map +1 -1
  5. package/dist/core/core-ports.d.ts +19 -0
  6. package/dist/core/core-ports.d.ts.map +1 -1
  7. package/dist/core/core-ports.js.map +1 -1
  8. package/dist/core/create-core-server.d.ts +2 -0
  9. package/dist/core/create-core-server.d.ts.map +1 -1
  10. package/dist/core/create-core-server.js +45 -105
  11. package/dist/core/create-core-server.js.map +1 -1
  12. package/dist/core/create-core-services.d.ts +33 -5
  13. package/dist/core/create-core-services.d.ts.map +1 -1
  14. package/dist/core/create-core-services.js +146 -82
  15. package/dist/core/create-core-services.js.map +1 -1
  16. package/dist/core/lifecycle.d.ts +74 -28
  17. package/dist/core/lifecycle.d.ts.map +1 -1
  18. package/dist/core/lifecycle.js +157 -20
  19. package/dist/core/lifecycle.js.map +1 -1
  20. package/dist/core/public-config.d.ts +5 -1
  21. package/dist/core/public-config.d.ts.map +1 -1
  22. package/dist/core/public-config.js +7 -5
  23. package/dist/core/public-config.js.map +1 -1
  24. package/dist/core-config.d.ts +95 -1
  25. package/dist/core-config.d.ts.map +1 -1
  26. package/dist/core-config.js +24 -8
  27. package/dist/core-config.js.map +1 -1
  28. package/dist/index.d.ts +5 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +6 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/modules/access/access-control.service.d.ts +18 -1
  33. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  34. package/dist/modules/access/access-control.service.js +19 -8
  35. package/dist/modules/access/access-control.service.js.map +1 -1
  36. package/dist/modules/access/access.routes.d.ts +2 -1
  37. package/dist/modules/access/access.routes.d.ts.map +1 -1
  38. package/dist/modules/access/access.routes.js +10 -10
  39. package/dist/modules/access/access.routes.js.map +1 -1
  40. package/dist/modules/access/change-read-gate.d.ts +6 -3
  41. package/dist/modules/access/change-read-gate.d.ts.map +1 -1
  42. package/dist/modules/access/change-read-gate.js +9 -6
  43. package/dist/modules/access/change-read-gate.js.map +1 -1
  44. package/dist/modules/access/creator-access.d.ts +4 -2
  45. package/dist/modules/access/creator-access.d.ts.map +1 -1
  46. package/dist/modules/access/creator-access.js +7 -4
  47. package/dist/modules/access/creator-access.js.map +1 -1
  48. package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
  49. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
  50. package/dist/modules/agent-instructions/read-preamble.js +6 -9
  51. package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
  52. package/dist/modules/audit/agent-audit.service.d.ts +54 -0
  53. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -0
  54. package/dist/modules/audit/agent-audit.service.js +290 -0
  55. package/dist/modules/audit/agent-audit.service.js.map +1 -0
  56. package/dist/modules/audit/agent-rest-audit.middleware.d.ts +40 -0
  57. package/dist/modules/audit/agent-rest-audit.middleware.d.ts.map +1 -0
  58. package/dist/modules/audit/agent-rest-audit.middleware.js +75 -0
  59. package/dist/modules/audit/agent-rest-audit.middleware.js.map +1 -0
  60. package/dist/modules/audit/audit.contract.d.ts +145 -0
  61. package/dist/modules/audit/audit.contract.d.ts.map +1 -0
  62. package/dist/modules/audit/audit.contract.js +35 -0
  63. package/dist/modules/audit/audit.contract.js.map +1 -0
  64. package/dist/modules/audit/audit.routes.d.ts +21 -0
  65. package/dist/modules/audit/audit.routes.d.ts.map +1 -0
  66. package/dist/modules/audit/audit.routes.js +157 -0
  67. package/dist/modules/audit/audit.routes.js.map +1 -0
  68. package/dist/modules/audit/event-classifier.d.ts +52 -0
  69. package/dist/modules/audit/event-classifier.d.ts.map +1 -0
  70. package/dist/modules/audit/event-classifier.js +123 -0
  71. package/dist/modules/audit/event-classifier.js.map +1 -0
  72. package/dist/modules/audit/index.d.ts +7 -0
  73. package/dist/modules/audit/index.d.ts.map +1 -0
  74. package/dist/modules/audit/index.js +7 -0
  75. package/dist/modules/audit/index.js.map +1 -0
  76. package/dist/modules/audit/request-audit.d.ts +57 -0
  77. package/dist/modules/audit/request-audit.d.ts.map +1 -0
  78. package/dist/modules/audit/request-audit.js +117 -0
  79. package/dist/modules/audit/request-audit.js.map +1 -0
  80. package/dist/modules/auth/account-admission.d.ts +40 -0
  81. package/dist/modules/auth/account-admission.d.ts.map +1 -0
  82. package/dist/modules/auth/account-admission.js +17 -0
  83. package/dist/modules/auth/account-admission.js.map +1 -0
  84. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  85. package/dist/modules/auth/account.routes.js +4 -1
  86. package/dist/modules/auth/account.routes.js.map +1 -1
  87. package/dist/modules/auth/auth.service.d.ts +19 -1
  88. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  89. package/dist/modules/auth/auth.service.js +33 -1
  90. package/dist/modules/auth/auth.service.js.map +1 -1
  91. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  92. package/dist/modules/auth/oidc-auth-provider.js +9 -0
  93. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  94. package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
  95. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  96. package/dist/modules/connection-probe/connection-probe.service.js +10 -2
  97. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  98. package/dist/modules/database/advisory-lock.d.ts +17 -0
  99. package/dist/modules/database/advisory-lock.d.ts.map +1 -1
  100. package/dist/modules/database/advisory-lock.js +24 -3
  101. package/dist/modules/database/advisory-lock.js.map +1 -1
  102. package/dist/modules/database/connection.d.ts +67 -2
  103. package/dist/modules/database/connection.d.ts.map +1 -1
  104. package/dist/modules/database/connection.js +118 -12
  105. package/dist/modules/database/connection.js.map +1 -1
  106. package/dist/modules/database/core-schema.d.ts +414 -0
  107. package/dist/modules/database/core-schema.d.ts.map +1 -1
  108. package/dist/modules/database/core-schema.js +99 -0
  109. package/dist/modules/database/core-schema.js.map +1 -1
  110. package/dist/modules/database/migrate.d.ts +1 -1
  111. package/dist/modules/database/migrate.d.ts.map +1 -1
  112. package/dist/modules/database/migrate.js +27 -3
  113. package/dist/modules/database/migrate.js.map +1 -1
  114. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  115. package/dist/modules/diff/diff.service.js +6 -1
  116. package/dist/modules/diff/diff.service.js.map +1 -1
  117. package/dist/modules/kb-fs/branch-name.d.ts +0 -1
  118. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  119. package/dist/modules/kb-fs/branch-name.js +3 -3
  120. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  121. package/dist/modules/kb-fs/clone-config.d.ts +9 -8
  122. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
  123. package/dist/modules/kb-fs/clone-config.js +14 -12
  124. package/dist/modules/kb-fs/clone-config.js.map +1 -1
  125. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +9 -1
  126. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -1
  127. package/dist/modules/kb-fs/git-guarded-filesystem.js +16 -3
  128. package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -1
  129. package/dist/modules/mcp/mcp-auth.middleware.d.ts +1 -0
  130. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  131. package/dist/modules/mcp/mcp-auth.middleware.js +32 -0
  132. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  133. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  134. package/dist/modules/mcp/mcp.routes.js +8 -1
  135. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  136. package/dist/modules/mcp/mcp.service.d.ts +16 -1
  137. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  138. package/dist/modules/mcp/mcp.service.js +52 -9
  139. package/dist/modules/mcp/mcp.service.js.map +1 -1
  140. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +59 -0
  141. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  142. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +180 -8
  143. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  144. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
  145. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  146. package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
  147. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  148. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
  149. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  150. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
  151. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  152. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
  153. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  154. package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
  155. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  156. package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
  157. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  158. package/dist/modules/plugins/discovery/native.source.js +4 -2
  159. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  160. package/dist/modules/plugins/index.d.ts +1 -1
  161. package/dist/modules/plugins/index.d.ts.map +1 -1
  162. package/dist/modules/plugins/index.js +1 -1
  163. package/dist/modules/plugins/index.js.map +1 -1
  164. package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
  165. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  166. package/dist/modules/plugins/join-request-jobs.service.js +6 -6
  167. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  168. package/dist/modules/plugins/join-requests.service.d.ts +3 -1
  169. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  170. package/dist/modules/plugins/join-requests.service.js +9 -8
  171. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  172. package/dist/modules/plugins/plugin-links.d.ts +65 -10
  173. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  174. package/dist/modules/plugins/plugin-links.js +136 -31
  175. package/dist/modules/plugins/plugin-links.js.map +1 -1
  176. package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
  177. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  178. package/dist/modules/plugins/plugin-links.service.js +151 -21
  179. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  180. package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
  181. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  182. package/dist/modules/plugins/plugin-provision.service.js +33 -26
  183. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  184. package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
  185. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  186. package/dist/modules/plugins/plugin-rename.service.js +10 -12
  187. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  188. package/dist/modules/plugins/plugins.routes.d.ts +3 -0
  189. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  190. package/dist/modules/plugins/plugins.routes.js +45 -15
  191. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  192. package/dist/modules/plugins/plugins.service.d.ts +4 -4
  193. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  194. package/dist/modules/plugins/plugins.service.js +7 -10
  195. package/dist/modules/plugins/plugins.service.js.map +1 -1
  196. package/dist/modules/plugins/teams.routes.d.ts +4 -1
  197. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  198. package/dist/modules/plugins/teams.routes.js +11 -9
  199. package/dist/modules/plugins/teams.routes.js.map +1 -1
  200. package/dist/modules/secrets-vault/index.d.ts +1 -1
  201. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  202. package/dist/modules/secrets-vault/index.js +1 -1
  203. package/dist/modules/secrets-vault/index.js.map +1 -1
  204. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
  205. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  206. package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
  207. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  208. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
  209. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  210. package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
  211. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  212. package/dist/modules/settings/deployment-settings.service.d.ts +22 -10
  213. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  214. package/dist/modules/settings/deployment-settings.service.js +52 -28
  215. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  216. package/dist/modules/settings/setup.routes.d.ts +9 -2
  217. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  218. package/dist/modules/settings/setup.routes.js +20 -14
  219. package/dist/modules/settings/setup.routes.js.map +1 -1
  220. package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
  221. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
  222. package/dist/modules/skills/allowed-tools-check.js +8 -8
  223. package/dist/modules/skills/allowed-tools-check.js.map +1 -1
  224. package/dist/modules/skills/pending-skills.service.d.ts +3 -1
  225. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  226. package/dist/modules/skills/pending-skills.service.js +9 -6
  227. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  228. package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -1
  229. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  230. package/dist/modules/skills/skill-access-requests.routes.js +6 -6
  231. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  232. package/dist/modules/skills/skills.routes.d.ts +4 -1
  233. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  234. package/dist/modules/skills/skills.routes.js +4 -4
  235. package/dist/modules/skills/skills.routes.js.map +1 -1
  236. package/dist/modules/skills/skills.service.d.ts +4 -2
  237. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  238. package/dist/modules/skills/skills.service.js +13 -11
  239. package/dist/modules/skills/skills.service.js.map +1 -1
  240. package/dist/modules/tool-auth/external-api-key.interface.d.ts +21 -5
  241. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  242. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  243. package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
  244. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  245. package/dist/modules/tool-auth/external-api-key.service.js +22 -9
  246. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  247. package/dist/modules/tool-auth/internal-token.service.d.ts +10 -0
  248. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  249. package/dist/modules/tool-auth/internal-token.service.js +1 -0
  250. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  251. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +8 -0
  252. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  253. package/dist/modules/tool-auth/tool-auth.middleware.js +9 -1
  254. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  255. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
  256. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  257. package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
  258. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  259. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
  260. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  261. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
  262. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  263. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
  264. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  265. package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
  266. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  267. package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
  268. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
  269. package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
  270. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  271. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
  272. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  273. package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
  274. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  275. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
  276. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  277. package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
  278. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  279. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
  280. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  281. package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
  282. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  283. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
  284. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  285. package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -9
  286. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  287. package/dist/modules/workflow/git/git.service.d.ts +11 -5
  288. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  289. package/dist/modules/workflow/git/git.service.js +23 -14
  290. package/dist/modules/workflow/git/git.service.js.map +1 -1
  291. package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
  292. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  293. package/dist/modules/workflow/git/node-git-runner.js +35 -5
  294. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  295. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  296. package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
  297. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  298. package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
  299. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
  300. package/dist/modules/workflow/session-ontology.service.js +6 -2
  301. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  302. package/dist/modules/workflow/workflow.service.d.ts +8 -3
  303. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  304. package/dist/modules/workflow/workflow.service.js +33 -23
  305. package/dist/modules/workflow/workflow.service.js.map +1 -1
  306. package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
  307. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  308. package/dist/modules/workspace/session-ontology.gate.js +3 -3
  309. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  310. package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
  311. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  312. package/dist/modules/workspace/startup/kb-git.js +15 -13
  313. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  314. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +5 -8
  315. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  316. package/dist/modules/workspace/startup/kb-startup-runner.js +35 -38
  317. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  318. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
  319. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  320. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
  321. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  322. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
  323. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
  324. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
  325. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
  326. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
  327. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
  328. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
  329. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
  330. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  331. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  332. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
  333. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  334. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
  335. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  336. package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
  337. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  338. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
  339. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  340. package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
  341. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  342. package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
  343. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  344. package/dist/modules/workspace/startup/steps/template-source.js +10 -6
  345. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  346. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  347. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  348. package/dist/modules/workspace/workspace.routes.js +8 -7
  349. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  350. package/dist/modules/workspace/workspace.service.d.ts +26 -12
  351. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  352. package/dist/modules/workspace/workspace.service.js +41 -28
  353. package/dist/modules/workspace/workspace.service.js.map +1 -1
  354. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  355. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  356. package/dist/modules/workspace/workspace.tools.js +69 -31
  357. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  358. package/dist/modules/workspace/write-denial.d.ts +3 -2
  359. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  360. package/dist/modules/workspace/write-denial.js +6 -5
  361. package/dist/modules/workspace/write-denial.js.map +1 -1
  362. package/dist/shared/git-internals.d.ts +7 -1
  363. package/dist/shared/git-internals.d.ts.map +1 -1
  364. package/dist/shared/git-internals.js +67 -9
  365. package/dist/shared/git-internals.js.map +1 -1
  366. package/dist/shared/git.contract.d.ts +52 -7
  367. package/dist/shared/git.contract.d.ts.map +1 -1
  368. package/dist/shared/git.contract.js +30 -8
  369. package/dist/shared/git.contract.js.map +1 -1
  370. package/dist/shared/kb-context.d.ts +76 -0
  371. package/dist/shared/kb-context.d.ts.map +1 -0
  372. package/dist/shared/kb-context.js +108 -0
  373. package/dist/shared/kb-context.js.map +1 -0
  374. package/dist/shared/kb-layout.d.ts +3 -2
  375. package/dist/shared/kb-layout.d.ts.map +1 -1
  376. package/dist/shared/kb-layout.js +4 -3
  377. package/dist/shared/kb-layout.js.map +1 -1
  378. package/dist/shared/kb-layout.test.js +35 -27
  379. package/dist/shared/kb-layout.test.js.map +1 -1
  380. package/dist/shared/pending-proposals.d.ts +4 -1
  381. package/dist/shared/pending-proposals.d.ts.map +1 -1
  382. package/dist/shared/pending-proposals.js +3 -4
  383. package/dist/shared/pending-proposals.js.map +1 -1
  384. package/dist/shared/redact-secret.d.ts +5 -5
  385. package/dist/shared/redact-secret.js +5 -5
  386. package/dist/tenancy/index.d.ts +13 -0
  387. package/dist/tenancy/index.d.ts.map +1 -0
  388. package/dist/tenancy/index.js +13 -0
  389. package/dist/tenancy/index.js.map +1 -0
  390. package/dist/tenancy/static-tenant-source.d.ts +98 -0
  391. package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
  392. package/dist/tenancy/static-tenant-source.js +220 -0
  393. package/dist/tenancy/static-tenant-source.js.map +1 -0
  394. package/dist/tenancy/tenant-host.d.ts +80 -0
  395. package/dist/tenancy/tenant-host.d.ts.map +1 -0
  396. package/dist/tenancy/tenant-host.js +217 -0
  397. package/dist/tenancy/tenant-host.js.map +1 -0
  398. package/dist/tenancy/tenant-runtime.d.ts +91 -0
  399. package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
  400. package/dist/tenancy/tenant-runtime.js +174 -0
  401. package/dist/tenancy/tenant-runtime.js.map +1 -0
  402. package/dist/tenancy/tenant-secrets.d.ts +24 -0
  403. package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
  404. package/dist/tenancy/tenant-secrets.js +30 -0
  405. package/dist/tenancy/tenant-secrets.js.map +1 -0
  406. package/dist/tenancy/tenant-source.contract.d.ts +45 -0
  407. package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
  408. package/dist/tenancy/tenant-source.contract.js +21 -0
  409. package/dist/tenancy/tenant-source.contract.js.map +1 -0
  410. package/migrations/0000_core_init.sql +14 -14
  411. package/migrations/0003_deployment_settings.sql +1 -1
  412. package/migrations/0005_github_facade.sql +1 -1
  413. package/migrations/0012_agent_audit.sql +41 -0
  414. package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -0
  415. package/migrations/meta/0012_snapshot.json +2247 -0
  416. package/migrations/meta/0013_snapshot.json +2259 -0
  417. package/migrations/meta/_journal.json +14 -0
  418. package/package.json +4 -4
  419. package/src/__tests__/branch-model.test.ts +7 -6
  420. package/src/__tests__/kb-context.ts +30 -0
  421. package/src/__tests__/kb-layout-config.test.ts +35 -30
  422. package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
  423. package/src/core/__tests__/lifecycle.test.ts +78 -1
  424. package/src/core/__tests__/public-config.test.ts +5 -1
  425. package/src/core/catalog-cache-invalidation.ts +16 -11
  426. package/src/core/core-ports.ts +19 -0
  427. package/src/core/create-core-server.ts +55 -106
  428. package/src/core/create-core-services.ts +193 -95
  429. package/src/core/lifecycle.ts +214 -29
  430. package/src/core/public-config.ts +8 -5
  431. package/src/core-config.ts +107 -8
  432. package/src/index.ts +25 -2
  433. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
  434. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
  435. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
  436. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
  437. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  438. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
  439. package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
  440. package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
  441. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
  442. package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
  443. package/src/modules/access/__tests__/creator-access.test.ts +3 -2
  444. package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
  445. package/src/modules/access/access-control.service.ts +20 -6
  446. package/src/modules/access/access.routes.ts +10 -13
  447. package/src/modules/access/change-read-gate.ts +10 -4
  448. package/src/modules/access/creator-access.ts +7 -2
  449. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
  450. package/src/modules/agent-instructions/read-preamble.ts +10 -12
  451. package/src/modules/audit/__tests__/agent-audit.service.test.ts +370 -0
  452. package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -0
  453. package/src/modules/audit/__tests__/audit.routes.test.ts +201 -0
  454. package/src/modules/audit/__tests__/event-classifier.test.ts +121 -0
  455. package/src/modules/audit/agent-audit.service.ts +316 -0
  456. package/src/modules/audit/agent-rest-audit.middleware.ts +83 -0
  457. package/src/modules/audit/audit.contract.ts +164 -0
  458. package/src/modules/audit/audit.routes.ts +168 -0
  459. package/src/modules/audit/event-classifier.ts +155 -0
  460. package/src/modules/audit/index.ts +14 -0
  461. package/src/modules/audit/request-audit.ts +141 -0
  462. package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
  463. package/src/modules/auth/__tests__/auth.service.test.ts +60 -0
  464. package/src/modules/auth/account-admission.ts +44 -0
  465. package/src/modules/auth/account.routes.ts +4 -1
  466. package/src/modules/auth/auth.service.ts +33 -1
  467. package/src/modules/auth/oidc-auth-provider.ts +9 -0
  468. package/src/modules/connection-probe/connection-probe.service.ts +7 -1
  469. package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
  470. package/src/modules/database/__tests__/connection.test.ts +98 -0
  471. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
  472. package/src/modules/database/advisory-lock.ts +28 -4
  473. package/src/modules/database/connection.ts +143 -13
  474. package/src/modules/database/core-schema.ts +104 -0
  475. package/src/modules/database/migrate.ts +48 -14
  476. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  477. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  478. package/src/modules/diff/diff.service.ts +6 -1
  479. package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
  480. package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
  481. package/src/modules/kb-fs/branch-name.ts +3 -3
  482. package/src/modules/kb-fs/clone-config.ts +15 -12
  483. package/src/modules/kb-fs/git-guarded-filesystem.ts +16 -3
  484. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
  485. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +183 -13
  486. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -2
  487. package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +24 -4
  488. package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +1 -1
  489. package/src/modules/mcp/__tests__/mcp.service.test.ts +120 -3
  490. package/src/modules/mcp/mcp-auth.middleware.ts +31 -0
  491. package/src/modules/mcp/mcp.routes.ts +11 -1
  492. package/src/modules/mcp/mcp.service.ts +73 -8
  493. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +184 -6
  494. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
  495. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
  496. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
  497. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
  498. package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -1
  499. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
  500. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
  501. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
  502. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
  503. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
  504. package/src/modules/plugins/__tests__/plugins.routes.test.ts +11 -2
  505. package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
  506. package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
  507. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
  508. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
  509. package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
  510. package/src/modules/plugins/discovery/native.source.ts +4 -1
  511. package/src/modules/plugins/index.ts +1 -1
  512. package/src/modules/plugins/join-request-jobs.service.ts +8 -7
  513. package/src/modules/plugins/join-requests.service.ts +7 -7
  514. package/src/modules/plugins/plugin-links.service.ts +192 -19
  515. package/src/modules/plugins/plugin-links.ts +147 -27
  516. package/src/modules/plugins/plugin-provision.service.ts +54 -27
  517. package/src/modules/plugins/plugin-rename.service.ts +10 -10
  518. package/src/modules/plugins/plugins.routes.ts +45 -15
  519. package/src/modules/plugins/plugins.service.ts +7 -11
  520. package/src/modules/plugins/teams.routes.ts +16 -8
  521. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
  522. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
  523. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
  524. package/src/modules/secrets-vault/index.ts +2 -0
  525. package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
  526. package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
  527. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +59 -11
  528. package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
  529. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
  530. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
  531. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
  532. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
  533. package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
  534. package/src/modules/settings/deployment-settings.service.ts +68 -28
  535. package/src/modules/settings/setup.routes.ts +26 -16
  536. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
  537. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
  538. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
  539. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +2 -1
  540. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
  541. package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
  542. package/src/modules/skills/allowed-tools-check.ts +7 -6
  543. package/src/modules/skills/pending-skills.service.ts +9 -8
  544. package/src/modules/skills/skill-access-requests.routes.ts +7 -7
  545. package/src/modules/skills/skills.routes.ts +4 -3
  546. package/src/modules/skills/skills.service.ts +13 -9
  547. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +29 -8
  548. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +6 -0
  549. package/src/modules/tool-auth/external-api-key.interface.ts +22 -5
  550. package/src/modules/tool-auth/external-api-key.service.ts +25 -8
  551. package/src/modules/tool-auth/internal-token.service.ts +11 -0
  552. package/src/modules/tool-auth/tool-auth.middleware.ts +17 -1
  553. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
  554. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
  555. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
  556. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  557. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
  558. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
  559. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
  560. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
  561. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
  562. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
  563. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
  564. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
  565. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
  566. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
  567. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
  568. package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
  569. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
  570. package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
  571. package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
  572. package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
  573. package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
  574. package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
  575. package/src/modules/workflow/__tests__/apply-failure.test.ts +2 -1
  576. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
  577. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
  578. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
  579. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
  580. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
  581. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
  582. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
  583. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +28 -27
  584. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
  585. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
  586. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
  587. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
  588. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
  589. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +2 -1
  590. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +2 -1
  591. package/src/modules/workflow/agent-tools/workflow.tools.ts +11 -9
  592. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
  593. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
  594. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
  595. package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
  596. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
  597. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
  598. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
  599. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
  600. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
  601. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
  602. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
  603. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
  604. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
  605. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
  606. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
  607. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
  608. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
  609. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
  610. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +9 -8
  611. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
  612. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
  613. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
  614. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
  615. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
  616. package/src/modules/workflow/git/git.service.ts +24 -13
  617. package/src/modules/workflow/git/node-git-runner.ts +37 -4
  618. package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
  619. package/src/modules/workflow/session-ontology.service.ts +7 -2
  620. package/src/modules/workflow/workflow.service.ts +31 -24
  621. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
  622. package/src/modules/workspace/__tests__/git-internals.security.test.ts +144 -52
  623. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
  624. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
  625. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
  626. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
  627. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
  628. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
  629. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
  630. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
  631. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
  632. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
  633. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
  634. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  635. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  636. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  637. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  638. package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
  639. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
  640. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
  641. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +4 -3
  642. package/src/modules/workspace/__tests__/workspace.tools.test.ts +9 -8
  643. package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
  644. package/src/modules/workspace/session-ontology.gate.ts +6 -4
  645. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
  646. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +29 -21
  647. package/src/modules/workspace/startup/kb-git.ts +15 -22
  648. package/src/modules/workspace/startup/kb-startup-runner.ts +40 -46
  649. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
  650. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
  651. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
  652. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
  653. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
  654. package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
  655. package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
  656. package/src/modules/workspace/startup/steps/template-source.ts +8 -5
  657. package/src/modules/workspace/workspace.routes.ts +8 -7
  658. package/src/modules/workspace/workspace.service.ts +39 -27
  659. package/src/modules/workspace/workspace.tools.ts +68 -31
  660. package/src/modules/workspace/write-denial.ts +7 -5
  661. package/src/shared/__tests__/git-internals.test.ts +41 -0
  662. package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
  663. package/src/shared/git-internals.ts +61 -6
  664. package/src/shared/git.contract.ts +70 -8
  665. package/src/shared/kb-context.ts +130 -0
  666. package/src/shared/kb-layout.test.ts +37 -27
  667. package/src/shared/kb-layout.ts +4 -3
  668. package/src/shared/pending-proposals.ts +7 -4
  669. package/src/shared/redact-secret.ts +5 -5
  670. package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
  671. package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
  672. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
  673. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
  674. package/src/tenancy/index.ts +35 -0
  675. package/src/tenancy/static-tenant-source.ts +296 -0
  676. package/src/tenancy/tenant-host.ts +261 -0
  677. package/src/tenancy/tenant-runtime.ts +208 -0
  678. package/src/tenancy/tenant-secrets.ts +41 -0
  679. package/src/tenancy/tenant-source.contract.ts +59 -0
  680. package/dist/modules/access/access-errors.d.ts +0 -34
  681. package/dist/modules/access/access-errors.d.ts.map +0 -1
  682. package/dist/modules/access/access-errors.js +0 -40
  683. package/dist/modules/access/access-errors.js.map +0 -1
  684. package/dist/modules/access/access-splice.d.ts +0 -140
  685. package/dist/modules/access/access-splice.d.ts.map +0 -1
  686. package/dist/modules/access/access-splice.js +0 -389
  687. package/dist/modules/access/access-splice.js.map +0 -1
  688. package/dist/modules/access/group-files.d.ts +0 -83
  689. package/dist/modules/access/group-files.d.ts.map +0 -1
  690. package/dist/modules/access/group-files.js +0 -167
  691. package/dist/modules/access/group-files.js.map +0 -1
  692. package/dist/modules/access/kb-read-filter.d.ts +0 -41
  693. package/dist/modules/access/kb-read-filter.d.ts.map +0 -1
  694. package/dist/modules/access/kb-read-filter.js +0 -60
  695. package/dist/modules/access/kb-read-filter.js.map +0 -1
  696. package/dist/modules/access/render-roles-yaml.d.ts +0 -22
  697. package/dist/modules/access/render-roles-yaml.d.ts.map +0 -1
  698. package/dist/modules/access/render-roles-yaml.js +0 -56
  699. package/dist/modules/access/render-roles-yaml.js.map +0 -1
  700. package/dist/modules/access/roles-yaml-guard.d.ts +0 -56
  701. package/dist/modules/access/roles-yaml-guard.d.ts.map +0 -1
  702. package/dist/modules/access/roles-yaml-guard.js +0 -79
  703. package/dist/modules/access/roles-yaml-guard.js.map +0 -1
  704. package/dist/modules/diff/diff-paths.d.ts +0 -2
  705. package/dist/modules/diff/diff-paths.d.ts.map +0 -1
  706. package/dist/modules/diff/diff-paths.js +0 -9
  707. package/dist/modules/diff/diff-paths.js.map +0 -1
  708. package/dist/modules/groups/group-provision.service.d.ts +0 -130
  709. package/dist/modules/groups/group-provision.service.d.ts.map +0 -1
  710. package/dist/modules/groups/group-provision.service.js +0 -288
  711. package/dist/modules/groups/group-provision.service.js.map +0 -1
  712. package/dist/modules/groups/groups.contract.d.ts +0 -106
  713. package/dist/modules/groups/groups.contract.d.ts.map +0 -1
  714. package/dist/modules/groups/groups.contract.js +0 -36
  715. package/dist/modules/groups/groups.contract.js.map +0 -1
  716. package/dist/modules/groups/groups.routes.d.ts +0 -42
  717. package/dist/modules/groups/groups.routes.d.ts.map +0 -1
  718. package/dist/modules/groups/groups.routes.js +0 -379
  719. package/dist/modules/groups/groups.routes.js.map +0 -1
  720. package/dist/modules/groups/groups.service.d.ts +0 -60
  721. package/dist/modules/groups/groups.service.d.ts.map +0 -1
  722. package/dist/modules/groups/groups.service.js +0 -172
  723. package/dist/modules/groups/groups.service.js.map +0 -1
  724. package/dist/modules/groups/index.d.ts +0 -7
  725. package/dist/modules/groups/index.d.ts.map +0 -1
  726. package/dist/modules/groups/index.js +0 -6
  727. package/dist/modules/groups/index.js.map +0 -1
  728. package/dist/modules/groups/join-proposals.d.ts +0 -53
  729. package/dist/modules/groups/join-proposals.d.ts.map +0 -1
  730. package/dist/modules/groups/join-proposals.js +0 -67
  731. package/dist/modules/groups/join-proposals.js.map +0 -1
  732. package/dist/modules/groups/join-requests.service.d.ts +0 -81
  733. package/dist/modules/groups/join-requests.service.d.ts.map +0 -1
  734. package/dist/modules/groups/join-requests.service.js +0 -135
  735. package/dist/modules/groups/join-requests.service.js.map +0 -1
  736. package/dist/modules/mcp/mcp-session-store.d.ts +0 -74
  737. package/dist/modules/mcp/mcp-session-store.d.ts.map +0 -1
  738. package/dist/modules/mcp/mcp-session-store.js +0 -131
  739. package/dist/modules/mcp/mcp-session-store.js.map +0 -1
  740. package/dist/modules/workflow/file-change-notifier.d.ts +0 -38
  741. package/dist/modules/workflow/file-change-notifier.d.ts.map +0 -1
  742. package/dist/modules/workflow/file-change-notifier.js +0 -22
  743. package/dist/modules/workflow/file-change-notifier.js.map +0 -1
  744. package/dist/modules/workflow/git/branch-name.d.ts +0 -10
  745. package/dist/modules/workflow/git/branch-name.d.ts.map +0 -1
  746. package/dist/modules/workflow/git/branch-name.js +0 -76
  747. package/dist/modules/workflow/git/branch-name.js.map +0 -1
  748. package/dist/modules/workflow/git/clone-config.d.ts +0 -61
  749. package/dist/modules/workflow/git/clone-config.d.ts.map +0 -1
  750. package/dist/modules/workflow/git/clone-config.js +0 -69
  751. package/dist/modules/workflow/git/clone-config.js.map +0 -1
  752. package/dist/modules/workflow/git/mutex.d.ts +0 -11
  753. package/dist/modules/workflow/git/mutex.d.ts.map +0 -1
  754. package/dist/modules/workflow/git/mutex.js +0 -23
  755. package/dist/modules/workflow/git/mutex.js.map +0 -1
  756. package/dist/modules/workflow/locking-filesystem.d.ts +0 -137
  757. package/dist/modules/workflow/locking-filesystem.d.ts.map +0 -1
  758. package/dist/modules/workflow/locking-filesystem.js +0 -553
  759. package/dist/modules/workflow/locking-filesystem.js.map +0 -1
  760. package/dist/modules/workflow/read-only-filesystem.d.ts +0 -24
  761. package/dist/modules/workflow/read-only-filesystem.d.ts.map +0 -1
  762. package/dist/modules/workflow/read-only-filesystem.js +0 -39
  763. package/dist/modules/workflow/read-only-filesystem.js.map +0 -1
  764. package/dist/modules/workflow/workflow.errors.d.ts +0 -197
  765. package/dist/modules/workflow/workflow.errors.d.ts.map +0 -1
  766. package/dist/modules/workflow/workflow.errors.js +0 -298
  767. package/dist/modules/workflow/workflow.errors.js.map +0 -1
  768. package/dist/modules/workspace/bevel-ignore.d.ts +0 -30
  769. package/dist/modules/workspace/bevel-ignore.d.ts.map +0 -1
  770. package/dist/modules/workspace/bevel-ignore.js +0 -61
  771. package/dist/modules/workspace/bevel-ignore.js.map +0 -1
  772. package/dist/modules/workspace/kb-seed.interface.d.ts +0 -36
  773. package/dist/modules/workspace/kb-seed.interface.d.ts.map +0 -1
  774. package/dist/modules/workspace/kb-seed.interface.js +0 -2
  775. package/dist/modules/workspace/kb-seed.interface.js.map +0 -1
  776. package/dist/modules/workspace/kb-seed.service.d.ts +0 -125
  777. package/dist/modules/workspace/kb-seed.service.d.ts.map +0 -1
  778. package/dist/modules/workspace/kb-seed.service.js +0 -535
  779. package/dist/modules/workspace/kb-seed.service.js.map +0 -1
  780. package/dist/modules/workspace/plugins-migration.d.ts +0 -50
  781. package/dist/modules/workspace/plugins-migration.d.ts.map +0 -1
  782. package/dist/modules/workspace/plugins-migration.js +0 -379
  783. package/dist/modules/workspace/plugins-migration.js.map +0 -1
  784. package/dist/shared/fs-errors.d.ts +0 -8
  785. package/dist/shared/fs-errors.d.ts.map +0 -1
  786. package/dist/shared/fs-errors.js +0 -11
  787. package/dist/shared/fs-errors.js.map +0 -1
  788. package/dist/shared/fs-walk.d.ts +0 -23
  789. package/dist/shared/fs-walk.d.ts.map +0 -1
  790. package/dist/shared/fs-walk.js +0 -42
  791. package/dist/shared/fs-walk.js.map +0 -1
  792. package/dist/shared/hash-email.d.ts +0 -11
  793. package/dist/shared/hash-email.d.ts.map +0 -1
  794. package/dist/shared/hash-email.js +0 -14
  795. package/dist/shared/hash-email.js.map +0 -1
  796. package/dist/shared/kb-walk.d.ts +0 -52
  797. package/dist/shared/kb-walk.d.ts.map +0 -1
  798. package/dist/shared/kb-walk.js +0 -76
  799. package/dist/shared/kb-walk.js.map +0 -1
@@ -2,9 +2,12 @@ import { describe, expect, it, vi } from 'vitest';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { InvalidGrantError, InvalidScopeError, InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
4
4
  import type { OAuthClientInformationFull } from '@modelcontextprotocol/sdk/shared/auth.js';
5
- import { BevelOAuthProvider } from '../oauth/bevel-oauth-provider.js';
5
+ import type { SQL } from 'drizzle-orm';
6
+ import { PgDialect } from 'drizzle-orm/pg-core';
7
+ import { BevelOAuthProvider, agentKeyFor } from '../oauth/bevel-oauth-provider.js';
6
8
  import { signAuthRequest, verifyAuthRequest } from '../oauth/oauth-state.js';
7
9
  import type { Database } from '../../database/connection.js';
10
+ import { agentConnections, oauthTokens } from '../../database/schema.js';
8
11
 
9
12
  const STATE_SECRET = 'test-state-secret';
10
13
  const PREFIX = 'bevel-mcp_';
@@ -12,9 +15,19 @@ const FRONTEND = 'https://app.example.com';
12
15
 
13
16
  /** Same fake drizzle chain as external-api-key.service.test.ts: every
14
17
  * chainable method returns the chain, the chain is thenable, and each
15
- * db.insert/select/update pops the next queued result. */
18
+ * db.insert/select/update pops the next queued result. What the provider
19
+ * WRITES and WHERE it writes it are captured too — `set`, `values`, the
20
+ * `update` target and every `where` predicate (rendered to SQL by
21
+ * {@link render}) — so a test can pin the row a statement lands on, not
22
+ * merely that a statement ran. */
16
23
  function makeFakeDb(queue: any[]) {
17
- const captured: { values: any[]; set: any[] } = { values: [], set: [] };
24
+ const captured: {
25
+ values: any[];
26
+ set: any[];
27
+ where: SQL[];
28
+ updateTargets: unknown[];
29
+ onConflict: any[];
30
+ } = { values: [], set: [], where: [], updateTargets: [], onConflict: [] };
18
31
 
19
32
  function nextChain() {
20
33
  const result = queue.shift();
@@ -26,11 +39,13 @@ function makeFakeDb(queue: any[]) {
26
39
  });
27
40
  chain.values = passthrough((a) => captured.values.push(a[0]));
28
41
  chain.set = passthrough((a) => captured.set.push(a[0]));
29
- chain.where = passthrough();
42
+ chain.where = passthrough((a) => captured.where.push(a[0]));
30
43
  chain.limit = passthrough();
31
44
  chain.innerJoin = passthrough();
45
+ chain.leftJoin = passthrough();
32
46
  chain.from = passthrough();
33
47
  chain.returning = passthrough();
48
+ chain.onConflictDoNothing = passthrough((a) => captured.onConflict.push(a[0]));
34
49
  chain.then = (onF: any, onR: any) => Promise.resolve(result).then(onF, onR);
35
50
  return chain;
36
51
  }
@@ -38,12 +53,23 @@ function makeFakeDb(queue: any[]) {
38
53
  const db = {
39
54
  insert: vi.fn(() => nextChain()),
40
55
  select: vi.fn(() => nextChain()),
41
- update: vi.fn(() => nextChain()),
56
+ update: vi.fn((table: unknown) => {
57
+ captured.updateTargets.push(table);
58
+ return nextChain();
59
+ }),
60
+ // The mint-time prune of expired token rows; nothing here queues for it.
61
+ delete: vi.fn(() => nextChain()),
42
62
  } as unknown as Database;
43
63
 
44
64
  return { db, captured };
45
65
  }
46
66
 
67
+ /** A captured predicate as the SQL it would send, with its parameters. */
68
+ function render(fragment: SQL): { sql: string; params: unknown[] } {
69
+ const q = new PgDialect().sqlToQuery(fragment);
70
+ return { sql: q.sql, params: q.params };
71
+ }
72
+
47
73
  function makeProvider(queue: any[] = []) {
48
74
  const { db, captured } = makeFakeDb(queue);
49
75
  const provider = new BevelOAuthProvider({
@@ -183,7 +209,15 @@ describe('BevelOAuthProvider', () => {
183
209
  clientId: 'client-1', userId: 'user-1', redirectUri: 'https://agent.example.com/callback',
184
210
  scope: 'mcp', resource: null,
185
211
  };
186
- const { provider, captured } = makeProvider([[consumed] /* update.returning */, undefined /* insert */]);
212
+ const { provider, captured } = makeProvider([
213
+ [consumed] /* update.returning */,
214
+ undefined /* prune: expired tokens */,
215
+ undefined /* prune: expired codes */,
216
+ [{ clientName: 'Claude' }] /* the client's registration */,
217
+ [] /* no live agent connection yet for (user, "claude") */,
218
+ [{ id: 'conn-1' }] /* connection insert.returning */,
219
+ undefined /* token insert */,
220
+ ]);
187
221
 
188
222
  const tokens = await provider.exchangeAuthorizationCode(
189
223
  CLIENT, 'the-code', undefined, 'https://agent.example.com/callback',
@@ -194,15 +228,51 @@ describe('BevelOAuthProvider', () => {
194
228
  expect(tokens.refresh_token!.startsWith(`${PREFIX}r_`)).toBe(true);
195
229
  expect(tokens.expires_in).toBe(3600);
196
230
  expect(tokens.scope).toBe('mcp');
197
- // The token row stores hashes of exactly what was returned.
198
- expect(captured.values[0]).toMatchObject({
231
+ // A first mint for (user, agent) makes the durable agent connection,
232
+ // keyed by the client's folded name and snapshotting the name shown…
233
+ expect(captured.values[0]).toEqual({ userId: 'user-1', clientId: 'client-1', clientName: 'Claude', agentKey: 'claude' });
234
+ // …looked up by that key, so a later registration of "Claude" by this
235
+ // person lands on the same row instead of making a sixth one.
236
+ // (where[0] consumed the code, [1]/[2] are the prunes, [3] read the client.)
237
+ expect(render(captured.where[4])).toMatchObject({ params: ['user-1', 'claude'] });
238
+ expect(render(captured.where[4]).sql).toMatch(/"agent_connections"."agent_key" = \$2/);
239
+ // …and the token row stores hashes of exactly what was returned, bound to it.
240
+ expect(captured.values[1]).toMatchObject({
199
241
  accessTokenHash: sha256(tokens.access_token),
200
242
  refreshTokenHash: sha256(tokens.refresh_token!),
201
243
  clientId: 'client-1',
202
244
  userId: 'user-1',
245
+ connectionId: 'conn-1',
203
246
  });
204
247
  });
205
248
 
249
+ it('a mint that loses the race for the first connection re-reads the winner instead of failing', async () => {
250
+ const consumed = { clientId: 'client-1', userId: 'user-1', redirectUri: 'https://agent.example.com/callback', scope: null, resource: null };
251
+ const { provider, captured } = makeProvider([
252
+ [consumed],
253
+ undefined, undefined, /* prunes */
254
+ [{ clientName: 'Claude' }],
255
+ [] /* no live connection at first look */,
256
+ [] /* insert: ON CONFLICT DO NOTHING returned no row */,
257
+ [{ id: 'conn-winner' }] /* the re-read finds the other mint's row */,
258
+ undefined /* token insert */,
259
+ ]);
260
+ await provider.exchangeAuthorizationCode(CLIENT, 'the-code', undefined, 'https://agent.example.com/callback');
261
+ expect(captured.values[1]).toMatchObject({ connectionId: 'conn-winner' });
262
+ // The insert really did stand down on the conflict — on the live-agent
263
+ // index, not any other — rather than the fake merely answering "no row".
264
+ expect(captured.onConflict).toHaveLength(1);
265
+ expect(captured.onConflict[0].target).toEqual([agentConnections.userId, agentConnections.agentKey]);
266
+ expect(render(captured.onConflict[0].where).sql).toMatch(/"revoked_at" is null/);
267
+ });
268
+
269
+ it('keys a connection by the folded client name, so re-registrations of one agent share a row; a nameless client keys by its id', () => {
270
+ expect(agentKeyFor('Claude', 'c-1')).toBe('claude');
271
+ expect(agentKeyFor(' Claude Code ', 'c-2')).toBe('claude code');
272
+ expect(agentKeyFor(null, 'c-3')).toBe('c-3');
273
+ expect(agentKeyFor(' ', 'c-4')).toBe('c-4');
274
+ });
275
+
206
276
  it('exchangeAuthorizationCode rejects a spent/unknown code and a redirect_uri mismatch', async () => {
207
277
  await expect(
208
278
  makeProvider([[]]).provider.exchangeAuthorizationCode(CLIENT, 'spent'),
@@ -216,12 +286,30 @@ describe('BevelOAuthProvider', () => {
216
286
 
217
287
  it('exchangeRefreshToken rotates (revokes old row, mints new pair) and only narrows scope', async () => {
218
288
  const oldRow = {
219
- clientId: 'client-1', userId: 'user-1', scope: 'mcp extra', resource: null,
289
+ clientId: 'client-1', userId: 'user-1', connectionId: 'conn-1', scope: 'mcp extra', resource: null,
220
290
  refreshExpiresAt: new Date(Date.now() + 60_000),
221
291
  };
222
- const { provider } = makeProvider([[oldRow] /* revoke.returning */, undefined /* insert */]);
292
+ const { provider, captured, db } = makeProvider([
293
+ [oldRow] /* revoke.returning */,
294
+ [{ id: 'conn-1' }] /* the token's connection, still live */,
295
+ undefined /* the (throttled) use stamp */,
296
+ undefined, undefined, /* prunes */
297
+ undefined /* token insert */,
298
+ ]);
223
299
  const tokens = await provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old', ['mcp']);
224
300
  expect(tokens.scope).toBe('mcp');
301
+ // A refresh STAYS on its token's connection: one connection spans every
302
+ // token the agent holds, nothing is looked up by (user, agent) and no
303
+ // second row is inserted for it.
304
+ expect(captured.values).toHaveLength(1);
305
+ expect(captured.values[0]).toMatchObject({ connectionId: 'conn-1' });
306
+ // The liveness check is a read of THAT connection, live rows only…
307
+ expect((db as any).select).toHaveBeenCalledTimes(1);
308
+ expect(render(captured.where[1])).toMatchObject({ params: ['conn-1'] });
309
+ expect(render(captured.where[1]).sql).toMatch(/"agent_connections"."revoked_at" is null/);
310
+ // …and the use is stamped separately, off the refresh's own path.
311
+ expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
312
+ expect(Object.keys(captured.set[1])).toEqual(['lastUsedAt']);
225
313
 
226
314
  // Widening is refused.
227
315
  await expect(
@@ -234,18 +322,100 @@ describe('BevelOAuthProvider', () => {
234
322
  ).rejects.toBeInstanceOf(InvalidGrantError);
235
323
  });
236
324
 
325
+ it('exchangeRefreshToken refuses a token whose agent connection was revoked, minting nothing', async () => {
326
+ // The race a revoke cannot otherwise win: the refresh consumed its old
327
+ // token a moment before the revoke landed. Looking the connection up
328
+ // afresh would find none live and make a new one; staying on the token's
329
+ // own connection finds it revoked and stops.
330
+ const oldRow = {
331
+ clientId: 'client-1', userId: 'user-1', connectionId: 'conn-1', scope: 'mcp', resource: null,
332
+ refreshExpiresAt: new Date(Date.now() + 60_000),
333
+ };
334
+ const { provider, captured } = makeProvider([[oldRow], [] /* connection revoked or gone */]);
335
+ await expect(provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old')).rejects.toBeInstanceOf(InvalidGrantError);
336
+ expect(captured.values).toHaveLength(0);
337
+ expect(captured.onConflict).toHaveLength(0);
338
+ });
339
+
340
+ it('exchangeRefreshToken places a token from before connections existed under one, as a first mint would', async () => {
341
+ const oldRow = {
342
+ clientId: 'client-1', userId: 'user-1', connectionId: null, scope: 'mcp', resource: null,
343
+ refreshExpiresAt: new Date(Date.now() + 60_000),
344
+ };
345
+ const { provider, captured } = makeProvider([
346
+ [oldRow],
347
+ undefined, undefined, /* prunes */
348
+ [{ clientName: 'Claude' }],
349
+ [] /* no live connection */,
350
+ [{ id: 'conn-new' }] /* connection insert.returning */,
351
+ undefined /* token insert */,
352
+ ]);
353
+ await provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old');
354
+ expect(captured.values[0]).toEqual({ userId: 'user-1', clientId: 'client-1', clientName: 'Claude', agentKey: 'claude' });
355
+ expect(captured.values[1]).toMatchObject({ connectionId: 'conn-new' });
356
+ });
357
+
237
358
  it('verifyAccessToken resolves a live token to AuthInfo carrying the Bevel user', async () => {
238
359
  const row = {
239
- id: 'tok-row-1', clientId: 'client-1', scope: 'mcp', resource: null,
360
+ id: 'tok-row-1', clientId: 'client-1', connectionId: 'conn-1', scope: 'mcp', resource: null,
240
361
  expiresAt: new Date(Date.now() + 60_000), userId: 'user-1', userEmail: 'alice@example.com',
241
362
  };
242
- const { provider } = makeProvider([[row] /* select */, undefined /* lastUsedAt touch */]);
363
+ const { provider, captured } = makeProvider([
364
+ [row] /* select */,
365
+ undefined /* token lastUsedAt touch */,
366
+ undefined /* connection lastUsedAt touch */,
367
+ [row] /* a second verify a moment later */,
368
+ undefined /* its token touch — and NO second connection touch */,
369
+ ]);
243
370
 
244
371
  const info = await provider.verifyAccessToken('bevel-mcp_live');
245
372
 
246
373
  expect(info.clientId).toBe('client-1');
247
374
  expect(info.scopes).toEqual(['mcp']);
248
- expect(info.extra).toMatchObject({ userId: 'user-1', userEmail: 'alice@example.com' });
375
+ // The agent connection rides along, for the auth middleware to bind.
376
+ expect(info.extra).toMatchObject({ userId: 'user-1', userEmail: 'alice@example.com', connectionId: 'conn-1' });
377
+ // The lookup admits a token only while its connection is unrevoked — or
378
+ // has none to be revoked (a token from before connections existed).
379
+ const lookup = render(captured.where[0]).sql;
380
+ expect(lookup).toMatch(/"oauth_tokens"."connection_id" is null or "agent_connections"."revoked_at" is null/);
381
+ // Both "last used" marks are touched: the token's row, and THIS token's
382
+ // agent connection.
383
+ await new Promise((r) => setTimeout(r, 0));
384
+ expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
385
+ expect(captured.set.map((s) => Object.keys(s))).toEqual([['lastUsedAt'], ['lastUsedAt']]);
386
+ expect(render(captured.where[1])).toMatchObject({ params: ['tok-row-1'] });
387
+ expect(render(captured.where[2])).toMatchObject({ params: ['conn-1'] });
388
+ expect(render(captured.where[2]).sql).toMatch(/"agent_connections"."id" = \$1/);
389
+ // The connection's stamp is throttled: a verify a moment later touches
390
+ // the token row again but not the connection — a minute has not passed.
391
+ await provider.verifyAccessToken('bevel-mcp_live');
392
+ await new Promise((r) => setTimeout(r, 0));
393
+ expect(captured.updateTargets).toEqual([oauthTokens, agentConnections, oauthTokens]);
394
+ });
395
+
396
+ it('revokeToken (RFC 7009) ends the agent connection once no live token remains on it, as the owner\'s doing', async () => {
397
+ const { provider, captured } = makeProvider([
398
+ [{ connectionId: 'conn-1' }] /* the revoked pair's row */,
399
+ [{ live: 0 }] /* nothing else live on the connection */,
400
+ undefined /* connection close */,
401
+ ]);
402
+ await provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_old', token_type_hint: 'refresh_token' });
403
+ expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
404
+ expect(captured.set[1]).toMatchObject({ revokedBy: 'owner' });
405
+ expect(captured.set[1].revokedAt).toBeInstanceOf(Date);
406
+ expect(render(captured.where[2])).toMatchObject({ params: ['conn-1'] });
407
+ expect(render(captured.where[2]).sql).toMatch(/"agent_connections"."revoked_at" is null/);
408
+
409
+ // Another live pair on the connection (the agent signed in twice): the
410
+ // connection stays.
411
+ const busy = makeProvider([[{ connectionId: 'conn-1' }], [{ live: 1 }]]);
412
+ await busy.provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_other' });
413
+ expect(busy.captured.updateTargets).toEqual([oauthTokens]);
414
+
415
+ // An unknown or foreign token revokes nothing and touches no connection.
416
+ const unknown = makeProvider([[]]);
417
+ await unknown.provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_gone' });
418
+ expect(unknown.captured.updateTargets).toEqual([oauthTokens]);
249
419
  });
250
420
 
251
421
  it('verifyAccessToken throws InvalidTokenError for unknown/revoked/expired tokens', async () => {
@@ -48,6 +48,8 @@ function makeExternalApiKeyService(
48
48
 
49
49
  function makeOAuthProvider(
50
50
  verify?: (t: string) => Promise<{ extra?: Record<string, unknown> }>,
51
+ /** Whether an agent connection still admits requests — true unless a test says otherwise. */
52
+ live?: (connectionId: string) => Promise<boolean>,
51
53
  ) {
52
54
  return {
53
55
  looksLikeAccessToken: (t: string) => typeof t === 'string' && t.startsWith('bevel-mcp_'),
@@ -57,6 +59,8 @@ function makeOAuthProvider(
57
59
  throw new InvalidTokenError('unknown token');
58
60
  }),
59
61
  ),
62
+ isConnectionLive: vi.fn(live ?? (async () => true)),
63
+ noteConnectionUse: vi.fn(),
60
64
  } as unknown as BevelOAuthProvider;
61
65
  }
62
66
 
@@ -179,7 +183,7 @@ describe('createMcpAuthMiddleware', () => {
179
183
  const auth = makeAuthService();
180
184
  const externalApiKeys = makeExternalApiKeyService();
181
185
  const oauth = makeOAuthProvider(async () => ({
182
- extra: { userId: 'user-5', userEmail: 'eve@example.com' },
186
+ extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: 'conn-5' },
183
187
  }));
184
188
  const mw = makeMw({ auth, keys: externalApiKeys, oauth });
185
189
  const { req, res, next } = makeReqRes('Bearer bevel-mcp_token123');
@@ -188,14 +192,28 @@ describe('createMcpAuthMiddleware', () => {
188
192
 
189
193
  expect(req.userId).toBe('user-5');
190
194
  expect(req.userEmail).toBe('eve@example.com');
191
- // OAuth sessions are unmetered like JWT sessions — no connection key id.
195
+ // OAuth sessions are unmetered like JWT sessions — no connection key id…
192
196
  expect(req.externalApiKeyId).toBeUndefined();
197
+ // …but they ARE attributed to the agent behind the grant, for the Audit log.
198
+ expect(req.agentConnectionId).toBe('conn-5');
193
199
  expect(next).toHaveBeenCalled();
194
200
  // Neither the connection-key nor the JWT path may see this token shape.
195
201
  expect((externalApiKeys as any).verifyAndLoadToken).not.toHaveBeenCalled();
196
202
  expect((auth as any).verifyToken).not.toHaveBeenCalled();
197
203
  });
198
204
 
205
+ it('binds no agent for an OAuth token minted before connections existed, and still admits it', async () => {
206
+ const oauth = makeOAuthProvider(async () => ({
207
+ extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: null },
208
+ }));
209
+ const mw = makeMw({ oauth });
210
+ const { req, res, next } = makeReqRes('Bearer bevel-mcp_token123');
211
+ await mw(req, res, next);
212
+ expect(req.userId).toBe('user-5');
213
+ expect(req.agentConnectionId).toBeUndefined();
214
+ expect(next).toHaveBeenCalled();
215
+ });
216
+
199
217
  it('401s with the discovery challenge when the OAuth token is invalid/expired/revoked', async () => {
200
218
  const mw = makeMw();
201
219
  const { req, res, next, setHeader, status } = makeReqRes('Bearer bevel-mcp_expired');
@@ -276,6 +294,46 @@ describe('createMcpAuthMiddleware', () => {
276
294
  expect(next).toHaveBeenCalled();
277
295
  expect(req.userId).toBe('user-7');
278
296
  expect(req.userEmail).toBe('seven@example.com');
297
+ // A plain loopback identity names no agent.
298
+ expect(req.agentConnectionId).toBeUndefined();
299
+ });
300
+
301
+ it('carries the agent connection of an exchanged grant, so the local server stays attributed', async () => {
302
+ const token = internal.mint({ userId: 'user-7', externalProxy: true, connectionId: 'conn-7' }, 60_000);
303
+ const auth = makeAuthService();
304
+ (auth.getUserById as ReturnType<typeof vi.fn>) = vi.fn(async () => ({
305
+ id: 'user-7',
306
+ email: 'seven@example.com',
307
+ }));
308
+ const oauth = makeOAuthProvider();
309
+ const mw = makeMw({ internal, auth, oauth });
310
+ const { req, res, next } = makeReqRes(`Bearer ${token}`);
311
+ await mw(req, res, next);
312
+ expect(next).toHaveBeenCalled();
313
+ expect(req.agentConnectionId).toBe('conn-7');
314
+ // Every request through the exchanged grant asks whether the
315
+ // connection still admits one, and counts as a use of it.
316
+ expect((oauth as any).isConnectionLive).toHaveBeenCalledWith('conn-7');
317
+ expect((oauth as any).noteConnectionUse).toHaveBeenCalledWith('conn-7');
318
+ });
319
+
320
+ it('401s an exchanged grant whose agent connection was revoked — the token is stateless, the connection is not', async () => {
321
+ const token = internal.mint({ userId: 'user-7', externalProxy: true, connectionId: 'conn-7' }, 60_000);
322
+ const auth = makeAuthService();
323
+ (auth.getUserById as ReturnType<typeof vi.fn>) = vi.fn(async () => ({
324
+ id: 'user-7',
325
+ email: 'seven@example.com',
326
+ }));
327
+ const oauth = makeOAuthProvider(undefined, async () => false);
328
+ const mw = makeMw({ internal, auth, oauth });
329
+ const { req, res, next, status, setHeader } = makeReqRes(`Bearer ${token}`);
330
+ await mw(req, res, next);
331
+ expect(next).not.toHaveBeenCalled();
332
+ expect(status).toHaveBeenCalledWith(401);
333
+ expect(setHeader).toHaveBeenCalledWith('WWW-Authenticate', expect.stringContaining('resource_metadata'));
334
+ expect(req.agentConnectionId).toBeUndefined();
335
+ // A refused request is no use of the connection.
336
+ expect((oauth as any).noteConnectionUse).not.toHaveBeenCalled();
279
337
  });
280
338
 
281
339
  it('401s an expired internal token', async () => {
@@ -74,7 +74,7 @@ async function exchange(baseUrl: string, authorization?: string): Promise<Respon
74
74
  describe('POST /mcp/local-token', () => {
75
75
  it('exchanges a valid OAuth access token for an internal token with the loopback identity + TTL', async () => {
76
76
  const oauth = makeOAuthProvider(async () => ({
77
- extra: { userId: 'user-5', userEmail: 'eve@example.com' },
77
+ extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: 'conn-9' },
78
78
  // A grant with plenty of life left: the loopback constant is the binding cap.
79
79
  expiresAt: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
80
80
  }));
@@ -88,21 +88,41 @@ describe('POST /mcp/local-token', () => {
88
88
  // above outlives it, so the constant is the cap that binds.
89
89
  expect(body.expiresInMs).toBe(MCP_LOOPBACK_TOKEN_TTL_MS);
90
90
  // The minted token verifies to the resolved user with the externalProxy
91
- // flag — identical shape to the hosted session's loopback bearer.
92
- expect(internalTokens.verify(body.token)).toEqual({ userId: 'user-5', externalProxy: true });
91
+ // flag — identical shape to the hosted session's loopback bearer — plus
92
+ // the grant's agent connection, so the local server's calls stay
93
+ // attributed to that agent in the Audit log.
94
+ expect(internalTokens.verify(body.token)).toEqual({
95
+ userId: 'user-5',
96
+ externalProxy: true,
97
+ connectionId: 'conn-9',
98
+ });
93
99
  // …and the tool-auth verifier resolves it to source 'external', exactly
94
100
  // how /api/agent/* will treat the local server.
95
101
  const verify = createTokenVerifier(
96
102
  { looksLikeExternalApiKey: () => false } as unknown as IExternalApiKeyService,
97
103
  internalTokens,
98
104
  );
105
+ // …carrying the agent connection, which the REST recorder attributes
106
+ // the local server's direct tool calls to.
99
107
  await expect(verify(body.token)).resolves.toEqual({
100
108
  ok: true,
101
- auth: { source: 'external', userId: 'user-5', scope: 'write' },
109
+ auth: { source: 'external', userId: 'user-5', connectionId: 'conn-9', scope: 'write' },
102
110
  });
103
111
  expect((oauth as any).verifyAccessToken).toHaveBeenCalledWith('bevel-mcp_valid123');
104
112
  });
105
113
 
114
+ it('mints without a connection claim for a grant that carries none (a token from before connections existed)', async () => {
115
+ const oauth = makeOAuthProvider(async () => ({
116
+ extra: { userId: 'user-5', userEmail: 'eve@example.com' },
117
+ expiresAt: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
118
+ }));
119
+ const { baseUrl, internalTokens } = await mount(oauth);
120
+ const res = await exchange(baseUrl, 'Bearer bevel-mcp_valid123');
121
+ expect(res.status).toBe(200);
122
+ const body = (await res.json()) as { token: string };
123
+ expect(internalTokens.verify(body.token)).toEqual({ userId: 'user-5', externalProxy: true });
124
+ });
125
+
106
126
  /**
107
127
  * The binding that keeps the exchange from OUTLIVING its grant: an access
108
128
  * token with less life left than the loopback constant caps the minted
@@ -108,7 +108,7 @@ describe('POST /mcp (stateless)', () => {
108
108
  expect((await messagesOf(res))[0].result.serverInfo.name).toBe('stub');
109
109
  // The messages reach the service, so it can tell an initialize apart.
110
110
  expect(mcpService.createRequestServer).toHaveBeenCalledWith(
111
- { userId: 'user-A', tokenId: null, bearer: '' },
111
+ { userId: 'user-A', tokenId: null, connectionId: null, bearer: '' },
112
112
  INITIALIZE,
113
113
  );
114
114
  });
@@ -12,6 +12,7 @@ import { createManualRoutes } from '../../tool-registry/manual.routes.js';
12
12
  import { ToolRegistry } from '../../tool-registry/tool-registry.js';
13
13
  import { toolDef } from '../../tool-helpers/tool-def.js';
14
14
  import { PLATFORM_HEADER, TOOL_PREFIX_LINE } from '../../agent-instructions/index.js';
15
+ import type { AgentEventInput, IAgentEventRecorder } from '../../audit/audit.contract.js';
15
16
 
16
17
  /**
17
18
  * End-to-end proxy test: a real express app serving the registry-driven tool
@@ -43,6 +44,10 @@ async function setup(deps?: {
43
44
  readAgentPreamble?: () => Promise<string | null>;
44
45
  /** Extra echo tools to register under these names (the four KB tools, in the prefix tests). */
45
46
  extraTools?: string[];
47
+ /** The Audit log's recorder, when a test watches what gets recorded. */
48
+ auditRecorder?: IAgentEventRecorder;
49
+ /** The agent connection an OAuth caller arrived through (with `tokenId: null`). */
50
+ connectionId?: string | null;
46
51
  }) {
47
52
  const registry = new ToolRegistry();
48
53
  registry.registerExternalTool(
@@ -91,6 +96,16 @@ async function setup(deps?: {
91
96
  res.json({ text: `echo: ${b.prompt}`, sessionId: typeof b.sessionId === 'string' ? b.sessionId : 'new-sess' });
92
97
  });
93
98
  app.post('/api/agent/tools/boom', (_req, res) => res.status(500).json({ error: 'kaboom' }));
99
+ // The two the Audit log's skill classification reads through: a catalog of
100
+ // one skill, and a file read that answers for any path (registered as
101
+ // tools only by the tests that need them, via `extraTools`).
102
+ app.post('/api/agent/tools/list_skills', (_req, res) =>
103
+ res.json({ skills: [{ name: 'rfi', description: 'RFI answers', path: 'Plugins/Sales/rfi' }] }),
104
+ );
105
+ app.post('/api/agent/tools/read_file', (req, res) => {
106
+ const b = (req.body ?? {}) as { path?: string };
107
+ res.json({ content: `contents of ${b.path}` });
108
+ });
94
109
  // A live UTCP manual a catalog entry can point at — one tool, dispatched back
95
110
  // to the echo above. Mounted always (it costs one route) so a test can add a
96
111
  // MANUAL to the served catalog, not merely a tool to the registry: what a
@@ -149,10 +164,11 @@ async function setup(deps?: {
149
164
  deps?.toolManuals as any,
150
165
  undefined, // internalTokens — the fake loopback here accepts any bearer
151
166
  deps?.revokeOAuthAccess,
167
+ deps?.auditRecorder,
152
168
  );
153
169
  lastService = mcp;
154
170
  const tokenId = deps?.tokenId === undefined ? 'tok-1' : deps.tokenId;
155
- return connectClient(mcp, tokenId);
171
+ return connectClient(mcp, tokenId, deps?.connectionId ?? null);
156
172
  }
157
173
 
158
174
  /** The service `setup` built last — for a test that needs a SECOND request against the same service. */
@@ -165,9 +181,9 @@ let lastService: McpService | undefined;
165
181
  * The initialize message is passed so the server carries instructions,
166
182
  * exactly as the route's initialize request would.
167
183
  */
168
- async function connectClient(mcp: McpService, tokenId: string | null): Promise<Client> {
184
+ async function connectClient(mcp: McpService, tokenId: string | null, connectionId: string | null = null): Promise<Client> {
169
185
  const server = await mcp.createRequestServer(
170
- { userId: 'user-A', tokenId, bearer: 'bevel_testkey' },
186
+ { userId: 'user-A', tokenId, connectionId, bearer: 'bevel_testkey' },
171
187
  { jsonrpc: '2.0', id: 0, method: 'initialize', params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'test-client', version: '0.0.0' } } },
172
188
  );
173
189
 
@@ -680,3 +696,104 @@ describe('McpService — a collision ends when its sibling is renamed away', ()
680
696
  expect(survivor.some((m) => m.startsWith('[mcp] skipping manual "notion-eu": ') && !m.includes('rewrites to'))).toBe(true);
681
697
  });
682
698
  });
699
+
700
+ describe('McpService — the Audit log records what an agent calls', () => {
701
+ /** A recorder that keeps every event, and the same thing as the proxy's collaborator type. */
702
+ function spyRecorder(): IAgentEventRecorder & { events: AgentEventInput[] } {
703
+ const events: AgentEventInput[] = [];
704
+ return { events, record: (e) => void events.push(e) };
705
+ }
706
+ const text = (res: Awaited<ReturnType<Client['callTool']>>) => (res.content as Array<{ text: string }>)[0].text;
707
+ // Recording is fire-and-forget behind the call; give the microtasks a turn.
708
+ const settle = () => new Promise((r) => setTimeout(r, 10));
709
+
710
+ it('records a direct platform tool call as a hexis capability under the connection key, with its outcome', async () => {
711
+ const recorder = spyRecorder();
712
+ const client = await setup({ auditRecorder: recorder });
713
+
714
+ await client.callTool({ name: 'ask', arguments: { body: { prompt: 'hello' } } });
715
+ await client.callTool({ name: 'boom', arguments: {} });
716
+ await settle();
717
+
718
+ expect(recorder.events).toHaveLength(2);
719
+ expect(recorder.events[0]).toMatchObject({
720
+ userId: 'user-A',
721
+ principal: { kind: 'key', id: 'tok-1' },
722
+ kind: 'capability',
723
+ manual: null,
724
+ name: 'ask',
725
+ outcome: 'ok',
726
+ });
727
+ expect(typeof recorder.events[0].durationMs).toBe('number');
728
+ // The tool answered with an error result — recorded as such, not as ok.
729
+ expect(recorder.events[1]).toMatchObject({ kind: 'capability', name: 'boom', outcome: 'error' });
730
+ });
731
+
732
+ it('records a chain as a capability AND each call made inside it', async () => {
733
+ const recorder = spyRecorder();
734
+ const client = await setup({ auditRecorder: recorder });
735
+
736
+ await client.callTool({
737
+ name: 'call_tool_chain',
738
+ arguments: { code: "KNOWLEDGE_BASE.ask({ body: { prompt: 'one' } }); return KNOWLEDGE_BASE.ask({ body: { prompt: 'two' } });" },
739
+ });
740
+ await settle();
741
+
742
+ const names = recorder.events.map((e) => `${e.kind}:${e.name}:${e.outcome}`);
743
+ // The two inner calls finish (and are recorded) before the chain itself.
744
+ expect(names).toEqual(['capability:ask:ok', 'capability:ask:ok', 'capability:call_tool_chain:ok']);
745
+ });
746
+
747
+ it('attributes an OAuth caller to its agent connection, and records nothing for a caller with neither', async () => {
748
+ const asAgent = spyRecorder();
749
+ const agent = await setup({ auditRecorder: asAgent, tokenId: null, connectionId: 'conn-1' });
750
+ await agent.callTool({ name: 'ask', arguments: { body: { prompt: 'hi' } } });
751
+ await settle();
752
+ expect(asAgent.events[0]).toMatchObject({ principal: { kind: 'agent', id: 'conn-1' }, name: 'ask' });
753
+
754
+ const nobody = spyRecorder();
755
+ const jwt = await setup({ auditRecorder: nobody, tokenId: null, connectionId: null });
756
+ await jwt.callTool({ name: 'ask', arguments: { body: { prompt: 'hi' } } });
757
+ await settle();
758
+ expect(nobody.events).toEqual([]);
759
+ });
760
+
761
+ it('records a read inside a skill folder as that skill, and a read elsewhere as the read', async () => {
762
+ const recorder = spyRecorder();
763
+ const client = await setup({ auditRecorder: recorder, extraTools: ['read_file', 'list_skills'] });
764
+
765
+ await client.callTool({ name: 'read_file', arguments: { body: { path: 'Plugins/Sales/rfi/SKILL.md' } } });
766
+ await client.callTool({ name: 'read_file', arguments: { body: { path: 'KnowledgeBase/Product/roadmap.md' } } });
767
+ await settle();
768
+
769
+ expect(recorder.events[0]).toMatchObject({ kind: 'skill', manual: 'Plugins/Sales/rfi', name: 'rfi', outcome: 'ok' });
770
+ expect(recorder.events[1]).toMatchObject({ kind: 'capability', manual: null, name: 'read_file', outcome: 'ok' });
771
+ });
772
+
773
+ it('records a prompt read as a skill, with an error outcome when the skill is unknown', async () => {
774
+ const recorder = spyRecorder();
775
+ const client = await setup({ auditRecorder: recorder });
776
+ // The harness serves no get_skill endpoint, so every prompt is unknown here.
777
+ await expect(client.getPrompt({ name: 'rfi' })).rejects.toThrow(/Unknown skill/);
778
+ await settle();
779
+ expect(recorder.events[0]).toMatchObject({ kind: 'skill', name: 'rfi', outcome: 'error' });
780
+ });
781
+
782
+ it('records a call refused for a missing sign-in as denied, without running the tool', async () => {
783
+ const recorder = spyRecorder();
784
+ const vault = { statusFor: async (_u: string, keys: string[]) => keys.map((key) => ({ key, adminConfigured: false, userConfigured: false })) };
785
+ const manuals = {
786
+ userScopedKeysForManual: async (manual: string) =>
787
+ manual === 'KNOWLEDGE_BASE' ? [{ key: 'KNOWLEDGE_BASE_API_KEY', name: 'API_KEY', label: 'Your API key', oauth: false }] : [],
788
+ };
789
+ const client = await setup({ auditRecorder: recorder, secretsVault: vault, toolManuals: manuals });
790
+
791
+ const res = await client.callTool({ name: 'ask', arguments: { body: { prompt: 'hello' } } });
792
+ await settle();
793
+
794
+ expect(text(res)).not.toMatch(/echo:/);
795
+ expect(recorder.events).toEqual([
796
+ expect.objectContaining({ kind: 'capability', name: 'ask', outcome: 'denied', durationMs: null }),
797
+ ]);
798
+ });
799
+ });