@bevel-software/platform-core-backend 0.20.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 (576) 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 +23 -105
  11. package/dist/core/create-core-server.js.map +1 -1
  12. package/dist/core/create-core-services.d.ts +30 -5
  13. package/dist/core/create-core-services.d.ts.map +1 -1
  14. package/dist/core/create-core-services.js +136 -81
  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/auth/account-admission.d.ts +40 -0
  53. package/dist/modules/auth/account-admission.d.ts.map +1 -0
  54. package/dist/modules/auth/account-admission.js +17 -0
  55. package/dist/modules/auth/account-admission.js.map +1 -0
  56. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  57. package/dist/modules/auth/account.routes.js +4 -1
  58. package/dist/modules/auth/account.routes.js.map +1 -1
  59. package/dist/modules/auth/auth.service.d.ts +19 -1
  60. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  61. package/dist/modules/auth/auth.service.js +33 -1
  62. package/dist/modules/auth/auth.service.js.map +1 -1
  63. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  64. package/dist/modules/auth/oidc-auth-provider.js +9 -0
  65. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  66. package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
  67. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  68. package/dist/modules/connection-probe/connection-probe.service.js +10 -2
  69. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  70. package/dist/modules/database/advisory-lock.d.ts +17 -0
  71. package/dist/modules/database/advisory-lock.d.ts.map +1 -1
  72. package/dist/modules/database/advisory-lock.js +24 -3
  73. package/dist/modules/database/advisory-lock.js.map +1 -1
  74. package/dist/modules/database/connection.d.ts +67 -2
  75. package/dist/modules/database/connection.d.ts.map +1 -1
  76. package/dist/modules/database/connection.js +118 -12
  77. package/dist/modules/database/connection.js.map +1 -1
  78. package/dist/modules/database/migrate.d.ts +1 -1
  79. package/dist/modules/database/migrate.d.ts.map +1 -1
  80. package/dist/modules/database/migrate.js +27 -3
  81. package/dist/modules/database/migrate.js.map +1 -1
  82. package/dist/modules/kb-fs/branch-name.d.ts +0 -1
  83. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  84. package/dist/modules/kb-fs/branch-name.js +3 -3
  85. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  86. package/dist/modules/kb-fs/clone-config.d.ts +9 -8
  87. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
  88. package/dist/modules/kb-fs/clone-config.js +14 -12
  89. package/dist/modules/kb-fs/clone-config.js.map +1 -1
  90. package/dist/modules/mcp/mcp.service.d.ts +7 -0
  91. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  92. package/dist/modules/mcp/mcp.service.js +4 -4
  93. package/dist/modules/mcp/mcp.service.js.map +1 -1
  94. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
  95. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  96. package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
  97. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  98. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
  99. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  100. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
  101. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  102. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
  103. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  104. package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
  105. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  106. package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
  107. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  108. package/dist/modules/plugins/discovery/native.source.js +4 -2
  109. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  110. package/dist/modules/plugins/index.d.ts +1 -1
  111. package/dist/modules/plugins/index.d.ts.map +1 -1
  112. package/dist/modules/plugins/index.js +1 -1
  113. package/dist/modules/plugins/index.js.map +1 -1
  114. package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
  115. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  116. package/dist/modules/plugins/join-request-jobs.service.js +6 -6
  117. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  118. package/dist/modules/plugins/join-requests.service.d.ts +3 -1
  119. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  120. package/dist/modules/plugins/join-requests.service.js +9 -8
  121. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  122. package/dist/modules/plugins/plugin-links.d.ts +65 -10
  123. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  124. package/dist/modules/plugins/plugin-links.js +136 -31
  125. package/dist/modules/plugins/plugin-links.js.map +1 -1
  126. package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
  127. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  128. package/dist/modules/plugins/plugin-links.service.js +151 -21
  129. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  130. package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
  131. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  132. package/dist/modules/plugins/plugin-provision.service.js +33 -26
  133. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  134. package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
  135. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  136. package/dist/modules/plugins/plugin-rename.service.js +10 -12
  137. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  138. package/dist/modules/plugins/plugins.routes.d.ts +3 -0
  139. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  140. package/dist/modules/plugins/plugins.routes.js +45 -15
  141. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  142. package/dist/modules/plugins/plugins.service.d.ts +4 -4
  143. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  144. package/dist/modules/plugins/plugins.service.js +7 -10
  145. package/dist/modules/plugins/plugins.service.js.map +1 -1
  146. package/dist/modules/plugins/teams.routes.d.ts +4 -1
  147. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  148. package/dist/modules/plugins/teams.routes.js +11 -9
  149. package/dist/modules/plugins/teams.routes.js.map +1 -1
  150. package/dist/modules/secrets-vault/index.d.ts +1 -1
  151. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  152. package/dist/modules/secrets-vault/index.js +1 -1
  153. package/dist/modules/secrets-vault/index.js.map +1 -1
  154. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
  155. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  156. package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
  157. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  158. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
  159. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  160. package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
  161. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  162. package/dist/modules/settings/deployment-settings.service.d.ts +12 -8
  163. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  164. package/dist/modules/settings/deployment-settings.service.js +20 -22
  165. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  166. package/dist/modules/settings/setup.routes.d.ts +9 -2
  167. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  168. package/dist/modules/settings/setup.routes.js +20 -14
  169. package/dist/modules/settings/setup.routes.js.map +1 -1
  170. package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
  171. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
  172. package/dist/modules/skills/allowed-tools-check.js +8 -8
  173. package/dist/modules/skills/allowed-tools-check.js.map +1 -1
  174. package/dist/modules/skills/pending-skills.service.d.ts +3 -1
  175. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  176. package/dist/modules/skills/pending-skills.service.js +9 -6
  177. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  178. package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -1
  179. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  180. package/dist/modules/skills/skill-access-requests.routes.js +6 -6
  181. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  182. package/dist/modules/skills/skills.routes.d.ts +4 -1
  183. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  184. package/dist/modules/skills/skills.routes.js +4 -4
  185. package/dist/modules/skills/skills.routes.js.map +1 -1
  186. package/dist/modules/skills/skills.service.d.ts +4 -2
  187. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  188. package/dist/modules/skills/skills.service.js +13 -11
  189. package/dist/modules/skills/skills.service.js.map +1 -1
  190. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
  191. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  192. package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
  193. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  194. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
  195. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  196. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
  197. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  198. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
  199. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  200. package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
  201. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  202. package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
  203. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
  204. package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
  205. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  206. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
  207. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  208. package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
  209. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  210. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
  211. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  212. package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
  213. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  214. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
  215. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  216. package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
  217. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  218. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
  219. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  220. package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -9
  221. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  222. package/dist/modules/workflow/git/git.service.d.ts +11 -5
  223. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  224. package/dist/modules/workflow/git/git.service.js +23 -14
  225. package/dist/modules/workflow/git/git.service.js.map +1 -1
  226. package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
  227. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  228. package/dist/modules/workflow/git/node-git-runner.js +35 -5
  229. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  230. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  231. package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
  232. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  233. package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
  234. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
  235. package/dist/modules/workflow/session-ontology.service.js +6 -2
  236. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  237. package/dist/modules/workflow/workflow.service.d.ts +8 -3
  238. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  239. package/dist/modules/workflow/workflow.service.js +33 -23
  240. package/dist/modules/workflow/workflow.service.js.map +1 -1
  241. package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
  242. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  243. package/dist/modules/workspace/session-ontology.gate.js +3 -3
  244. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  245. package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
  246. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  247. package/dist/modules/workspace/startup/kb-git.js +15 -13
  248. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  249. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +5 -8
  250. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  251. package/dist/modules/workspace/startup/kb-startup-runner.js +35 -38
  252. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  253. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
  254. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  255. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
  256. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  257. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
  258. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
  259. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
  260. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
  261. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
  262. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
  263. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
  264. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
  265. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  266. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  267. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
  268. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  269. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
  270. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  271. package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
  272. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  273. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
  274. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  275. package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
  276. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  277. package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
  278. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  279. package/dist/modules/workspace/startup/steps/template-source.js +10 -6
  280. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  281. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  282. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  283. package/dist/modules/workspace/workspace.routes.js +8 -7
  284. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  285. package/dist/modules/workspace/workspace.service.d.ts +18 -12
  286. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  287. package/dist/modules/workspace/workspace.service.js +32 -28
  288. package/dist/modules/workspace/workspace.service.js.map +1 -1
  289. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  290. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  291. package/dist/modules/workspace/workspace.tools.js +20 -18
  292. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  293. package/dist/modules/workspace/write-denial.d.ts +3 -2
  294. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  295. package/dist/modules/workspace/write-denial.js +6 -5
  296. package/dist/modules/workspace/write-denial.js.map +1 -1
  297. package/dist/shared/git.contract.d.ts +52 -7
  298. package/dist/shared/git.contract.d.ts.map +1 -1
  299. package/dist/shared/git.contract.js +30 -8
  300. package/dist/shared/git.contract.js.map +1 -1
  301. package/dist/shared/kb-context.d.ts +76 -0
  302. package/dist/shared/kb-context.d.ts.map +1 -0
  303. package/dist/shared/kb-context.js +108 -0
  304. package/dist/shared/kb-context.js.map +1 -0
  305. package/dist/shared/kb-layout.d.ts +3 -2
  306. package/dist/shared/kb-layout.d.ts.map +1 -1
  307. package/dist/shared/kb-layout.js +4 -3
  308. package/dist/shared/kb-layout.js.map +1 -1
  309. package/dist/shared/kb-layout.test.js +35 -27
  310. package/dist/shared/kb-layout.test.js.map +1 -1
  311. package/dist/shared/pending-proposals.d.ts +4 -1
  312. package/dist/shared/pending-proposals.d.ts.map +1 -1
  313. package/dist/shared/pending-proposals.js +3 -4
  314. package/dist/shared/pending-proposals.js.map +1 -1
  315. package/dist/shared/redact-secret.d.ts +5 -5
  316. package/dist/shared/redact-secret.js +5 -5
  317. package/dist/tenancy/index.d.ts +13 -0
  318. package/dist/tenancy/index.d.ts.map +1 -0
  319. package/dist/tenancy/index.js +13 -0
  320. package/dist/tenancy/index.js.map +1 -0
  321. package/dist/tenancy/static-tenant-source.d.ts +98 -0
  322. package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
  323. package/dist/tenancy/static-tenant-source.js +220 -0
  324. package/dist/tenancy/static-tenant-source.js.map +1 -0
  325. package/dist/tenancy/tenant-host.d.ts +80 -0
  326. package/dist/tenancy/tenant-host.d.ts.map +1 -0
  327. package/dist/tenancy/tenant-host.js +217 -0
  328. package/dist/tenancy/tenant-host.js.map +1 -0
  329. package/dist/tenancy/tenant-runtime.d.ts +91 -0
  330. package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
  331. package/dist/tenancy/tenant-runtime.js +174 -0
  332. package/dist/tenancy/tenant-runtime.js.map +1 -0
  333. package/dist/tenancy/tenant-secrets.d.ts +24 -0
  334. package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
  335. package/dist/tenancy/tenant-secrets.js +30 -0
  336. package/dist/tenancy/tenant-secrets.js.map +1 -0
  337. package/dist/tenancy/tenant-source.contract.d.ts +45 -0
  338. package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
  339. package/dist/tenancy/tenant-source.contract.js +21 -0
  340. package/dist/tenancy/tenant-source.contract.js.map +1 -0
  341. package/migrations/0000_core_init.sql +14 -14
  342. package/migrations/0003_deployment_settings.sql +1 -1
  343. package/migrations/0005_github_facade.sql +1 -1
  344. package/migrations/0012_agent_audit.sql +6 -6
  345. package/package.json +3 -3
  346. package/src/__tests__/branch-model.test.ts +7 -6
  347. package/src/__tests__/kb-context.ts +30 -0
  348. package/src/__tests__/kb-layout-config.test.ts +35 -30
  349. package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
  350. package/src/core/__tests__/lifecycle.test.ts +78 -1
  351. package/src/core/__tests__/public-config.test.ts +5 -1
  352. package/src/core/catalog-cache-invalidation.ts +16 -11
  353. package/src/core/core-ports.ts +19 -0
  354. package/src/core/create-core-server.ts +27 -106
  355. package/src/core/create-core-services.ts +180 -95
  356. package/src/core/lifecycle.ts +214 -29
  357. package/src/core/public-config.ts +8 -5
  358. package/src/core-config.ts +107 -8
  359. package/src/index.ts +25 -2
  360. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
  361. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
  362. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
  363. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
  364. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  365. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
  366. package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
  367. package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
  368. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
  369. package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
  370. package/src/modules/access/__tests__/creator-access.test.ts +3 -2
  371. package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
  372. package/src/modules/access/access-control.service.ts +20 -6
  373. package/src/modules/access/access.routes.ts +10 -13
  374. package/src/modules/access/change-read-gate.ts +10 -4
  375. package/src/modules/access/creator-access.ts +7 -2
  376. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
  377. package/src/modules/agent-instructions/read-preamble.ts +10 -12
  378. package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
  379. package/src/modules/auth/__tests__/auth.service.test.ts +60 -0
  380. package/src/modules/auth/account-admission.ts +44 -0
  381. package/src/modules/auth/account.routes.ts +4 -1
  382. package/src/modules/auth/auth.service.ts +33 -1
  383. package/src/modules/auth/oidc-auth-provider.ts +9 -0
  384. package/src/modules/connection-probe/connection-probe.service.ts +7 -1
  385. package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
  386. package/src/modules/database/__tests__/connection.test.ts +98 -0
  387. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
  388. package/src/modules/database/advisory-lock.ts +28 -4
  389. package/src/modules/database/connection.ts +143 -13
  390. package/src/modules/database/migrate.ts +48 -14
  391. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  392. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  393. package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
  394. package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
  395. package/src/modules/kb-fs/branch-name.ts +3 -3
  396. package/src/modules/kb-fs/clone-config.ts +15 -12
  397. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
  398. package/src/modules/mcp/mcp.service.ts +11 -4
  399. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
  400. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
  401. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
  402. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
  403. package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -1
  404. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
  405. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
  406. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
  407. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
  408. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
  409. package/src/modules/plugins/__tests__/plugins.routes.test.ts +11 -2
  410. package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
  411. package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
  412. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
  413. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
  414. package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
  415. package/src/modules/plugins/discovery/native.source.ts +4 -1
  416. package/src/modules/plugins/index.ts +1 -1
  417. package/src/modules/plugins/join-request-jobs.service.ts +8 -7
  418. package/src/modules/plugins/join-requests.service.ts +7 -7
  419. package/src/modules/plugins/plugin-links.service.ts +192 -19
  420. package/src/modules/plugins/plugin-links.ts +147 -27
  421. package/src/modules/plugins/plugin-provision.service.ts +54 -27
  422. package/src/modules/plugins/plugin-rename.service.ts +10 -10
  423. package/src/modules/plugins/plugins.routes.ts +45 -15
  424. package/src/modules/plugins/plugins.service.ts +7 -11
  425. package/src/modules/plugins/teams.routes.ts +16 -8
  426. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
  427. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
  428. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
  429. package/src/modules/secrets-vault/index.ts +2 -0
  430. package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
  431. package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
  432. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +24 -11
  433. package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
  434. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
  435. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
  436. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
  437. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
  438. package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
  439. package/src/modules/settings/deployment-settings.service.ts +21 -20
  440. package/src/modules/settings/setup.routes.ts +26 -16
  441. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
  442. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
  443. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
  444. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +2 -1
  445. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
  446. package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
  447. package/src/modules/skills/allowed-tools-check.ts +7 -6
  448. package/src/modules/skills/pending-skills.service.ts +9 -8
  449. package/src/modules/skills/skill-access-requests.routes.ts +7 -7
  450. package/src/modules/skills/skills.routes.ts +4 -3
  451. package/src/modules/skills/skills.service.ts +13 -9
  452. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
  453. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
  454. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
  455. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  456. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
  457. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
  458. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
  459. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
  460. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
  461. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
  462. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
  463. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
  464. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
  465. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
  466. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
  467. package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
  468. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
  469. package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
  470. package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
  471. package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
  472. package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
  473. package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
  474. package/src/modules/workflow/__tests__/apply-failure.test.ts +2 -1
  475. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
  476. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
  477. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
  478. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
  479. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
  480. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
  481. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
  482. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +28 -27
  483. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
  484. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
  485. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
  486. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
  487. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
  488. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +2 -1
  489. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +2 -1
  490. package/src/modules/workflow/agent-tools/workflow.tools.ts +11 -9
  491. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
  492. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
  493. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
  494. package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
  495. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
  496. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
  497. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
  498. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
  499. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
  500. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
  501. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
  502. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
  503. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
  504. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
  505. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
  506. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
  507. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
  508. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
  509. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +9 -8
  510. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
  511. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
  512. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
  513. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
  514. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
  515. package/src/modules/workflow/git/git.service.ts +24 -13
  516. package/src/modules/workflow/git/node-git-runner.ts +37 -4
  517. package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
  518. package/src/modules/workflow/session-ontology.service.ts +7 -2
  519. package/src/modules/workflow/workflow.service.ts +31 -24
  520. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
  521. package/src/modules/workspace/__tests__/git-internals.security.test.ts +10 -9
  522. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
  523. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
  524. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
  525. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
  526. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
  527. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
  528. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
  529. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
  530. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
  531. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
  532. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
  533. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  534. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  535. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  536. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  537. package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
  538. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
  539. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
  540. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +4 -3
  541. package/src/modules/workspace/__tests__/workspace.tools.test.ts +9 -8
  542. package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
  543. package/src/modules/workspace/session-ontology.gate.ts +6 -4
  544. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
  545. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +29 -21
  546. package/src/modules/workspace/startup/kb-git.ts +15 -22
  547. package/src/modules/workspace/startup/kb-startup-runner.ts +40 -46
  548. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
  549. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
  550. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
  551. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
  552. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
  553. package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
  554. package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
  555. package/src/modules/workspace/startup/steps/template-source.ts +8 -5
  556. package/src/modules/workspace/workspace.routes.ts +8 -7
  557. package/src/modules/workspace/workspace.service.ts +30 -27
  558. package/src/modules/workspace/workspace.tools.ts +21 -20
  559. package/src/modules/workspace/write-denial.ts +7 -5
  560. package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
  561. package/src/shared/git.contract.ts +70 -8
  562. package/src/shared/kb-context.ts +130 -0
  563. package/src/shared/kb-layout.test.ts +37 -27
  564. package/src/shared/kb-layout.ts +4 -3
  565. package/src/shared/pending-proposals.ts +7 -4
  566. package/src/shared/redact-secret.ts +5 -5
  567. package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
  568. package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
  569. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
  570. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
  571. package/src/tenancy/index.ts +35 -0
  572. package/src/tenancy/static-tenant-source.ts +296 -0
  573. package/src/tenancy/tenant-host.ts +261 -0
  574. package/src/tenancy/tenant-runtime.ts +208 -0
  575. package/src/tenancy/tenant-secrets.ts +41 -0
  576. package/src/tenancy/tenant-source.contract.ts +59 -0
@@ -13,7 +13,8 @@ import {
13
13
  import type { IAccessControl } from '../access/access-control.interface.js';
14
14
  import { WorkflowDomainError } from '../../shared/domain-errors.js';
15
15
  import { domainErrorBody } from '../../shared/http-errors.js';
16
- import { pluginsWorkspaceId, pluginFolderBelowRoot } from './plugins.service.js';
16
+ import { pluginFolderBelowRoot } from './plugins.service.js';
17
+ import type { KbContext } from '../../shared/kb-context.js';
17
18
  import { PluginProvisionError, type PluginProvisionService } from './plugin-provision.service.js';
18
19
  import { PluginLinkError, type PluginLinksService } from './plugin-links.service.js';
19
20
  import { PluginRenameError, type PluginRenameService } from './plugin-rename.service.js';
@@ -159,6 +160,8 @@ export function createPluginsRoutes(
159
160
  joinRequestJobs: PluginJoinRequestJobs,
160
161
  provision: PluginProvisionService,
161
162
  resolveUser: (req: express.Request) => Promise<AuthUser | null>,
163
+ /** The released branch's clone — every verdict here is read from it. */
164
+ kb: Pick<KbContext, 'defaultWorkspaceId'>,
162
165
  /** Optional: a host without the link machinery simply has no link routes. */
163
166
  links?: PluginLinksService,
164
167
  /** Optional: a host without it has no rename route. */
@@ -209,11 +212,30 @@ export function createPluginsRoutes(
209
212
  * POST /api/plugins/:name/links { skillPath } → { root, skills }
210
213
  * DELETE /api/plugins/:name/links?skillPath= → { root, revoked }
211
214
  * POST /api/plugins/:name/links/repair { skillPath } → { root }
215
+ * POST /api/plugins/:name/links/repair-all → { repaired, skipped }
212
216
  *
213
217
  * Refusals carry a `kind` the UI branches on — `needs-skill-write` (409) is
214
218
  * the one that becomes "request write access".
219
+ *
220
+ * `repair-all` is the plugin PAGE's: it repairs every link of the plugin the
221
+ * caller can, and reports what it could not. A caller who may not write the
222
+ * plugin gets the same 404 every other link route gives them, so probing it
223
+ * confirms nothing about a plugin they cannot see.
215
224
  */
216
225
  if (links) {
226
+ /** The one refusal mapping every link route answers with. */
227
+ const linkFailure = (res: express.Response, err: unknown) => {
228
+ if (err instanceof PluginLinkError) {
229
+ res.status(err.status).json({ error: err.message, ...err.payload });
230
+ return;
231
+ }
232
+ if (err instanceof WorkflowDomainError) {
233
+ res.status(err.status).json(domainErrorBody(err));
234
+ return;
235
+ }
236
+ log.error('link operation failed:', { err });
237
+ res.status(500).json({ error: 'Failed to update the plugin\'s links' });
238
+ };
217
239
  const linkOp = async (
218
240
  req: express.Request,
219
241
  res: express.Response,
@@ -237,16 +259,7 @@ export function createPluginsRoutes(
237
259
  try {
238
260
  res.json(await op(user, String(req.params.name), skillPath));
239
261
  } catch (err) {
240
- if (err instanceof PluginLinkError) {
241
- res.status(err.status).json({ error: err.message, ...err.payload });
242
- return;
243
- }
244
- if (err instanceof WorkflowDomainError) {
245
- res.status(err.status).json(domainErrorBody(err));
246
- return;
247
- }
248
- log.error('link operation failed:', { err });
249
- res.status(500).json({ error: 'Failed to update the plugin\'s links' });
262
+ linkFailure(res, err);
250
263
  }
251
264
  };
252
265
  const bodyPath = (req: express.Request) => ((req.body ?? {}) as { skillPath?: unknown }).skillPath;
@@ -254,6 +267,23 @@ export function createPluginsRoutes(
254
267
  router.post('/plugins/:name/links/repair', (req, res) =>
255
268
  linkOp(req, res, (u, p, s) => links.repair(u, p, s), bodyPath),
256
269
  );
270
+ // No `skillPath`: this one is about the plugin, so it cannot ride `linkOp`.
271
+ router.post('/plugins/:name/links/repair-all', async (req, res) => {
272
+ if (!req.userEmail) {
273
+ res.status(401).json({ error: 'Unauthenticated' });
274
+ return;
275
+ }
276
+ const user = await resolveUser(req);
277
+ if (!user) {
278
+ res.status(401).json({ error: 'Unauthenticated' });
279
+ return;
280
+ }
281
+ try {
282
+ res.json(await links.repairAll(user, String(req.params.name)));
283
+ } catch (err) {
284
+ linkFailure(res, err);
285
+ }
286
+ });
257
287
  router.delete('/plugins/:name/links', (req, res) =>
258
288
  linkOp(req, res, (u, p, s) => links.unlink(u, p, s), (r) => r.query.skillPath),
259
289
  );
@@ -296,7 +326,7 @@ export function createPluginsRoutes(
296
326
  res.json({ plugins: [] });
297
327
  return;
298
328
  }
299
- const wsId = pluginsWorkspaceId();
329
+ const wsId = kb.defaultWorkspaceId();
300
330
  const probes = probesFor(catalog);
301
331
  const [readable, writable, owned] = await Promise.all([
302
332
  accessControl.canReadBatch(wsId, email, probes),
@@ -453,7 +483,7 @@ export function createPluginsRoutes(
453
483
  }
454
484
  // By identity — the manifest name the catalog keys on.
455
485
  const plugin = (await pluginIndex.catalog()).find((g) => g.name === req.params.name);
456
- const wsId = pluginsWorkspaceId();
486
+ const wsId = kb.defaultWorkspaceId();
457
487
  const ownerVerdicts = plugin
458
488
  ? await Promise.all(
459
489
  plugin.folders.map((f) => accessControl.canOwner(wsId, email, memberProbe(f))),
@@ -494,7 +524,7 @@ export function createPluginsRoutes(
494
524
  const catalog = await pluginIndex.catalog();
495
525
  // By identity — the manifest name the catalog keys on.
496
526
  const plugin = catalog.find((g) => g.name === req.params.name);
497
- const wsId = pluginsWorkspaceId();
527
+ const wsId = kb.defaultWorkspaceId();
498
528
  // Same ANY-folder shape `GET /plugins` resolves with, so a plugin can
499
529
  // never be listed as discoverable there and rejected as unknown here.
500
530
  const verdicts = plugin
@@ -567,7 +597,7 @@ export function createPluginsRoutes(
567
597
  return null;
568
598
  }
569
599
  const writable = await accessControl.canWriteBatch(
570
- pluginsWorkspaceId(),
600
+ kb.defaultWorkspaceId(),
571
601
  email,
572
602
  plugin.folders.map(accessMdOf),
573
603
  );
@@ -3,13 +3,10 @@ import { logger } from '../../shared/logging.js';
3
3
 
4
4
  const log = logger('plugins');
5
5
  import path from 'node:path';
6
- import {
7
- DEFAULT_BRANCH,
8
- } from '@bevel-software/platform-shared';
9
6
  import { isPrivateAccessMd } from '../access-model/access-grammar.js';
10
7
  import { isAbsence } from '../../shared/fs.contract.js';
11
8
  import type { WorkspaceService } from '../workspace/workspace.service.js';
12
- import { workspaceIdForBranch } from '../../shared/workspace-id.js';
9
+ import type { KbContext } from '../../shared/kb-context.js';
13
10
  import type { IAccessControl } from '../access/access-control.interface.js';
14
11
  import type { ISkillService } from '../skills/skills.contract.js';
15
12
  import type { IToolManualService } from '../tool-manuals/tool-manuals.contract.js';
@@ -63,7 +60,7 @@ export class PluginIndexService implements IPluginIndexService {
63
60
  private readonly accessControl: IAccessControl,
64
61
  private readonly skillService: ISkillService,
65
62
  private readonly toolManualService: IToolManualService,
66
- private readonly kbDirName: string,
63
+ private readonly kb: KbContext,
67
64
  /** Where plugins come from — the one discovery every catalog shares. */
68
65
  private readonly source: PluginSource,
69
66
  now: () => number = Date.now,
@@ -77,6 +74,10 @@ export class PluginIndexService implements IPluginIndexService {
77
74
  this.cache = new TtlCache(CACHE_TTL_MS, now);
78
75
  }
79
76
 
77
+ private get kbDirName(): string {
78
+ return this.kb.kbDirName;
79
+ }
80
+
80
81
  invalidate(): void {
81
82
  this.cache.invalidate();
82
83
  }
@@ -115,7 +116,7 @@ export class PluginIndexService implements IPluginIndexService {
115
116
  */
116
117
  private async build(): Promise<PluginCatalogEntry[] | null> {
117
118
  try {
118
- const wsId = (await this.workspaceService.getOrCreateForBranch(DEFAULT_BRANCH)).id;
119
+ const wsId = (await this.workspaceService.getOrCreateForBranch(this.kb.defaultBranch)).id;
119
120
  const kbRoot = path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName);
120
121
 
121
122
  const scanned = await this.scanFolders(kbRoot);
@@ -301,11 +302,6 @@ function bucketByFolder(items: { path: string }[], folders: Map<string, string[]
301
302
  return counts;
302
303
  }
303
304
 
304
- /** The default-branch workspace id every plugin resolution runs against. */
305
- export function pluginsWorkspaceId(): string {
306
- return workspaceIdForBranch(DEFAULT_BRANCH);
307
- }
308
-
309
305
  /**
310
306
  * A plugin folder's path BELOW the plugins root: `GTM`, or `teams/deep`.
311
307
  *
@@ -2,12 +2,17 @@ import express from 'express';
2
2
  import { logger } from '../../shared/logging.js';
3
3
 
4
4
  const log = logger('teams');
5
- import { isPersonalPluginDir, isPersonalPluginFolder, pluginOfPath } from '@bevel-software/platform-shared';
5
+ import {
6
+ isPersonalPluginDir,
7
+ isPersonalPluginFolder,
8
+ pluginOfPath,
9
+ type KbLayout,
10
+ } from '@bevel-software/platform-shared';
6
11
  import type { IAccessControl } from '../access/access-control.interface.js';
12
+ import type { KbContext } from '../../shared/kb-context.js';
7
13
  import type { ISkillService } from '../skills/skills.contract.js';
8
14
  import type { IToolManualService } from '../tool-manuals/tool-manuals.contract.js';
9
15
  import type { IPluginIndexService, PluginCatalogEntry } from './plugins.contract.js';
10
- import { pluginsWorkspaceId } from './plugins.service.js';
11
16
 
12
17
  /**
13
18
  * What one team can use, by id — the Library's "Your teams" lens.
@@ -51,6 +56,8 @@ export function createTeamsRoutes(
51
56
  pluginIndex: IPluginIndexService,
52
57
  skillService: Pick<ISkillService, 'listSkills'>,
53
58
  toolManuals: Pick<IToolManualService, 'listAllSummaries'>,
59
+ /** The released branch's clone the verdicts are read from, and the layout that names the plugins root. */
60
+ kb: Pick<KbContext, 'defaultWorkspaceId' | 'layout'>,
54
61
  ): express.Router {
55
62
  const router = express.Router();
56
63
 
@@ -61,7 +68,7 @@ export function createTeamsRoutes(
61
68
  return;
62
69
  }
63
70
  try {
64
- const wsId = pluginsWorkspaceId();
71
+ const wsId = kb.defaultWorkspaceId();
65
72
  const [{ groups }, catalog, allSkills, allTools] = await Promise.all([
66
73
  accessControl.kbPrincipals(wsId),
67
74
  pluginIndex.catalog(),
@@ -73,9 +80,10 @@ export function createTeamsRoutes(
73
80
  // be a team's to read — whatever a hand-written grant in there says.
74
81
  // One rule for the plugin and for its contents, so the two cannot
75
82
  // disagree.
76
- const plugins = catalog.filter((g) => !g.folders.every(isPersonalPluginDir));
77
- const skills = allSkills.filter((s) => !inPersonalPlugin(s.path));
78
- const tools = allTools.filter((t) => !inPersonalPlugin(t.path));
83
+ const layout = kb.layout;
84
+ const plugins = catalog.filter((g) => !g.folders.every((f) => isPersonalPluginDir(f, layout)));
85
+ const skills = allSkills.filter((s) => !inPersonalPlugin(s.path, layout));
86
+ const tools = allTools.filter((t) => !inPersonalPlugin(t.path, layout));
79
87
  const folderProbes = plugins.flatMap((g) => g.folders);
80
88
  const discoverProbes = folderProbes.map(accessMdOf);
81
89
  const skillProbes = skills.map((s) => skillMdOf(s.path));
@@ -123,7 +131,7 @@ export function createTeamsRoutes(
123
131
  const accessMdOf = (folder: string) => `${folder}/access.md`;
124
132
  const skillMdOf = (skillPath: string) => `${skillPath}/SKILL.md`;
125
133
  /** Under `Plugins/personal-<id>/…` — one person's space, whatever it grants. */
126
- const inPersonalPlugin = (repoPath: string) => {
127
- const folder = pluginOfPath(repoPath);
134
+ const inPersonalPlugin = (repoPath: string, layout: KbLayout) => {
135
+ const folder = pluginOfPath(repoPath, layout);
128
136
  return folder !== null && isPersonalPluginFolder(folder);
129
137
  };
@@ -2,6 +2,7 @@ import type { Server as HttpServer } from 'node:http';
2
2
  import express from 'express';
3
3
  import { afterEach, describe, expect, it } from 'vitest';
4
4
  import { createSecretsVaultRoutes } from '../secrets-vault.routes.js';
5
+ import { testKbContext } from '../../../__tests__/kb-context.js';
5
6
 
6
7
  /**
7
8
  * The aggregated `/connect/pending` surface: everything standing between the
@@ -85,6 +86,7 @@ async function baseUrlWith(auth: { userId?: string; email?: string }): Promise<s
85
86
  app.use(
86
87
  '/api',
87
88
  createSecretsVaultRoutes({
89
+ kb: testKbContext(),
88
90
  secretsVault,
89
91
  toolManualService,
90
92
  accessControl,
@@ -170,6 +172,7 @@ describe('GET /api/connect/pending — the workspace tier', () => {
170
172
  app.use(
171
173
  '/api',
172
174
  createSecretsVaultRoutes({
175
+ kb: testKbContext(),
173
176
  secretsVault: {
174
177
  statusFor: async (_u: string, keys: string[]) =>
175
178
  keys.map((key) => ({
@@ -258,6 +261,7 @@ describe('GET /api/connect/pending — readability', () => {
258
261
  app.use(
259
262
  '/api',
260
263
  createSecretsVaultRoutes({
264
+ kb: testKbContext(),
261
265
  secretsVault: {
262
266
  statusFor: async (_u: string, keys: string[]) =>
263
267
  keys.map((key) => ({
@@ -384,6 +388,7 @@ describe('GET /api/connect/pending — OAuth scope coverage', () => {
384
388
  app.use(
385
389
  '/api',
386
390
  createSecretsVaultRoutes({
391
+ kb: testKbContext(),
387
392
  secretsVault: vaultWithGranted(grantedScopes),
388
393
  toolManualService: oauthTool,
389
394
  accessControl,
@@ -478,6 +483,7 @@ describe('GET /api/connect/pending — tool sign-ins are not double-listed as st
478
483
  app.use(
479
484
  '/api',
480
485
  createSecretsVaultRoutes({
486
+ kb: testKbContext(),
481
487
  secretsVault: vault,
482
488
  toolManualService: oauthTool,
483
489
  accessControl,
@@ -538,6 +544,7 @@ describe('GET /api/connect/pending — a row of the wrong KIND is not "set up"',
538
544
  app.use(
539
545
  '/api',
540
546
  createSecretsVaultRoutes({
547
+ kb: testKbContext(),
541
548
  secretsVault: {
542
549
  statusFor: async (_u: string, keys: string[]) => keys.map(opts.status),
543
550
  list: async () => [],
@@ -1,7 +1,11 @@
1
1
  import { describe, test, expect } from 'vitest';
2
+ import { UtcpClientConfigSerializer } from '@utcp/sdk';
2
3
  import {
3
4
  BevelSecretsVariableLoader,
5
+ DEFAULT_SECRETS_SCOPE,
6
+ bevelSecretsLoaderConfig,
4
7
  registerBevelSecretsVariableLoader,
8
+ unregisterBevelSecretsVariableLoader,
5
9
  } from '../secrets-variable-loader.js';
6
10
  import type { ISecretsVaultService } from '../secrets-vault.contract.js';
7
11
 
@@ -51,4 +55,40 @@ describe('BevelSecretsVariableLoader', () => {
51
55
  const loader = new BevelSecretsVariableLoader('');
52
56
  expect(await loader.get('m_X')).toBeNull();
53
57
  });
58
+
59
+ test('two knowledge bases in one process resolve the same user and key from their own vaults', async () => {
60
+ // One process, two vaults, one user id in each (ids are only unique
61
+ // within a knowledge base): a loader reads the vault registered under
62
+ // ITS scope, and a scope nobody registered resolves nothing.
63
+ registerBevelSecretsVariableLoader(fakeVault('user-1', { weather_WEATHER_KEY: 'sk-acme' }), 'acme');
64
+ registerBevelSecretsVariableLoader(fakeVault('user-1', { weather_WEATHER_KEY: 'sk-globex' }), 'globex');
65
+ expect(await new BevelSecretsVariableLoader('user-1', 'acme').get('weather_WEATHER_KEY')).toBe('sk-acme');
66
+ expect(await new BevelSecretsVariableLoader('user-1', 'globex').get('weather_WEATHER_KEY')).toBe('sk-globex');
67
+ expect(await new BevelSecretsVariableLoader('user-1', 'nobody').get('weather_WEATHER_KEY')).toBeNull();
68
+ });
69
+
70
+ test('the descriptor round-trips the scope through the serializer registry', async () => {
71
+ registerBevelSecretsVariableLoader(fakeVault('user-1', { weather_WEATHER_KEY: 'sk-acme' }), 'acme');
72
+ const config = new UtcpClientConfigSerializer().validateDict({
73
+ variables: {},
74
+ load_variables_from: [bevelSecretsLoaderConfig('user-1', 'acme')],
75
+ });
76
+ const [loader] = config.load_variables_from as BevelSecretsVariableLoader[];
77
+ expect(loader.scope).toBe('acme');
78
+ expect(await loader.get('weather_WEATHER_KEY')).toBe('sk-acme');
79
+ // Without a scope the descriptor names the single knowledge base's.
80
+ const [plain] = new UtcpClientConfigSerializer().validateDict({
81
+ variables: {},
82
+ load_variables_from: [bevelSecretsLoaderConfig('user-1')],
83
+ }).load_variables_from as BevelSecretsVariableLoader[];
84
+ expect(plain.scope).toBe(DEFAULT_SECRETS_SCOPE);
85
+ });
86
+
87
+ test('unregistering a scope makes its loaders resolve nothing', async () => {
88
+ registerBevelSecretsVariableLoader(fakeVault('user-1', { weather_WEATHER_KEY: 'sk-acme' }), 'evicted');
89
+ const loader = new BevelSecretsVariableLoader('user-1', 'evicted');
90
+ expect(await loader.get('weather_WEATHER_KEY')).toBe('sk-acme');
91
+ unregisterBevelSecretsVariableLoader('evicted');
92
+ expect(await loader.get('weather_WEATHER_KEY')).toBeNull();
93
+ });
54
94
  });
@@ -2,6 +2,7 @@ import type { Server as HttpServer } from 'node:http';
2
2
  import express from 'express';
3
3
  import { afterEach, describe, expect, it, vi } from 'vitest';
4
4
  import { createSecretsVaultRoutes } from '../secrets-vault.routes.js';
5
+ import { testKbContext } from '../../../__tests__/kb-context.js';
5
6
 
6
7
  /**
7
8
  * THE OWNER GATE: a tool's shared (admin) secrets — plain values and OAuth
@@ -91,6 +92,7 @@ async function baseUrlAs(email: string): Promise<string> {
91
92
  app.use(
92
93
  '/api',
93
94
  createSecretsVaultRoutes({
95
+ kb: testKbContext(),
94
96
  secretsVault,
95
97
  toolManualService,
96
98
  accessControl,
@@ -21,6 +21,8 @@ export {
21
21
  } from './secrets-vault.contract.js';
22
22
  export {
23
23
  registerBevelSecretsVariableLoader,
24
+ unregisterBevelSecretsVariableLoader,
24
25
  bevelSecretsLoaderConfig,
25
26
  BEVEL_SECRETS_LOADER_TYPE,
27
+ DEFAULT_SECRETS_SCOPE,
26
28
  } from './secrets-variable-loader.js';
@@ -18,29 +18,43 @@ const log = logger('secrets-vault');
18
18
  * The `UtcpClient.create` path re-validates the whole config through the
19
19
  * serializer registry, so a loader can't be handed in as a bare live object — it
20
20
  * must round-trip through a registered serializer. We therefore put a plain
21
- * descriptor `{ variable_loader_type, user_id }` in `load_variables_from` (via
22
- * {@link bevelSecretsLoaderConfig}) and let the registered serializer rebuild a
23
- * live loader bound to the process-wide vault. Swapping the vault backend (e.g.
24
- * to a client's external secret manager) is just a different
25
- * `ISecretsVaultService` passed to {@link registerBevelSecretsVariableLoader}.
21
+ * descriptor `{ variable_loader_type, user_id, scope }` in `load_variables_from`
22
+ * (via {@link bevelSecretsLoaderConfig}) and let the registered serializer
23
+ * rebuild a live loader bound to the vault registered under that scope.
24
+ *
25
+ * ONE VAULT PER SCOPE, NOT PER PROCESS. The serializer registry is UTCP's and
26
+ * process-wide, but a process can host several knowledge bases, each with a
27
+ * vault of its own, and a user id is only unique within one of them. So the
28
+ * descriptor names the scope its vault was registered under, and a loader
29
+ * reads through that vault alone: two knowledge bases whose users both stored
30
+ * `weather_WEATHER_KEY` never see each other's value. A single-tenant
31
+ * deployment registers under {@link DEFAULT_SECRETS_SCOPE} and names nothing.
32
+ * Swapping the vault backend (e.g. to a client's external secret manager) is
33
+ * just a different `ISecretsVaultService` passed to
34
+ * {@link registerBevelSecretsVariableLoader}.
26
35
  */
27
36
  export const BEVEL_SECRETS_LOADER_TYPE = 'bevel_secrets';
28
37
 
38
+ /** The scope a deployment that hosts one knowledge base registers under. */
39
+ export const DEFAULT_SECRETS_SCOPE = 'default';
40
+
29
41
  /**
30
- * Process-wide vault the reconstructed loaders read through. Set once at
31
- * composition time (before any client is built) so the serializer — which only
32
- * receives a plain dict — can bind live loaders to it.
42
+ * The vaults the reconstructed loaders read through, by scope. A composition
43
+ * root registers its vault before any client is built; a graph that is torn
44
+ * down unregisters it so a stale descriptor resolves nothing.
33
45
  */
34
- let sharedSecretsVault: ISecretsVaultService | null = null;
46
+ const vaults = new Map<string, ISecretsVaultService>();
35
47
 
36
48
  export class BevelSecretsVariableLoader implements VariableLoader {
37
49
  readonly variable_loader_type = BEVEL_SECRETS_LOADER_TYPE;
38
50
  readonly user_id: string;
51
+ readonly scope: string;
39
52
  // VariableLoader is an open shape; allow arbitrary extra props.
40
53
  [key: string]: unknown;
41
54
 
42
- constructor(userId: string) {
55
+ constructor(userId: string, scope: string = DEFAULT_SECRETS_SCOPE) {
43
56
  this.user_id = userId;
57
+ this.scope = scope;
44
58
  }
45
59
 
46
60
  /**
@@ -52,9 +66,10 @@ export class BevelSecretsVariableLoader implements VariableLoader {
52
66
  * harvest a secret the user configured for a different manual.
53
67
  */
54
68
  async get(effectiveKey: string): Promise<string | null> {
55
- if (!sharedSecretsVault || !this.user_id) return null;
69
+ const vault = vaults.get(this.scope);
70
+ if (!vault || !this.user_id) return null;
56
71
  try {
57
- return await sharedSecretsVault.resolve(this.user_id, effectiveKey);
72
+ return await vault.resolve(this.user_id, effectiveKey);
58
73
  } catch (err) {
59
74
  // Log the fault so a backend failure is distinguishable from a merely
60
75
  // unset secret (resolve returning null) when troubleshooting.
@@ -66,15 +81,18 @@ export class BevelSecretsVariableLoader implements VariableLoader {
66
81
 
67
82
  class BevelSecretsVariableLoaderSerializer extends Serializer<VariableLoader> {
68
83
  toDict(obj: VariableLoader): Record<string, unknown> {
84
+ const loader = obj as BevelSecretsVariableLoader;
69
85
  return {
70
86
  variable_loader_type: BEVEL_SECRETS_LOADER_TYPE,
71
- user_id: (obj as BevelSecretsVariableLoader).user_id ?? '',
87
+ user_id: loader.user_id ?? '',
88
+ scope: loader.scope ?? DEFAULT_SECRETS_SCOPE,
72
89
  };
73
90
  }
74
91
 
75
92
  validateDict(obj: Record<string, unknown>): VariableLoader {
76
93
  const userId = typeof obj.user_id === 'string' ? obj.user_id : '';
77
- return new BevelSecretsVariableLoader(userId);
94
+ const scope = typeof obj.scope === 'string' && obj.scope ? obj.scope : DEFAULT_SECRETS_SCOPE;
95
+ return new BevelSecretsVariableLoader(userId, scope);
78
96
  }
79
97
  }
80
98
 
@@ -91,14 +109,26 @@ VariableLoaderSerializer.registerVariableLoader(
91
109
  );
92
110
 
93
111
  /**
94
- * Bind the process-wide vault the reconstructed loaders read through. Called
95
- * once at composition time; before it runs, loaders resolve to null.
112
+ * Bind the vault the loaders of `scope` read through. Called once per
113
+ * knowledge base at composition time; before it runs, loaders of that scope
114
+ * resolve to null.
96
115
  */
97
- export function registerBevelSecretsVariableLoader(secretsVault: ISecretsVaultService): void {
98
- sharedSecretsVault = secretsVault;
116
+ export function registerBevelSecretsVariableLoader(
117
+ secretsVault: ISecretsVaultService,
118
+ scope: string = DEFAULT_SECRETS_SCOPE,
119
+ ): void {
120
+ vaults.set(scope, secretsVault);
121
+ }
122
+
123
+ /** Forget a scope's vault — the last step of tearing a knowledge base's graph down. */
124
+ export function unregisterBevelSecretsVariableLoader(scope: string = DEFAULT_SECRETS_SCOPE): void {
125
+ vaults.delete(scope);
99
126
  }
100
127
 
101
- /** The plain descriptor to place in a `UtcpClientConfig.load_variables_from` for a given user. */
102
- export function bevelSecretsLoaderConfig(userId: string): Record<string, unknown> {
103
- return { variable_loader_type: BEVEL_SECRETS_LOADER_TYPE, user_id: userId };
128
+ /**
129
+ * The plain descriptor to place in a `UtcpClientConfig.load_variables_from`
130
+ * for a given user of the knowledge base registered under `scope`.
131
+ */
132
+ export function bevelSecretsLoaderConfig(userId: string, scope: string = DEFAULT_SECRETS_SCOPE): Record<string, unknown> {
133
+ return { variable_loader_type: BEVEL_SECRETS_LOADER_TYPE, user_id: userId, scope };
104
134
  }
@@ -3,7 +3,6 @@ import { logger } from '../../shared/logging.js';
3
3
 
4
4
  const log = logger('secrets');
5
5
  import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
6
- import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
7
6
  import {
8
7
  InvalidSecretError,
9
8
  SecretNotFoundError,
@@ -17,11 +16,13 @@ import type { IToolManualService, ToolManualSummary, ToolVariable } from '../too
17
16
  import type { IConnectionProbeService } from '../connection-probe/connection-probe.contract.js';
18
17
  import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
19
18
  import type { IAccessControl } from '../access/access-control.interface.js';
20
- import { workspaceIdForBranch } from '../../shared/workspace-id.js';
19
+ import type { KbContext } from '../../shared/kb-context.js';
21
20
  import '../auth/auth.middleware.js'; // Express Request augmentation (req.userId / req.userEmail)
22
21
 
23
22
  export interface SecretsVaultRoutesDeps {
24
23
  secretsVault: ISecretsVaultService;
24
+ /** The released branch's clone — where the `.tool` files the access verdicts are read from live. */
25
+ kb: Pick<KbContext, 'defaultWorkspaceId'>;
25
26
  /** Source of `.tool` manuals + their declared variable scopes (default-branch catalog). */
26
27
  toolManualService: IToolManualService;
27
28
  /** Gates who may set a tool's ADMIN (shared) secrets — writers of the `.tool` file. */
@@ -79,16 +80,16 @@ export function isSafeReturnPath(returnTo: unknown): returnTo is string {
79
80
  * `req.userId` — secrets are private per user. Mounted behind the JWT middleware.
80
81
  */
81
82
  export function createSecretsVaultRoutes(deps: SecretsVaultRoutesDeps): express.Router {
82
- const { secretsVault, toolManualService, accessControl, connectionProbe } = deps;
83
+ const { secretsVault, toolManualService, accessControl, connectionProbe, kb } = deps;
83
84
  const router = express.Router();
84
85
  // A FUNCTION, not a constant. Routers are constructed at boot, and on a
85
86
  // deployment configured through the setup screen the branch model does not
86
- // exist yet at that moment — `DEFAULT_BRANCH` is still ''. The live binding
87
- // updates when setup applies the model, but only reads INSIDE a function
87
+ // exist yet at that moment — the default branch is still ''. The context
88
+ // learns it when setup applies the model, but only reads INSIDE a function
88
89
  // body see it; a construction-time capture would keep handing an empty
89
90
  // workspace id to the access resolver until the next restart ("Invalid
90
91
  // workspace ID" on every /secrets/tools call).
91
- const defaultWs = () => workspaceIdForBranch(DEFAULT_BRANCH);
92
+ const defaultWs = () => kb.defaultWorkspaceId();
92
93
 
93
94
  // The vault key = the exact key UTCP looks the var up under (doubles underscores
94
95
  // in the manual namespace), so storage and resolution agree for snake_case ids.
@@ -149,20 +149,12 @@ describe('DeploymentSettingsService — secrets', () => {
149
149
  expect(token).not.toHaveProperty('value');
150
150
  });
151
151
 
152
- it('publishes a stored token as GITHUB_TOKEN, which is what git reads', async () => {
152
+ it('keeps a stored token out of the process environment: git reads it per call', async () => {
153
153
  const { db } = makeDb();
154
154
  const settings = new DeploymentSettingsService(db, ENC_KEY);
155
155
  await settings.save({ gitToken: 'ghp_fromsetup' }, null);
156
- expect(process.env.GITHUB_TOKEN).toBe('ghp_fromsetup');
157
- });
158
-
159
- it('does not overwrite a git token the environment supplied', async () => {
160
- process.env.GIT_TOKEN = 'ghp_fromenv';
161
- process.env.GITHUB_TOKEN = 'ghp_fromenv';
162
- const { db } = makeDb();
163
- const settings = new DeploymentSettingsService(db, ENC_KEY);
164
- settings.syncGitTokenEnv();
165
- expect(process.env.GITHUB_TOKEN).toBe('ghp_fromenv');
156
+ expect(process.env.GITHUB_TOKEN).toBeUndefined();
157
+ expect(settings.resolve('gitToken')).toBe('ghp_fromsetup');
166
158
  });
167
159
 
168
160
  it('refuses to store a secret with no encryption key rather than writing plaintext', async () => {
@@ -528,3 +520,24 @@ describe('DeploymentSettingsService — validation', () => {
528
520
  ).toEqual([]);
529
521
  });
530
522
  });
523
+
524
+ describe('DeploymentSettingsService — an environment of its own', () => {
525
+ it('resolves the env-first layer from the environment it was given, not the process\'s', async () => {
526
+ process.env.KB_REPO_URL = 'https://host.example/leaks-into-every-tenant.git';
527
+ const { db } = makeDb();
528
+ const own = new DeploymentSettingsService(db, ENC_KEY, undefined, {
529
+ env: { KB_REPO_URL: 'https://github.com/acme/kb.git', GIT_USERNAME: 'oauth2' },
530
+ });
531
+ await own.load();
532
+ expect(own.resolve('kbRepoUrl')).toBe('https://github.com/acme/kb.git');
533
+ expect(own.sourceOf('kbRepoUrl')).toBe('env');
534
+ expect(own.resolve('gitUsername')).toBe('oauth2');
535
+ // What the given environment leaves out is not taken from the process either.
536
+ expect(own.resolve('gitToken')).toBe('');
537
+ expect(own.sourceOf('gitToken')).toBe('unset');
538
+ // A service built without one keeps reading the process, as before.
539
+ const shared = new DeploymentSettingsService(db, ENC_KEY);
540
+ await shared.load();
541
+ expect(shared.resolve('kbRepoUrl')).toBe('https://host.example/leaks-into-every-tenant.git');
542
+ });
543
+ });
@@ -10,7 +10,7 @@ import {
10
10
  rootFolderListerFor,
11
11
  type GitRunner,
12
12
  } from '../git-root-folders.js';
13
- import type { IGitRunner } from '../../../shared/git.contract.js';
13
+ import { NO_GIT_CREDENTIALS, type IGitRunner } from '../../../shared/git.contract.js';
14
14
 
15
15
  const LISTING = {
16
16
  url: 'https://example.com/acme/kb.git',
@@ -226,6 +226,7 @@ describe('rootFolderListerFor — the listing runs through the deployment git po
226
226
  const outcomes = ['', 'KnowledgeBase\0Plugins\0'];
227
227
  const port: IGitRunner = {
228
228
  defaultTimeoutMs: 1000,
229
+ credentials: NO_GIT_CREDENTIALS,
229
230
  run: (async (cwd: string, args: string[], opts: { env?: NodeJS.ProcessEnv; timeoutMs?: number } = {}) => {
230
231
  calls.push({ cwd, args, opts });
231
232
  return { stdout: outcomes.shift() ?? '', stderr: '' };
@@ -6,6 +6,7 @@ import { createSetupRoutes } from '../setup.routes.js';
6
6
  import { DeploymentSettingsService } from '../deployment-settings.service.js';
7
7
  import type { Database } from '../../database/connection.js';
8
8
  import type { IAdminAccessService } from '../../admin/admin.interface.js';
9
+ import { testKbContext } from '../../../__tests__/kb-context.js';
9
10
 
10
11
  /**
11
12
  * A boot that survived an unreachable remote leaves the RUNNER standing on a
@@ -68,7 +69,7 @@ function listen(runner: { runAll(): Promise<void>; lastFailure(): string | null
68
69
  });
69
70
  app.use(
70
71
  '/api',
71
- createSetupRoutes(settings, { isAdmin: async () => true } as IAdminAccessService, runner),
72
+ createSetupRoutes(settings, { isAdmin: async () => true } as IAdminAccessService, runner, testKbContext()),
72
73
  );
73
74
  server = app.listen(0);
74
75
  const { port } = server.address() as AddressInfo;