@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
@@ -4,7 +4,7 @@ import { KbPluginSource } from '../discovery/kb-plugin-source.js';
4
4
  import fs from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import os from 'node:os';
7
- import { DEFAULT_BRANCH, type AuthUser } from '@bevel-software/platform-shared';
7
+ import { DEFAULT_BRANCH, withLinkedSkillRoots, type AuthUser } from '@bevel-software/platform-shared';
8
8
 
9
9
  import type { WorkspaceService } from '../../workspace/workspace.service.js';
10
10
  import { AccessControlService } from '../../access/access-control.service.js';
@@ -13,6 +13,7 @@ import { workspaceIdForBranch } from '../../../shared/workspace-id.js';
13
13
  import { PluginLinkIndex } from '../plugin-links.js';
14
14
  import { PluginLinksService, PluginLinkError } from '../plugin-links.service.js';
15
15
  import type { ProvisionCommitDriver } from '../plugin-provision.service.js';
16
+ import { testKbContext } from '../../../__tests__/kb-context.js';
16
17
 
17
18
  /**
18
19
  * Linking end to end over a real tree: the real resolver decides who may
@@ -36,6 +37,10 @@ describe('PluginLinksService', () => {
36
37
  let root: string;
37
38
  let repo: string;
38
39
  let commits: string[];
40
+ /** The commit SUBJECT each of those landed with — `undefined` = the default. */
41
+ let subjects: (string | undefined)[];
42
+ /** Set to make the commit driver refuse every path containing it. */
43
+ let failCommitsUnder: string | null;
39
44
  let access: AccessControlService;
40
45
  let skills: SkillService;
41
46
  let index: PluginLinkIndex;
@@ -52,6 +57,8 @@ describe('PluginLinksService', () => {
52
57
  root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-links-'));
53
58
  repo = path.join(root, wsId, KB_DIR);
54
59
  commits = [];
60
+ subjects = [];
61
+ failCommitsUnder = null;
55
62
  const workspaceService = {
56
63
  getOrCreateForBranch: async () => ({ id: wsId }),
57
64
  getWorkspacePath: async (id: string) => path.join(root, id),
@@ -64,8 +71,12 @@ describe('PluginLinksService', () => {
64
71
  ensureRemotesFetched: async () => undefined,
65
72
  } as unknown as WorkspaceService;
66
73
  const driver: ProvisionCommitDriver = {
67
- runPendingCommit: async (_ws, _branch, target) => {
74
+ runPendingCommit: async (_ws, _branch, target, _user, opts) => {
75
+ if (failCommitsUnder !== null && target.includes(failCommitsUnder)) {
76
+ throw new Error('the push gate said no');
77
+ }
68
78
  commits.push(target);
79
+ subjects.push(opts?.summary);
69
80
  },
70
81
  };
71
82
 
@@ -84,9 +95,10 @@ describe('PluginLinksService', () => {
84
95
 
85
96
  const disk = new NodeFs();
86
97
  access = new AccessControlService(workspaceService, KB_DIR, disk);
87
- skills = new SkillService(workspaceService, access, KB_DIR, disk);
88
- index = new PluginLinkIndex(workspaceService, skills, access, KB_DIR, new KbPluginSource(disk));
89
- svc = new PluginLinksService(workspaceService, driver, access, skills, index, KB_DIR);
98
+ const kb = testKbContext({ kbDirName: KB_DIR });
99
+ skills = new SkillService(workspaceService, access, kb, disk);
100
+ index = new PluginLinkIndex(workspaceService, skills, access, kb, new KbPluginSource(disk, kb));
101
+ svc = new PluginLinksService(workspaceService, driver, access, skills, index, kb);
90
102
  });
91
103
  afterEach(() => fs.rm(root, { recursive: true, force: true }));
92
104
 
@@ -281,4 +293,207 @@ describe('PluginLinksService', () => {
281
293
  index.invalidate();
282
294
  expect(await index.pluginsOf('Plugins/GTM/skills/outreach')).toEqual([{ name: 'gtm', linked: false, granted: true }]);
283
295
  });
296
+
297
+ /**
298
+ * A link written into the manifest WITHOUT its grants — the hand edit this
299
+ * whole surface exists for. `link()` writes both lines; every test below is
300
+ * about what the index and the repair say when they are absent.
301
+ */
302
+ const linkByHand = async (...roots: string[]) => {
303
+ await write(
304
+ 'Plugins/GTM/plugin.json',
305
+ `${JSON.stringify(withLinkedSkillRoots({ name: 'gtm', version: '1.0.0' }, roots), null, 2)}\n`,
306
+ );
307
+ access.invalidate(wsId);
308
+ skills.invalidate();
309
+ index.invalidate();
310
+ };
311
+
312
+ /** Whether the index calls the link to one skill healthy. */
313
+ const grantedOf = async (skillPath: string) =>
314
+ (await index.pluginsOf(skillPath)).find((m) => m.name === 'gtm')?.granted;
315
+
316
+ describe('granted — the two lines at the root, and nothing else', () => {
317
+ it('a PUBLIC root is not granted: the grant is the link, not the readability', async () => {
318
+ // The demo-core shape: the repository root says `read: everyone`, so
319
+ // GTM's members can read the skill and the link still names nothing.
320
+ await write('access.md', '---\n---\nread:\n - everyone\nwrite:\n - Admin\n');
321
+ await linkByHand('Skills/Eng/deploy');
322
+
323
+ expect(await access.canRead(wsId, member.email, 'Skills/Eng/deploy/SKILL.md')).toBe(true);
324
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
325
+ });
326
+
327
+ it('a plugin grant INHERITED from a folder above the root does not count', async () => {
328
+ await write(
329
+ 'Skills/Eng/access.md',
330
+ '---\n---\nread:\n - plugin/gtm/read\nwrite:\n - Eve <eve@x.io>\n - plugin/gtm/write\n',
331
+ );
332
+ await linkByHand('Skills/Eng/deploy');
333
+
334
+ // The members CAN read it — and the root the manifest names still says
335
+ // nothing about GTM, so the link is one edit of Skills/Eng away from
336
+ // silently sharing nothing.
337
+ expect(await access.canRead(wsId, member.email, 'Skills/Eng/deploy/SKILL.md')).toBe(true);
338
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
339
+ });
340
+
341
+ it('both lines at the root are granted; a deny BELOW the root is not', async () => {
342
+ await write(
343
+ 'Skills/Eng/access.md',
344
+ '---\n---\nread:\n - plugin/gtm/read\nwrite:\n - Eve <eve@x.io>\n - plugin/gtm/write\n',
345
+ );
346
+ await write('Skills/Eng/rollback/access.md', '---\n---\nread:\n - deny plugin/gtm/read\n');
347
+ await linkByHand('Skills/Eng');
348
+
349
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(true);
350
+ expect(await grantedOf('Skills/Eng/rollback')).toBe(false);
351
+ });
352
+
353
+ it('a deny AT the root beside the lines is not granted — the deny is the operator\'s', async () => {
354
+ // What a repair leaves behind when someone has denied the plugin: the
355
+ // grant is spliced in, the deny stays, and the link stays broken.
356
+ await write(
357
+ 'Skills/Eng/deploy/access.md',
358
+ '---\n---\nread:\n - plugin/gtm/read\n - deny plugin/gtm/read\nwrite:\n - plugin/gtm/write\n',
359
+ );
360
+ await linkByHand('Skills/Eng/deploy');
361
+
362
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
363
+ });
364
+ });
365
+
366
+ describe('repairAll — what opening the plugin page runs', () => {
367
+ it('does not call a link denied at its root repaired: nothing to write, and the banner keeps it', async () => {
368
+ // The lines are there; a `deny` beside them is what breaks the link.
369
+ // The repair has nothing to write, and a root it did not write is not
370
+ // one it fixed — reporting it repaired would hide it from the one
371
+ // person who could act, and reload the page for nothing.
372
+ await write(
373
+ 'Skills/Eng/deploy/access.md',
374
+ '---\n---\nread:\n - plugin/gtm/read\n - deny plugin/gtm/read\nwrite:\n - Mia <mia@x.io>\n - plugin/gtm/write\n',
375
+ );
376
+ await linkByHand('Skills/Eng/deploy');
377
+ commits.length = 0;
378
+
379
+ const report = await svc.repairAll(manager, 'gtm');
380
+ expect(report.repaired).toEqual([]);
381
+ expect(report.skipped).toMatchObject([
382
+ { root: 'Skills/Eng/deploy', reason: 'denied', skills: [{ path: 'Skills/Eng/deploy', name: 'deploy' }] },
383
+ ]);
384
+ // Eve edits Skills/Eng (the fixture's own rule) and Mia the root itself:
385
+ // both can remove the deny, so both are named.
386
+ expect(report.skipped[0].editors.users.map((u) => u.email).sort()).toEqual(['eve@x.io', 'mia@x.io']);
387
+ expect(commits).toEqual([]);
388
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
389
+ });
390
+
391
+ it('repairs every link the writer may write, silently, and says nothing about them', async () => {
392
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Eve <eve@x.io>\n - Mia <mia@x.io>\n');
393
+ await linkByHand('Skills/Eng/deploy', 'Skills/Eng/rollback');
394
+ commits.length = 0;
395
+ subjects.length = 0;
396
+
397
+ expect(await svc.repairAll(manager, 'gtm')).toEqual({
398
+ repaired: ['Skills/Eng/deploy', 'Skills/Eng/rollback'],
399
+ skipped: [],
400
+ });
401
+ expect(commits).toEqual([
402
+ `${KB_DIR}/Skills/Eng/deploy/access.md`,
403
+ `${KB_DIR}/Skills/Eng/rollback/access.md`,
404
+ ]);
405
+ // The commit is the only trace a silent repair leaves, so it has to
406
+ // explain itself.
407
+ expect(subjects).toEqual([
408
+ 'Repair link: gtm → Skills/Eng/deploy (automatic, on opening the plugin page)',
409
+ 'Repair link: gtm → Skills/Eng/rollback (automatic, on opening the plugin page)',
410
+ ]);
411
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(true);
412
+ expect(await grantedOf('Skills/Eng/rollback')).toBe(true);
413
+ });
414
+
415
+ it('writes exactly what the manual repair writes', async () => {
416
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Mia <mia@x.io>\n');
417
+ await linkByHand('Skills/Eng/deploy', 'Skills/Eng/rollback');
418
+
419
+ await svc.repair(manager, 'gtm', 'Skills/Eng/deploy');
420
+ await svc.repairAll(manager, 'gtm');
421
+ expect(await read('Skills/Eng/rollback/access.md')).toEqual(
422
+ await read('Skills/Eng/deploy/access.md'),
423
+ );
424
+ });
425
+
426
+ it('leaves a link the writer may not repair alone, and names who can', async () => {
427
+ // Skills/Eng is Eve's. Mia manages GTM and nothing else.
428
+ await linkByHand('Skills/Eng/deploy');
429
+ commits.length = 0;
430
+
431
+ const report = await svc.repairAll(manager, 'gtm');
432
+ expect(report.repaired).toEqual([]);
433
+ expect(report.skipped).toEqual([
434
+ {
435
+ root: 'Skills/Eng/deploy',
436
+ reason: 'needs-skill-write',
437
+ skills: [{ path: 'Skills/Eng/deploy', name: 'deploy' }],
438
+ // Admin holds the write floor on any access.md, so an admin can
439
+ // always repair a link; the eligible lists carry no display names,
440
+ // so Eve is named by the address a reader can write to.
441
+ editors: { roles: ['Admin'], users: [{ name: 'eve@x.io', email: 'eve@x.io' }] },
442
+ },
443
+ ]);
444
+ expect(commits).toEqual([]);
445
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
446
+ });
447
+
448
+ it('is refused for someone who may not write the plugin — the same 404 as an unknown one', async () => {
449
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Sam <sam@x.io>\n');
450
+ await linkByHand('Skills/Eng/deploy');
451
+
452
+ await expect(svc.repairAll(member, 'gtm')).rejects.toMatchObject({ status: 404 });
453
+ await expect(svc.repairAll(manager, 'Ghost')).rejects.toMatchObject({ status: 404 });
454
+ // Sam may write the skill; being unable to write GTM is what stopped it.
455
+ expect(commits).toEqual([]);
456
+ expect(await grantedOf('Skills/Eng/deploy')).toBe(false);
457
+ });
458
+
459
+ it('writes nothing the second time: a root that already carries both lines is left alone', async () => {
460
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Mia <mia@x.io>\n');
461
+ await linkByHand('Skills/Eng/deploy');
462
+ await svc.repairAll(manager, 'gtm');
463
+ commits.length = 0;
464
+
465
+ expect(await svc.repairAll(manager, 'gtm')).toEqual({ repaired: [], skipped: [] });
466
+ expect(commits).toEqual([]);
467
+ const rules = await read('Skills/Eng/deploy/access.md');
468
+ expect(rules.match(/plugin\/gtm\/read/g)).toHaveLength(1);
469
+ expect(rules.match(/plugin\/gtm\/write/g)).toHaveLength(1);
470
+ });
471
+
472
+ it('a repair that fails leaves the others repaired, and stays in the report', async () => {
473
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Eve <eve@x.io>\n - Mia <mia@x.io>\n');
474
+ await linkByHand('Skills/Eng/deploy', 'Skills/Eng/rollback');
475
+ failCommitsUnder = 'Skills/Eng/rollback';
476
+ commits.length = 0;
477
+
478
+ const report = await svc.repairAll(manager, 'gtm');
479
+ expect(report.repaired).toEqual(['Skills/Eng/deploy']);
480
+ expect(report.skipped).toMatchObject([{ root: 'Skills/Eng/rollback', reason: 'failed' }]);
481
+ // Eve and Mia both edit the scope, so both are named as able to repair it.
482
+ expect(report.skipped[0].editors.users.map((u) => u.email).sort()).toEqual([
483
+ 'eve@x.io',
484
+ 'mia@x.io',
485
+ ]);
486
+ expect(commits).toEqual([`${KB_DIR}/Skills/Eng/deploy/access.md`]);
487
+ expect(await read('Skills/Eng/deploy/access.md')).toContain('plugin/gtm/read');
488
+ });
489
+
490
+ it('has nothing to do for a plugin whose links are all healthy', async () => {
491
+ await write('Skills/Eng/access.md', '---\n---\nwrite:\n - Mia <mia@x.io>\n');
492
+ await svc.link(manager, 'gtm', 'Skills/Eng/deploy');
493
+ commits.length = 0;
494
+
495
+ expect(await svc.repairAll(manager, 'gtm')).toEqual({ repaired: [], skipped: [] });
496
+ expect(commits).toEqual([]);
497
+ });
498
+ });
284
499
  });
@@ -15,6 +15,7 @@ import {
15
15
  pluginAccessMd,
16
16
  personalAccessMd,
17
17
  } from '../plugin-provision.service.js';
18
+ import { testKbContext } from '../../../__tests__/kb-context.js';
18
19
 
19
20
  const KB = 'knowledge-base';
20
21
  const USER: AuthUser = { id: 'u1-abcd', email: 'ali@example.com', name: 'Ali Vega' } as AuthUser;
@@ -50,7 +51,8 @@ async function makeHarness() {
50
51
  const commits = { runPendingCommit: vi.fn(async () => undefined) };
51
52
  const accessControl = { invalidate: vi.fn() } as unknown as IAccessControl;
52
53
  const events = { emit: vi.fn() };
53
- const svc = new PluginProvisionService(workspaceService, commits, accessControl, KB, events, new KbPluginSource(new NodeFs()), new NodeFs());
54
+ const kb = testKbContext({ kbDirName: KB });
55
+ const svc = new PluginProvisionService(workspaceService, commits, accessControl, kb, events, new KbPluginSource(new NodeFs(), kb), new NodeFs());
54
56
  return { svc, dir, commits, accessControl, events, writeFile };
55
57
  }
56
58
 
@@ -12,6 +12,7 @@ import { PushNeedsAgentResolutionError } from '../../../shared/domain-errors.js'
12
12
  import { workspaceIdForBranch } from '../../../shared/workspace-id.js';
13
13
  import { KbPluginSource } from '../discovery/kb-plugin-source.js';
14
14
  import { PluginRenameError, PluginRenameService, renamePluginPrincipalInText } from '../plugin-rename.service.js';
15
+ import { testKbContext } from '../../../__tests__/kb-context.js';
15
16
 
16
17
  // The one walk of the checkout, with a switch that makes it report a hole —
17
18
  // the way a folder it cannot list would — to every listener on it. The
@@ -115,7 +116,7 @@ describe('PluginRenameService', () => {
115
116
 
116
117
  access = new AccessControlService(workspaceService, KB_DIR, new NodeFs());
117
118
  const disk = new HoledFs();
118
- svc = new PluginRenameService(workspaceService, driver, access, new KbPluginSource(disk), disk, KB_DIR, undefined, () => {
119
+ svc = new PluginRenameService(workspaceService, driver, access, new KbPluginSource(disk, testKbContext({ kbDirName: KB_DIR })), disk, testKbContext({ kbDirName: KB_DIR }), undefined, () => {
119
120
  invalidated += 1;
120
121
  });
121
122
  });
@@ -124,7 +125,7 @@ describe('PluginRenameService', () => {
124
125
  it('refuses to claim a name against a listing with a hole in it — an unreadable folder may hold that very plugin', async () => {
125
126
  // Discovery as the walker reports it when a folder exists but could not
126
127
  // be read: the plugins it did see, plus the hole.
127
- const real = new KbPluginSource(new NodeFs());
128
+ const real = new KbPluginSource(new NodeFs(), testKbContext({ kbDirName: KB_DIR }));
128
129
  const holed = {
129
130
  dialect: 'kb',
130
131
  discover: async (root: string) => ({ ...(await real.discover(root)), unreadable: ['Plugins/Hidden'] }),
@@ -135,7 +136,7 @@ describe('PluginRenameService', () => {
135
136
  access,
136
137
  holed,
137
138
  new NodeFs(),
138
- KB_DIR,
139
+ testKbContext({ kbDirName: KB_DIR }),
139
140
  );
140
141
  await expect(svcOverHole.rename(manager, 'gtm', { name: 'go-to-market' })).rejects.toMatchObject({
141
142
  status: 503,
@@ -155,7 +156,7 @@ describe('PluginRenameService', () => {
155
156
  });
156
157
 
157
158
  it('tells a NON-manager nothing about the tree — a holed discovery is still just "unknown plugin" to them', async () => {
158
- const real = new KbPluginSource(new NodeFs());
159
+ const real = new KbPluginSource(new NodeFs(), testKbContext({ kbDirName: KB_DIR }));
159
160
  const holed = {
160
161
  dialect: 'kb',
161
162
  discover: async (root: string) => ({ ...(await real.discover(root)), unreadable: ['Plugins/Hidden'] }),
@@ -166,7 +167,7 @@ describe('PluginRenameService', () => {
166
167
  access,
167
168
  holed,
168
169
  new NodeFs(),
169
- KB_DIR,
170
+ testKbContext({ kbDirName: KB_DIR }),
170
171
  );
171
172
  const refusal = await svcOverHole.rename(member, 'gtm', { name: 'go-to-market' }).catch((e: unknown) => e);
172
173
  expect(refusal).toMatchObject({ status: 404, payload: { kind: 'unknown-plugin' } });
@@ -269,7 +270,7 @@ describe('PluginRenameService', () => {
269
270
  expect(await read('Skills/Eng/deploy/access.md')).toBe(DEPLOY_RULES);
270
271
  // The three answers that must never diverge: the rename's, the
271
272
  // discovery source's, and the shared reader's over the file itself.
272
- const { plugins } = await new KbPluginSource(new NodeFs()).discover(repo);
273
+ const { plugins } = await new KbPluginSource(new NodeFs(), testKbContext({ kbDirName: KB_DIR })).discover(repo);
273
274
  const found = plugins.find((p) => p.folder === 'Plugins/GTM')!;
274
275
  expect([found.name, found.displayName]).toEqual([result.name, result.displayName]);
275
276
  expect(pluginDisplayNameOf(JSON.parse(await read('Plugins/GTM/plugin.json')))).toBe(result.displayName);
@@ -22,6 +22,7 @@ import { PluginJoinRequestJobs } from '../join-request-jobs.service.js';
22
22
  import { pluginFolderBelowRoot } from '../plugins.service.js';
23
23
  import { FakeJoinRequestStore } from './fake-join-request-store.js';
24
24
  import type { PluginSummary, IPluginIndexService } from '../plugins.contract.js';
25
+ import { testKbContext } from '../../../__tests__/kb-context.js';
25
26
 
26
27
  /**
27
28
  * HTTP-level contract for the plugin routes: the auth gate, the three-tier
@@ -155,7 +156,14 @@ async function makeHarness(opts: HarnessOpts = {}) {
155
156
 
156
157
  const index =
157
158
  opts.index ??
158
- new PluginIndexService(workspaceService, accessControl, skillService, toolService, KB, new KbPluginSource(new NodeFs()));
159
+ new PluginIndexService(
160
+ workspaceService,
161
+ accessControl,
162
+ skillService,
163
+ toolService,
164
+ testKbContext({ kbDirName: KB }),
165
+ new KbPluginSource(new NodeFs(), testKbContext({ kbDirName: KB })),
166
+ );
159
167
 
160
168
  const email = opts.email === undefined ? ALI : opts.email;
161
169
  const app = express();
@@ -185,7 +193,7 @@ async function makeHarness(opts: HarnessOpts = {}) {
185
193
  const joinRequestJobs = new PluginJoinRequestJobs(joinRequestStore, {
186
194
  workflow,
187
195
  workspaceService,
188
- kbDirName: KB,
196
+ kb: testKbContext({ kbDirName: KB }),
189
197
  target: async (pluginKey) => {
190
198
  const entry = (await index.catalog()).find(
191
199
  (g) => pluginFolderBelowRoot(g.folders[0]) === pluginKey,
@@ -221,6 +229,7 @@ async function makeHarness(opts: HarnessOpts = {}) {
221
229
  joinRequestJobs,
222
230
  provision as never,
223
231
  async (req) => (req.userEmail ? { ...ALI_USER, email: req.userEmail } : null),
232
+ testKbContext({ kbDirName: KB }),
224
233
  ),
225
234
  );
226
235
 
@@ -8,6 +8,7 @@ import type { ISkillService, SkillSummary } from '../../skills/skills.contract.j
8
8
  import type { IToolManualService, ToolManualSummary } from '../../tool-manuals/tool-manuals.contract.js';
9
9
  import type { IPluginIndexService, PluginCatalogEntry } from '../plugins.contract.js';
10
10
  import { createTeamsRoutes } from '../teams.routes.js';
11
+ import { testKbContext } from '../../../__tests__/kb-context.js';
11
12
 
12
13
  /**
13
14
  * `GET /api/teams` — what each group can use, by id, sliced to what the
@@ -92,7 +93,7 @@ async function harness(opts: HarnessOpts = {}) {
92
93
  if (email) req.userEmail = email;
93
94
  next();
94
95
  });
95
- app.use('/api', createTeamsRoutes(accessControl, index, skills, tools));
96
+ app.use('/api', createTeamsRoutes(accessControl, index, skills, tools, testKbContext()));
96
97
  const server = await new Promise<Server>((resolve) => {
97
98
  const s = app.listen(0, () => resolve(s));
98
99
  });
@@ -1,9 +1,8 @@
1
1
  import path from 'node:path';
2
- import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
3
2
  import type { WorkspaceService } from '../../workspace/workspace.service.js';
4
3
  import type { IAccessControl } from '../../access/access-control.interface.js';
5
4
  import type { ISkillService } from '../../skills/skills.contract.js';
6
- import { workspaceIdForBranch } from '../../../shared/workspace-id.js';
5
+ import type { KbContext } from '../../../shared/kb-context.js';
7
6
  import type { PluginLinkIndex } from '../plugin-links.js';
8
7
  import type { PluginSource } from '../discovery/plugin-source.js';
9
8
  import type { ITreeWalker } from '../../../shared/fs.contract.js';
@@ -31,7 +30,7 @@ export class MarketplaceCompilerService {
31
30
  private readonly accessControl: IAccessControl,
32
31
  private readonly skillService: ISkillService,
33
32
  private readonly links: PluginLinkIndex,
34
- private readonly kbDirName: string,
33
+ private readonly kb: KbContext,
35
34
  private readonly marketplace: {
36
35
  name: string;
37
36
  owner: string;
@@ -45,6 +44,10 @@ export class MarketplaceCompilerService {
45
44
  private readonly gitRunner: IGitRunner,
46
45
  ) {}
47
46
 
47
+ private get kbDirName(): string {
48
+ return this.kb.kbDirName;
49
+ }
50
+
48
51
  /**
49
52
  * The default-branch commit the next compile would read. Throws when git
50
53
  * cannot say: the repo service keys freshness on this value, and a stable
@@ -108,7 +111,7 @@ export class MarketplaceCompilerService {
108
111
  // --- internal --------------------------------------------------------------
109
112
 
110
113
  private async checkout(): Promise<{ wsId: string; kbRoot: string }> {
111
- const wsId = (await this.workspaceService.getOrCreateForBranch(DEFAULT_BRANCH)).id;
114
+ const wsId = (await this.workspaceService.getOrCreateForBranch(this.kb.defaultBranch)).id;
112
115
  return { wsId, kbRoot: path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName) };
113
116
  }
114
117
 
@@ -144,8 +147,3 @@ export class MarketplaceCompilerService {
144
147
  };
145
148
  }
146
149
  }
147
-
148
- /** The default-branch workspace id compiles run against. */
149
- export function compileWorkspaceId(): string {
150
- return workspaceIdForBranch(DEFAULT_BRANCH);
151
- }
@@ -3,8 +3,12 @@ import { NodeFs } from '../../../kb-fs/node-fs.js';
3
3
  import fs from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
- import { DEFAULT_KB_LAYOUT, configureKbLayout, pluginDisplayNameOf } from '@bevel-software/platform-shared';
6
+ import { pluginDisplayNameOf } from '@bevel-software/platform-shared';
7
7
  import { KbPluginSource } from '../kb-plugin-source.js';
8
+ import { testKbContext } from '../../../../__tests__/kb-context.js';
9
+
10
+ /** The layout the source under test reads; a suite swaps it for the customer's names. */
11
+ let kbContext = testKbContext();
8
12
 
9
13
  /**
10
14
  * The customer's tree, in their own root names (lowercase `plugins/` and
@@ -37,7 +41,7 @@ describe('KbPluginSource — bundles', () => {
37
41
 
38
42
  beforeEach(async () => {
39
43
  kb = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-bundle-'));
40
- configureKbLayout({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' });
44
+ kbContext = testKbContext({ layout: { knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' } });
41
45
  await write('configs/mcp/registry.json', JSON.stringify(REGISTRY));
42
46
  await write(
43
47
  'plugins/functional/cluster-a/example-plugin/plugin.bundle.json',
@@ -66,12 +70,12 @@ describe('KbPluginSource — bundles', () => {
66
70
  await write('plugins/broken/plugin.bundle.json', '{ not json');
67
71
  });
68
72
  afterEach(async () => {
69
- configureKbLayout({ ...DEFAULT_KB_LAYOUT });
73
+ kbContext = testKbContext();
70
74
  await fs.rm(kb, { recursive: true, force: true });
71
75
  });
72
76
 
73
77
  it('finds bundles at any depth and reads them as plugins that link skill roots', async () => {
74
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
78
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
75
79
  const byName = new Map(plugins.map((p) => [p.name, p]));
76
80
  expect([...byName.keys()].sort()).toEqual(['close', 'example-plugin', 'ledger', 'padded', 'unnamed']);
77
81
  // The bundle dialect's own rule — a declared display name, else the
@@ -119,7 +123,7 @@ describe('KbPluginSource — bundles', () => {
119
123
  });
120
124
 
121
125
  it('expands mcpProfile through the registry, following extends and reporting unknowns', async () => {
122
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
126
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
123
127
  const example = plugins.find((p) => p.name === 'example-plugin')!;
124
128
  expect(example.mcpServers).toEqual({
125
129
  confluence: { type: 'streamable-http', url: 'https://mcp.confluence.example' },
@@ -156,7 +160,7 @@ describe('KbPluginSource — bundles', () => {
156
160
  ],
157
161
  }),
158
162
  );
159
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
163
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
160
164
  const example = plugins.find((p) => p.name === 'example-plugin')!;
161
165
  expect(example.mcpServers).toEqual({
162
166
  jira: { type: 'stdio', command: 'npx', args: ['-y', 'jira-mcp'] },
@@ -174,7 +178,7 @@ describe('KbPluginSource — bundles', () => {
174
178
 
175
179
  it('cuts an extends cycle instead of hanging, and keeps what it collected', async () => {
176
180
  await write('plugins/loop/plugin.bundle.json', JSON.stringify({ name: 'loop', mcpProfile: 'loop-a' }));
177
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
181
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
178
182
  const loop = plugins.find((p) => p.name === 'loop')!;
179
183
  expect(loop.mcpServers).toEqual({ 'flat-http': { type: 'streamable-http', url: 'https://flat.example' } });
180
184
  expect(warnings.some((w) => w.includes('cycle'))).toBe(true);
@@ -182,7 +186,7 @@ describe('KbPluginSource — bundles', () => {
182
186
 
183
187
  it('a missing registry leaves the plugins standing, servers-less, with a warning per profile', async () => {
184
188
  await fs.rm(path.join(kb, 'configs'), { recursive: true });
185
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
189
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
186
190
  expect(plugins.find((p) => p.name === 'example-plugin')!.mcpServers).toBeNull();
187
191
  expect(warnings.some((w) => w.includes('no registry'))).toBe(true);
188
192
  });
@@ -200,7 +204,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
200
204
  kb = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-mixed-'));
201
205
  });
202
206
  afterEach(async () => {
203
- configureKbLayout({ ...DEFAULT_KB_LAYOUT });
207
+ kbContext = testKbContext();
204
208
  await fs.rm(kb, { recursive: true, force: true });
205
209
  });
206
210
 
@@ -226,7 +230,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
226
230
  // beneath: a container, not a plugin.
227
231
  await write('Plugins/functional/access.md', '---\n---\nread:\n - everyone\n');
228
232
 
229
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
233
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
230
234
  // Folders are visited in locale order (case-insensitive), depth first.
231
235
  // Position means nothing: only the two files make a plugin.
232
236
  expect(plugins.map((p) => [p.name, p.folder, p.linksAreManaged, p.exists])).toEqual([
@@ -245,7 +249,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
245
249
  await write('Plugins/b/GTM/plugin.json', '{}');
246
250
  // Different spelling, same manifest slug: still one plugin.
247
251
  await write('Plugins/c/gtm/plugin.json', '{}');
248
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
252
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
249
253
  expect(plugins.map((p) => p.folder)).toEqual(['Plugins/a/GTM']);
250
254
  expect(warnings).toEqual([
251
255
  'Plugins/b/GTM: plugin name "gtm" is already used by Plugins/a/GTM — plugin skipped',
@@ -261,7 +265,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
261
265
  await write('Plugins/Nested/plugin.json', `{"name":${'['.repeat(20000)}${']'.repeat(20000)}}`);
262
266
  await write('Plugins/Ops/plugin.json', JSON.stringify({ name: 'Not An Identifier' }));
263
267
  await write('Plugins/Plain/plugin.json', '{}');
264
- const { plugins, warnings } = await new KbPluginSource(new NodeFs()).discover(kb);
268
+ const { plugins, warnings } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
265
269
  expect(plugins.map((p) => [p.name, p.displayName, p.folder])).toEqual([
266
270
  // Both fields present: both are read, and `Plugins/GTM` says nothing.
267
271
  ['go-to-market', 'Go To Market', 'Plugins/GTM'],
@@ -303,7 +307,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
303
307
  : (realReadFile as (f: string, o: unknown) => Promise<unknown>).call(fs, file, opts)) as never),
304
308
  ];
305
309
  try {
306
- const { plugins, warnings, unreadable } = await new KbPluginSource(new NodeFs()).discover(kb);
310
+ const { plugins, warnings, unreadable } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
307
311
  // The container could not be listed: nothing beneath it was seen. The
308
312
  // manifest could not be read: an identity nobody could see, so the
309
313
  // plugin is not listed under a guessed one. Both are holes, not absence.
@@ -331,7 +335,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
331
335
  ? Promise.reject(Object.assign(new Error('EACCES: permission denied'), { code: 'EACCES' }))
332
336
  : (realReadFile as (f: string, o: unknown) => Promise<unknown>).call(fs, file, opts)) as never);
333
337
  try {
334
- const { plugins, unreadable } = await new KbPluginSource(new NodeFs()).discover(kb);
338
+ const { plugins, unreadable } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
335
339
  expect(unreadable).toEqual(['Plugins/Hidden/plugin.json']);
336
340
  expect(plugins.map((p) => p.name)).toEqual(['gtm']);
337
341
  } finally {
@@ -348,7 +352,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
348
352
  ? Promise.reject(Object.assign(new Error('ENOENT: no such file'), { code: 'ENOENT' }))
349
353
  : (realReadFile as (f: string, o: unknown) => Promise<unknown>).call(fs, file, opts)) as never);
350
354
  try {
351
- const { plugins, unreadable } = await new KbPluginSource(new NodeFs()).discover(kb);
355
+ const { plugins, unreadable } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
352
356
  expect(plugins.map((p) => p.name)).toEqual(['gtm']);
353
357
  expect(unreadable).toEqual([]);
354
358
  } finally {
@@ -357,7 +361,7 @@ describe('KbPluginSource — one walk, both shapes', () => {
357
361
  });
358
362
 
359
363
  it('a knowledge base without a plugins root has no plugins and no complaint', async () => {
360
- const { plugins, warnings, unreadable } = await new KbPluginSource(new NodeFs()).discover(kb);
364
+ const { plugins, warnings, unreadable } = await new KbPluginSource(new NodeFs(), kbContext).discover(kb);
361
365
  expect(plugins).toEqual([]);
362
366
  expect(warnings).toEqual([]);
363
367
  expect(unreadable).toEqual([]);
@@ -1,6 +1,11 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { isPersonalPluginDir, normalizeSkillRoot, pluginManifestName } from '@bevel-software/platform-shared';
3
+ import {
4
+ isPersonalPluginDir,
5
+ normalizeSkillRoot,
6
+ pluginManifestName,
7
+ type KbLayout,
8
+ } from '@bevel-software/platform-shared';
4
9
  import { isAbsence } from '../../../../shared/fs.contract.js';
5
10
  import type { DiscoveredPlugin } from '../plugin-source.js';
6
11
  import { expandProfile, parseRegistry, type McpRegistry } from './registry.js';
@@ -67,6 +72,8 @@ export async function readBundlePlugin(
67
72
  registry: McpRegistry | null,
68
73
  warnings: string[],
69
74
  unreadable: string[],
75
+ /** Names the plugins root a personal folder sits directly under. */
76
+ layout: KbLayout,
70
77
  ): Promise<DiscoveredPlugin | null> {
71
78
  // Reading and parsing are two failures with two meanings: a file that
72
79
  // cannot be read is a plugin nobody could see (counted, so a writer can
@@ -149,7 +156,7 @@ export async function readBundlePlugin(
149
156
  relFolder,
150
157
  // The same rule as the native reader: a reserved personal folder directly
151
158
  // under the root is a place, not a plugin, whatever file it carries.
152
- personal: isPersonalPluginDir(folder),
159
+ personal: isPersonalPluginDir(folder, layout),
153
160
  exists: true,
154
161
  manifest,
155
162
  manifestText: null,