@bevel-software/platform-core-backend 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (799) hide show
  1. package/dist/core/catalog-cache-invalidation.d.ts +7 -2
  2. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  3. package/dist/core/catalog-cache-invalidation.js +8 -9
  4. package/dist/core/catalog-cache-invalidation.js.map +1 -1
  5. package/dist/core/core-ports.d.ts +19 -0
  6. package/dist/core/core-ports.d.ts.map +1 -1
  7. package/dist/core/core-ports.js.map +1 -1
  8. package/dist/core/create-core-server.d.ts +2 -0
  9. package/dist/core/create-core-server.d.ts.map +1 -1
  10. package/dist/core/create-core-server.js +45 -105
  11. package/dist/core/create-core-server.js.map +1 -1
  12. package/dist/core/create-core-services.d.ts +33 -5
  13. package/dist/core/create-core-services.d.ts.map +1 -1
  14. package/dist/core/create-core-services.js +146 -82
  15. package/dist/core/create-core-services.js.map +1 -1
  16. package/dist/core/lifecycle.d.ts +74 -28
  17. package/dist/core/lifecycle.d.ts.map +1 -1
  18. package/dist/core/lifecycle.js +157 -20
  19. package/dist/core/lifecycle.js.map +1 -1
  20. package/dist/core/public-config.d.ts +5 -1
  21. package/dist/core/public-config.d.ts.map +1 -1
  22. package/dist/core/public-config.js +7 -5
  23. package/dist/core/public-config.js.map +1 -1
  24. package/dist/core-config.d.ts +95 -1
  25. package/dist/core-config.d.ts.map +1 -1
  26. package/dist/core-config.js +24 -8
  27. package/dist/core-config.js.map +1 -1
  28. package/dist/index.d.ts +5 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +6 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/modules/access/access-control.service.d.ts +18 -1
  33. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  34. package/dist/modules/access/access-control.service.js +19 -8
  35. package/dist/modules/access/access-control.service.js.map +1 -1
  36. package/dist/modules/access/access.routes.d.ts +2 -1
  37. package/dist/modules/access/access.routes.d.ts.map +1 -1
  38. package/dist/modules/access/access.routes.js +10 -10
  39. package/dist/modules/access/access.routes.js.map +1 -1
  40. package/dist/modules/access/change-read-gate.d.ts +6 -3
  41. package/dist/modules/access/change-read-gate.d.ts.map +1 -1
  42. package/dist/modules/access/change-read-gate.js +9 -6
  43. package/dist/modules/access/change-read-gate.js.map +1 -1
  44. package/dist/modules/access/creator-access.d.ts +4 -2
  45. package/dist/modules/access/creator-access.d.ts.map +1 -1
  46. package/dist/modules/access/creator-access.js +7 -4
  47. package/dist/modules/access/creator-access.js.map +1 -1
  48. package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
  49. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
  50. package/dist/modules/agent-instructions/read-preamble.js +6 -9
  51. package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
  52. package/dist/modules/audit/agent-audit.service.d.ts +54 -0
  53. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -0
  54. package/dist/modules/audit/agent-audit.service.js +290 -0
  55. package/dist/modules/audit/agent-audit.service.js.map +1 -0
  56. package/dist/modules/audit/agent-rest-audit.middleware.d.ts +40 -0
  57. package/dist/modules/audit/agent-rest-audit.middleware.d.ts.map +1 -0
  58. package/dist/modules/audit/agent-rest-audit.middleware.js +75 -0
  59. package/dist/modules/audit/agent-rest-audit.middleware.js.map +1 -0
  60. package/dist/modules/audit/audit.contract.d.ts +145 -0
  61. package/dist/modules/audit/audit.contract.d.ts.map +1 -0
  62. package/dist/modules/audit/audit.contract.js +35 -0
  63. package/dist/modules/audit/audit.contract.js.map +1 -0
  64. package/dist/modules/audit/audit.routes.d.ts +21 -0
  65. package/dist/modules/audit/audit.routes.d.ts.map +1 -0
  66. package/dist/modules/audit/audit.routes.js +157 -0
  67. package/dist/modules/audit/audit.routes.js.map +1 -0
  68. package/dist/modules/audit/event-classifier.d.ts +52 -0
  69. package/dist/modules/audit/event-classifier.d.ts.map +1 -0
  70. package/dist/modules/audit/event-classifier.js +123 -0
  71. package/dist/modules/audit/event-classifier.js.map +1 -0
  72. package/dist/modules/audit/index.d.ts +7 -0
  73. package/dist/modules/audit/index.d.ts.map +1 -0
  74. package/dist/modules/audit/index.js +7 -0
  75. package/dist/modules/audit/index.js.map +1 -0
  76. package/dist/modules/audit/request-audit.d.ts +57 -0
  77. package/dist/modules/audit/request-audit.d.ts.map +1 -0
  78. package/dist/modules/audit/request-audit.js +117 -0
  79. package/dist/modules/audit/request-audit.js.map +1 -0
  80. package/dist/modules/auth/account-admission.d.ts +40 -0
  81. package/dist/modules/auth/account-admission.d.ts.map +1 -0
  82. package/dist/modules/auth/account-admission.js +17 -0
  83. package/dist/modules/auth/account-admission.js.map +1 -0
  84. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  85. package/dist/modules/auth/account.routes.js +4 -1
  86. package/dist/modules/auth/account.routes.js.map +1 -1
  87. package/dist/modules/auth/auth.service.d.ts +19 -1
  88. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  89. package/dist/modules/auth/auth.service.js +33 -1
  90. package/dist/modules/auth/auth.service.js.map +1 -1
  91. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  92. package/dist/modules/auth/oidc-auth-provider.js +9 -0
  93. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  94. package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
  95. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  96. package/dist/modules/connection-probe/connection-probe.service.js +10 -2
  97. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  98. package/dist/modules/database/advisory-lock.d.ts +17 -0
  99. package/dist/modules/database/advisory-lock.d.ts.map +1 -1
  100. package/dist/modules/database/advisory-lock.js +24 -3
  101. package/dist/modules/database/advisory-lock.js.map +1 -1
  102. package/dist/modules/database/connection.d.ts +67 -2
  103. package/dist/modules/database/connection.d.ts.map +1 -1
  104. package/dist/modules/database/connection.js +118 -12
  105. package/dist/modules/database/connection.js.map +1 -1
  106. package/dist/modules/database/core-schema.d.ts +414 -0
  107. package/dist/modules/database/core-schema.d.ts.map +1 -1
  108. package/dist/modules/database/core-schema.js +99 -0
  109. package/dist/modules/database/core-schema.js.map +1 -1
  110. package/dist/modules/database/migrate.d.ts +1 -1
  111. package/dist/modules/database/migrate.d.ts.map +1 -1
  112. package/dist/modules/database/migrate.js +27 -3
  113. package/dist/modules/database/migrate.js.map +1 -1
  114. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  115. package/dist/modules/diff/diff.service.js +6 -1
  116. package/dist/modules/diff/diff.service.js.map +1 -1
  117. package/dist/modules/kb-fs/branch-name.d.ts +0 -1
  118. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  119. package/dist/modules/kb-fs/branch-name.js +3 -3
  120. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  121. package/dist/modules/kb-fs/clone-config.d.ts +9 -8
  122. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
  123. package/dist/modules/kb-fs/clone-config.js +14 -12
  124. package/dist/modules/kb-fs/clone-config.js.map +1 -1
  125. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +9 -1
  126. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -1
  127. package/dist/modules/kb-fs/git-guarded-filesystem.js +16 -3
  128. package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -1
  129. package/dist/modules/mcp/mcp-auth.middleware.d.ts +1 -0
  130. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  131. package/dist/modules/mcp/mcp-auth.middleware.js +32 -0
  132. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  133. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  134. package/dist/modules/mcp/mcp.routes.js +8 -1
  135. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  136. package/dist/modules/mcp/mcp.service.d.ts +16 -1
  137. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  138. package/dist/modules/mcp/mcp.service.js +52 -9
  139. package/dist/modules/mcp/mcp.service.js.map +1 -1
  140. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +59 -0
  141. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  142. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +180 -8
  143. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  144. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
  145. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  146. package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
  147. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  148. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
  149. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  150. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
  151. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  152. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
  153. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  154. package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
  155. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  156. package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
  157. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  158. package/dist/modules/plugins/discovery/native.source.js +4 -2
  159. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  160. package/dist/modules/plugins/index.d.ts +1 -1
  161. package/dist/modules/plugins/index.d.ts.map +1 -1
  162. package/dist/modules/plugins/index.js +1 -1
  163. package/dist/modules/plugins/index.js.map +1 -1
  164. package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
  165. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  166. package/dist/modules/plugins/join-request-jobs.service.js +6 -6
  167. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  168. package/dist/modules/plugins/join-requests.service.d.ts +3 -1
  169. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  170. package/dist/modules/plugins/join-requests.service.js +9 -8
  171. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  172. package/dist/modules/plugins/plugin-links.d.ts +65 -10
  173. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  174. package/dist/modules/plugins/plugin-links.js +136 -31
  175. package/dist/modules/plugins/plugin-links.js.map +1 -1
  176. package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
  177. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  178. package/dist/modules/plugins/plugin-links.service.js +151 -21
  179. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  180. package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
  181. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  182. package/dist/modules/plugins/plugin-provision.service.js +33 -26
  183. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  184. package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
  185. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  186. package/dist/modules/plugins/plugin-rename.service.js +10 -12
  187. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  188. package/dist/modules/plugins/plugins.routes.d.ts +3 -0
  189. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  190. package/dist/modules/plugins/plugins.routes.js +45 -15
  191. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  192. package/dist/modules/plugins/plugins.service.d.ts +4 -4
  193. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  194. package/dist/modules/plugins/plugins.service.js +7 -10
  195. package/dist/modules/plugins/plugins.service.js.map +1 -1
  196. package/dist/modules/plugins/teams.routes.d.ts +4 -1
  197. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  198. package/dist/modules/plugins/teams.routes.js +11 -9
  199. package/dist/modules/plugins/teams.routes.js.map +1 -1
  200. package/dist/modules/secrets-vault/index.d.ts +1 -1
  201. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  202. package/dist/modules/secrets-vault/index.js +1 -1
  203. package/dist/modules/secrets-vault/index.js.map +1 -1
  204. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
  205. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  206. package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
  207. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  208. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
  209. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  210. package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
  211. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  212. package/dist/modules/settings/deployment-settings.service.d.ts +22 -10
  213. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  214. package/dist/modules/settings/deployment-settings.service.js +52 -28
  215. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  216. package/dist/modules/settings/setup.routes.d.ts +9 -2
  217. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  218. package/dist/modules/settings/setup.routes.js +20 -14
  219. package/dist/modules/settings/setup.routes.js.map +1 -1
  220. package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
  221. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
  222. package/dist/modules/skills/allowed-tools-check.js +8 -8
  223. package/dist/modules/skills/allowed-tools-check.js.map +1 -1
  224. package/dist/modules/skills/pending-skills.service.d.ts +3 -1
  225. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  226. package/dist/modules/skills/pending-skills.service.js +9 -6
  227. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  228. package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -1
  229. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  230. package/dist/modules/skills/skill-access-requests.routes.js +6 -6
  231. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  232. package/dist/modules/skills/skills.routes.d.ts +4 -1
  233. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  234. package/dist/modules/skills/skills.routes.js +4 -4
  235. package/dist/modules/skills/skills.routes.js.map +1 -1
  236. package/dist/modules/skills/skills.service.d.ts +4 -2
  237. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  238. package/dist/modules/skills/skills.service.js +13 -11
  239. package/dist/modules/skills/skills.service.js.map +1 -1
  240. package/dist/modules/tool-auth/external-api-key.interface.d.ts +21 -5
  241. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  242. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  243. package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
  244. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  245. package/dist/modules/tool-auth/external-api-key.service.js +22 -9
  246. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  247. package/dist/modules/tool-auth/internal-token.service.d.ts +10 -0
  248. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  249. package/dist/modules/tool-auth/internal-token.service.js +1 -0
  250. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  251. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +8 -0
  252. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  253. package/dist/modules/tool-auth/tool-auth.middleware.js +9 -1
  254. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  255. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
  256. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  257. package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
  258. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  259. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
  260. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  261. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
  262. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  263. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
  264. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  265. package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
  266. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  267. package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
  268. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
  269. package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
  270. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  271. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
  272. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  273. package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
  274. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  275. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
  276. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  277. package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
  278. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  279. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
  280. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  281. package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
  282. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  283. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
  284. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  285. package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -9
  286. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  287. package/dist/modules/workflow/git/git.service.d.ts +11 -5
  288. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  289. package/dist/modules/workflow/git/git.service.js +23 -14
  290. package/dist/modules/workflow/git/git.service.js.map +1 -1
  291. package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
  292. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  293. package/dist/modules/workflow/git/node-git-runner.js +35 -5
  294. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  295. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  296. package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
  297. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  298. package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
  299. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
  300. package/dist/modules/workflow/session-ontology.service.js +6 -2
  301. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  302. package/dist/modules/workflow/workflow.service.d.ts +8 -3
  303. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  304. package/dist/modules/workflow/workflow.service.js +33 -23
  305. package/dist/modules/workflow/workflow.service.js.map +1 -1
  306. package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
  307. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  308. package/dist/modules/workspace/session-ontology.gate.js +3 -3
  309. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  310. package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
  311. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  312. package/dist/modules/workspace/startup/kb-git.js +15 -13
  313. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  314. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +5 -8
  315. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  316. package/dist/modules/workspace/startup/kb-startup-runner.js +35 -38
  317. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  318. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
  319. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  320. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
  321. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  322. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
  323. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
  324. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
  325. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
  326. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
  327. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
  328. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
  329. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
  330. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  331. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  332. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
  333. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  334. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
  335. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  336. package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
  337. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  338. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
  339. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  340. package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
  341. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  342. package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
  343. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  344. package/dist/modules/workspace/startup/steps/template-source.js +10 -6
  345. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  346. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  347. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  348. package/dist/modules/workspace/workspace.routes.js +8 -7
  349. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  350. package/dist/modules/workspace/workspace.service.d.ts +26 -12
  351. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  352. package/dist/modules/workspace/workspace.service.js +41 -28
  353. package/dist/modules/workspace/workspace.service.js.map +1 -1
  354. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  355. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  356. package/dist/modules/workspace/workspace.tools.js +69 -31
  357. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  358. package/dist/modules/workspace/write-denial.d.ts +3 -2
  359. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  360. package/dist/modules/workspace/write-denial.js +6 -5
  361. package/dist/modules/workspace/write-denial.js.map +1 -1
  362. package/dist/shared/git-internals.d.ts +7 -1
  363. package/dist/shared/git-internals.d.ts.map +1 -1
  364. package/dist/shared/git-internals.js +67 -9
  365. package/dist/shared/git-internals.js.map +1 -1
  366. package/dist/shared/git.contract.d.ts +52 -7
  367. package/dist/shared/git.contract.d.ts.map +1 -1
  368. package/dist/shared/git.contract.js +30 -8
  369. package/dist/shared/git.contract.js.map +1 -1
  370. package/dist/shared/kb-context.d.ts +76 -0
  371. package/dist/shared/kb-context.d.ts.map +1 -0
  372. package/dist/shared/kb-context.js +108 -0
  373. package/dist/shared/kb-context.js.map +1 -0
  374. package/dist/shared/kb-layout.d.ts +3 -2
  375. package/dist/shared/kb-layout.d.ts.map +1 -1
  376. package/dist/shared/kb-layout.js +4 -3
  377. package/dist/shared/kb-layout.js.map +1 -1
  378. package/dist/shared/kb-layout.test.js +35 -27
  379. package/dist/shared/kb-layout.test.js.map +1 -1
  380. package/dist/shared/pending-proposals.d.ts +4 -1
  381. package/dist/shared/pending-proposals.d.ts.map +1 -1
  382. package/dist/shared/pending-proposals.js +3 -4
  383. package/dist/shared/pending-proposals.js.map +1 -1
  384. package/dist/shared/redact-secret.d.ts +5 -5
  385. package/dist/shared/redact-secret.js +5 -5
  386. package/dist/tenancy/index.d.ts +13 -0
  387. package/dist/tenancy/index.d.ts.map +1 -0
  388. package/dist/tenancy/index.js +13 -0
  389. package/dist/tenancy/index.js.map +1 -0
  390. package/dist/tenancy/static-tenant-source.d.ts +98 -0
  391. package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
  392. package/dist/tenancy/static-tenant-source.js +220 -0
  393. package/dist/tenancy/static-tenant-source.js.map +1 -0
  394. package/dist/tenancy/tenant-host.d.ts +80 -0
  395. package/dist/tenancy/tenant-host.d.ts.map +1 -0
  396. package/dist/tenancy/tenant-host.js +217 -0
  397. package/dist/tenancy/tenant-host.js.map +1 -0
  398. package/dist/tenancy/tenant-runtime.d.ts +91 -0
  399. package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
  400. package/dist/tenancy/tenant-runtime.js +174 -0
  401. package/dist/tenancy/tenant-runtime.js.map +1 -0
  402. package/dist/tenancy/tenant-secrets.d.ts +24 -0
  403. package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
  404. package/dist/tenancy/tenant-secrets.js +30 -0
  405. package/dist/tenancy/tenant-secrets.js.map +1 -0
  406. package/dist/tenancy/tenant-source.contract.d.ts +45 -0
  407. package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
  408. package/dist/tenancy/tenant-source.contract.js +21 -0
  409. package/dist/tenancy/tenant-source.contract.js.map +1 -0
  410. package/migrations/0000_core_init.sql +14 -14
  411. package/migrations/0003_deployment_settings.sql +1 -1
  412. package/migrations/0005_github_facade.sql +1 -1
  413. package/migrations/0012_agent_audit.sql +41 -0
  414. package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -0
  415. package/migrations/meta/0012_snapshot.json +2247 -0
  416. package/migrations/meta/0013_snapshot.json +2259 -0
  417. package/migrations/meta/_journal.json +14 -0
  418. package/package.json +4 -4
  419. package/src/__tests__/branch-model.test.ts +7 -6
  420. package/src/__tests__/kb-context.ts +30 -0
  421. package/src/__tests__/kb-layout-config.test.ts +35 -30
  422. package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
  423. package/src/core/__tests__/lifecycle.test.ts +78 -1
  424. package/src/core/__tests__/public-config.test.ts +5 -1
  425. package/src/core/catalog-cache-invalidation.ts +16 -11
  426. package/src/core/core-ports.ts +19 -0
  427. package/src/core/create-core-server.ts +55 -106
  428. package/src/core/create-core-services.ts +193 -95
  429. package/src/core/lifecycle.ts +214 -29
  430. package/src/core/public-config.ts +8 -5
  431. package/src/core-config.ts +107 -8
  432. package/src/index.ts +25 -2
  433. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
  434. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
  435. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
  436. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
  437. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  438. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
  439. package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
  440. package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
  441. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
  442. package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
  443. package/src/modules/access/__tests__/creator-access.test.ts +3 -2
  444. package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
  445. package/src/modules/access/access-control.service.ts +20 -6
  446. package/src/modules/access/access.routes.ts +10 -13
  447. package/src/modules/access/change-read-gate.ts +10 -4
  448. package/src/modules/access/creator-access.ts +7 -2
  449. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
  450. package/src/modules/agent-instructions/read-preamble.ts +10 -12
  451. package/src/modules/audit/__tests__/agent-audit.service.test.ts +370 -0
  452. package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -0
  453. package/src/modules/audit/__tests__/audit.routes.test.ts +201 -0
  454. package/src/modules/audit/__tests__/event-classifier.test.ts +121 -0
  455. package/src/modules/audit/agent-audit.service.ts +316 -0
  456. package/src/modules/audit/agent-rest-audit.middleware.ts +83 -0
  457. package/src/modules/audit/audit.contract.ts +164 -0
  458. package/src/modules/audit/audit.routes.ts +168 -0
  459. package/src/modules/audit/event-classifier.ts +155 -0
  460. package/src/modules/audit/index.ts +14 -0
  461. package/src/modules/audit/request-audit.ts +141 -0
  462. package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
  463. package/src/modules/auth/__tests__/auth.service.test.ts +60 -0
  464. package/src/modules/auth/account-admission.ts +44 -0
  465. package/src/modules/auth/account.routes.ts +4 -1
  466. package/src/modules/auth/auth.service.ts +33 -1
  467. package/src/modules/auth/oidc-auth-provider.ts +9 -0
  468. package/src/modules/connection-probe/connection-probe.service.ts +7 -1
  469. package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
  470. package/src/modules/database/__tests__/connection.test.ts +98 -0
  471. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
  472. package/src/modules/database/advisory-lock.ts +28 -4
  473. package/src/modules/database/connection.ts +143 -13
  474. package/src/modules/database/core-schema.ts +104 -0
  475. package/src/modules/database/migrate.ts +48 -14
  476. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  477. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  478. package/src/modules/diff/diff.service.ts +6 -1
  479. package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
  480. package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
  481. package/src/modules/kb-fs/branch-name.ts +3 -3
  482. package/src/modules/kb-fs/clone-config.ts +15 -12
  483. package/src/modules/kb-fs/git-guarded-filesystem.ts +16 -3
  484. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
  485. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +183 -13
  486. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -2
  487. package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +24 -4
  488. package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +1 -1
  489. package/src/modules/mcp/__tests__/mcp.service.test.ts +120 -3
  490. package/src/modules/mcp/mcp-auth.middleware.ts +31 -0
  491. package/src/modules/mcp/mcp.routes.ts +11 -1
  492. package/src/modules/mcp/mcp.service.ts +73 -8
  493. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +184 -6
  494. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
  495. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
  496. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
  497. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
  498. package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -1
  499. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
  500. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
  501. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
  502. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
  503. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
  504. package/src/modules/plugins/__tests__/plugins.routes.test.ts +11 -2
  505. package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
  506. package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
  507. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
  508. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
  509. package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
  510. package/src/modules/plugins/discovery/native.source.ts +4 -1
  511. package/src/modules/plugins/index.ts +1 -1
  512. package/src/modules/plugins/join-request-jobs.service.ts +8 -7
  513. package/src/modules/plugins/join-requests.service.ts +7 -7
  514. package/src/modules/plugins/plugin-links.service.ts +192 -19
  515. package/src/modules/plugins/plugin-links.ts +147 -27
  516. package/src/modules/plugins/plugin-provision.service.ts +54 -27
  517. package/src/modules/plugins/plugin-rename.service.ts +10 -10
  518. package/src/modules/plugins/plugins.routes.ts +45 -15
  519. package/src/modules/plugins/plugins.service.ts +7 -11
  520. package/src/modules/plugins/teams.routes.ts +16 -8
  521. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
  522. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
  523. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
  524. package/src/modules/secrets-vault/index.ts +2 -0
  525. package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
  526. package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
  527. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +59 -11
  528. package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
  529. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
  530. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
  531. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
  532. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
  533. package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
  534. package/src/modules/settings/deployment-settings.service.ts +68 -28
  535. package/src/modules/settings/setup.routes.ts +26 -16
  536. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
  537. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
  538. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
  539. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +2 -1
  540. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
  541. package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
  542. package/src/modules/skills/allowed-tools-check.ts +7 -6
  543. package/src/modules/skills/pending-skills.service.ts +9 -8
  544. package/src/modules/skills/skill-access-requests.routes.ts +7 -7
  545. package/src/modules/skills/skills.routes.ts +4 -3
  546. package/src/modules/skills/skills.service.ts +13 -9
  547. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +29 -8
  548. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +6 -0
  549. package/src/modules/tool-auth/external-api-key.interface.ts +22 -5
  550. package/src/modules/tool-auth/external-api-key.service.ts +25 -8
  551. package/src/modules/tool-auth/internal-token.service.ts +11 -0
  552. package/src/modules/tool-auth/tool-auth.middleware.ts +17 -1
  553. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
  554. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
  555. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
  556. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  557. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
  558. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
  559. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
  560. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
  561. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
  562. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
  563. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
  564. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
  565. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
  566. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
  567. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
  568. package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
  569. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
  570. package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
  571. package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
  572. package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
  573. package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
  574. package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
  575. package/src/modules/workflow/__tests__/apply-failure.test.ts +2 -1
  576. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
  577. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
  578. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
  579. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
  580. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
  581. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
  582. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
  583. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +28 -27
  584. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
  585. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
  586. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
  587. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
  588. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
  589. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +2 -1
  590. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +2 -1
  591. package/src/modules/workflow/agent-tools/workflow.tools.ts +11 -9
  592. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
  593. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
  594. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
  595. package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
  596. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
  597. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
  598. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
  599. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
  600. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
  601. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
  602. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
  603. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
  604. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
  605. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
  606. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
  607. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
  608. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
  609. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
  610. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +9 -8
  611. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
  612. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
  613. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
  614. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
  615. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
  616. package/src/modules/workflow/git/git.service.ts +24 -13
  617. package/src/modules/workflow/git/node-git-runner.ts +37 -4
  618. package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
  619. package/src/modules/workflow/session-ontology.service.ts +7 -2
  620. package/src/modules/workflow/workflow.service.ts +31 -24
  621. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
  622. package/src/modules/workspace/__tests__/git-internals.security.test.ts +144 -52
  623. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
  624. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
  625. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
  626. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
  627. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
  628. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
  629. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
  630. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
  631. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
  632. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
  633. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
  634. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  635. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  636. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  637. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  638. package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
  639. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
  640. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
  641. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +4 -3
  642. package/src/modules/workspace/__tests__/workspace.tools.test.ts +9 -8
  643. package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
  644. package/src/modules/workspace/session-ontology.gate.ts +6 -4
  645. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
  646. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +29 -21
  647. package/src/modules/workspace/startup/kb-git.ts +15 -22
  648. package/src/modules/workspace/startup/kb-startup-runner.ts +40 -46
  649. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
  650. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
  651. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
  652. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
  653. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
  654. package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
  655. package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
  656. package/src/modules/workspace/startup/steps/template-source.ts +8 -5
  657. package/src/modules/workspace/workspace.routes.ts +8 -7
  658. package/src/modules/workspace/workspace.service.ts +39 -27
  659. package/src/modules/workspace/workspace.tools.ts +68 -31
  660. package/src/modules/workspace/write-denial.ts +7 -5
  661. package/src/shared/__tests__/git-internals.test.ts +41 -0
  662. package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
  663. package/src/shared/git-internals.ts +61 -6
  664. package/src/shared/git.contract.ts +70 -8
  665. package/src/shared/kb-context.ts +130 -0
  666. package/src/shared/kb-layout.test.ts +37 -27
  667. package/src/shared/kb-layout.ts +4 -3
  668. package/src/shared/pending-proposals.ts +7 -4
  669. package/src/shared/redact-secret.ts +5 -5
  670. package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
  671. package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
  672. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
  673. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
  674. package/src/tenancy/index.ts +35 -0
  675. package/src/tenancy/static-tenant-source.ts +296 -0
  676. package/src/tenancy/tenant-host.ts +261 -0
  677. package/src/tenancy/tenant-runtime.ts +208 -0
  678. package/src/tenancy/tenant-secrets.ts +41 -0
  679. package/src/tenancy/tenant-source.contract.ts +59 -0
  680. package/dist/modules/access/access-errors.d.ts +0 -34
  681. package/dist/modules/access/access-errors.d.ts.map +0 -1
  682. package/dist/modules/access/access-errors.js +0 -40
  683. package/dist/modules/access/access-errors.js.map +0 -1
  684. package/dist/modules/access/access-splice.d.ts +0 -140
  685. package/dist/modules/access/access-splice.d.ts.map +0 -1
  686. package/dist/modules/access/access-splice.js +0 -389
  687. package/dist/modules/access/access-splice.js.map +0 -1
  688. package/dist/modules/access/group-files.d.ts +0 -83
  689. package/dist/modules/access/group-files.d.ts.map +0 -1
  690. package/dist/modules/access/group-files.js +0 -167
  691. package/dist/modules/access/group-files.js.map +0 -1
  692. package/dist/modules/access/kb-read-filter.d.ts +0 -41
  693. package/dist/modules/access/kb-read-filter.d.ts.map +0 -1
  694. package/dist/modules/access/kb-read-filter.js +0 -60
  695. package/dist/modules/access/kb-read-filter.js.map +0 -1
  696. package/dist/modules/access/render-roles-yaml.d.ts +0 -22
  697. package/dist/modules/access/render-roles-yaml.d.ts.map +0 -1
  698. package/dist/modules/access/render-roles-yaml.js +0 -56
  699. package/dist/modules/access/render-roles-yaml.js.map +0 -1
  700. package/dist/modules/access/roles-yaml-guard.d.ts +0 -56
  701. package/dist/modules/access/roles-yaml-guard.d.ts.map +0 -1
  702. package/dist/modules/access/roles-yaml-guard.js +0 -79
  703. package/dist/modules/access/roles-yaml-guard.js.map +0 -1
  704. package/dist/modules/diff/diff-paths.d.ts +0 -2
  705. package/dist/modules/diff/diff-paths.d.ts.map +0 -1
  706. package/dist/modules/diff/diff-paths.js +0 -9
  707. package/dist/modules/diff/diff-paths.js.map +0 -1
  708. package/dist/modules/groups/group-provision.service.d.ts +0 -130
  709. package/dist/modules/groups/group-provision.service.d.ts.map +0 -1
  710. package/dist/modules/groups/group-provision.service.js +0 -288
  711. package/dist/modules/groups/group-provision.service.js.map +0 -1
  712. package/dist/modules/groups/groups.contract.d.ts +0 -106
  713. package/dist/modules/groups/groups.contract.d.ts.map +0 -1
  714. package/dist/modules/groups/groups.contract.js +0 -36
  715. package/dist/modules/groups/groups.contract.js.map +0 -1
  716. package/dist/modules/groups/groups.routes.d.ts +0 -42
  717. package/dist/modules/groups/groups.routes.d.ts.map +0 -1
  718. package/dist/modules/groups/groups.routes.js +0 -379
  719. package/dist/modules/groups/groups.routes.js.map +0 -1
  720. package/dist/modules/groups/groups.service.d.ts +0 -60
  721. package/dist/modules/groups/groups.service.d.ts.map +0 -1
  722. package/dist/modules/groups/groups.service.js +0 -172
  723. package/dist/modules/groups/groups.service.js.map +0 -1
  724. package/dist/modules/groups/index.d.ts +0 -7
  725. package/dist/modules/groups/index.d.ts.map +0 -1
  726. package/dist/modules/groups/index.js +0 -6
  727. package/dist/modules/groups/index.js.map +0 -1
  728. package/dist/modules/groups/join-proposals.d.ts +0 -53
  729. package/dist/modules/groups/join-proposals.d.ts.map +0 -1
  730. package/dist/modules/groups/join-proposals.js +0 -67
  731. package/dist/modules/groups/join-proposals.js.map +0 -1
  732. package/dist/modules/groups/join-requests.service.d.ts +0 -81
  733. package/dist/modules/groups/join-requests.service.d.ts.map +0 -1
  734. package/dist/modules/groups/join-requests.service.js +0 -135
  735. package/dist/modules/groups/join-requests.service.js.map +0 -1
  736. package/dist/modules/mcp/mcp-session-store.d.ts +0 -74
  737. package/dist/modules/mcp/mcp-session-store.d.ts.map +0 -1
  738. package/dist/modules/mcp/mcp-session-store.js +0 -131
  739. package/dist/modules/mcp/mcp-session-store.js.map +0 -1
  740. package/dist/modules/workflow/file-change-notifier.d.ts +0 -38
  741. package/dist/modules/workflow/file-change-notifier.d.ts.map +0 -1
  742. package/dist/modules/workflow/file-change-notifier.js +0 -22
  743. package/dist/modules/workflow/file-change-notifier.js.map +0 -1
  744. package/dist/modules/workflow/git/branch-name.d.ts +0 -10
  745. package/dist/modules/workflow/git/branch-name.d.ts.map +0 -1
  746. package/dist/modules/workflow/git/branch-name.js +0 -76
  747. package/dist/modules/workflow/git/branch-name.js.map +0 -1
  748. package/dist/modules/workflow/git/clone-config.d.ts +0 -61
  749. package/dist/modules/workflow/git/clone-config.d.ts.map +0 -1
  750. package/dist/modules/workflow/git/clone-config.js +0 -69
  751. package/dist/modules/workflow/git/clone-config.js.map +0 -1
  752. package/dist/modules/workflow/git/mutex.d.ts +0 -11
  753. package/dist/modules/workflow/git/mutex.d.ts.map +0 -1
  754. package/dist/modules/workflow/git/mutex.js +0 -23
  755. package/dist/modules/workflow/git/mutex.js.map +0 -1
  756. package/dist/modules/workflow/locking-filesystem.d.ts +0 -137
  757. package/dist/modules/workflow/locking-filesystem.d.ts.map +0 -1
  758. package/dist/modules/workflow/locking-filesystem.js +0 -553
  759. package/dist/modules/workflow/locking-filesystem.js.map +0 -1
  760. package/dist/modules/workflow/read-only-filesystem.d.ts +0 -24
  761. package/dist/modules/workflow/read-only-filesystem.d.ts.map +0 -1
  762. package/dist/modules/workflow/read-only-filesystem.js +0 -39
  763. package/dist/modules/workflow/read-only-filesystem.js.map +0 -1
  764. package/dist/modules/workflow/workflow.errors.d.ts +0 -197
  765. package/dist/modules/workflow/workflow.errors.d.ts.map +0 -1
  766. package/dist/modules/workflow/workflow.errors.js +0 -298
  767. package/dist/modules/workflow/workflow.errors.js.map +0 -1
  768. package/dist/modules/workspace/bevel-ignore.d.ts +0 -30
  769. package/dist/modules/workspace/bevel-ignore.d.ts.map +0 -1
  770. package/dist/modules/workspace/bevel-ignore.js +0 -61
  771. package/dist/modules/workspace/bevel-ignore.js.map +0 -1
  772. package/dist/modules/workspace/kb-seed.interface.d.ts +0 -36
  773. package/dist/modules/workspace/kb-seed.interface.d.ts.map +0 -1
  774. package/dist/modules/workspace/kb-seed.interface.js +0 -2
  775. package/dist/modules/workspace/kb-seed.interface.js.map +0 -1
  776. package/dist/modules/workspace/kb-seed.service.d.ts +0 -125
  777. package/dist/modules/workspace/kb-seed.service.d.ts.map +0 -1
  778. package/dist/modules/workspace/kb-seed.service.js +0 -535
  779. package/dist/modules/workspace/kb-seed.service.js.map +0 -1
  780. package/dist/modules/workspace/plugins-migration.d.ts +0 -50
  781. package/dist/modules/workspace/plugins-migration.d.ts.map +0 -1
  782. package/dist/modules/workspace/plugins-migration.js +0 -379
  783. package/dist/modules/workspace/plugins-migration.js.map +0 -1
  784. package/dist/shared/fs-errors.d.ts +0 -8
  785. package/dist/shared/fs-errors.d.ts.map +0 -1
  786. package/dist/shared/fs-errors.js +0 -11
  787. package/dist/shared/fs-errors.js.map +0 -1
  788. package/dist/shared/fs-walk.d.ts +0 -23
  789. package/dist/shared/fs-walk.d.ts.map +0 -1
  790. package/dist/shared/fs-walk.js +0 -42
  791. package/dist/shared/fs-walk.js.map +0 -1
  792. package/dist/shared/hash-email.d.ts +0 -11
  793. package/dist/shared/hash-email.d.ts.map +0 -1
  794. package/dist/shared/hash-email.js +0 -14
  795. package/dist/shared/hash-email.js.map +0 -1
  796. package/dist/shared/kb-walk.d.ts +0 -52
  797. package/dist/shared/kb-walk.d.ts.map +0 -1
  798. package/dist/shared/kb-walk.js +0 -76
  799. package/dist/shared/kb-walk.js.map +0 -1
@@ -9,7 +9,6 @@ import type { IAdminAccessService } from '../admin/admin.interface.js';
9
9
  import express from 'express';
10
10
  import type { AuthUser, IWorkflowService } from '@bevel-software/platform-shared';
11
11
  import {
12
- DEFAULT_BRANCH,
13
12
  KNOWLEDGE_DIR,
14
13
  canonicalRelativePath,
15
14
  folderPlaceholderPath,
@@ -21,6 +20,7 @@ import {
21
20
  } from '@bevel-software/platform-shared';
22
21
  import { FolderTooLargeError, type ReadTreeFilter } from './workspace.service.js';
23
22
  import { branchForWorkspaceId } from '../../shared/workspace-id.js';
23
+ import type { KbContext } from '../../shared/kb-context.js';
24
24
  import { EntryExistsError, type WorkspaceService } from './workspace.service.js';
25
25
  import type { AuthService } from '../auth/auth.service.js';
26
26
  import type { IAccessControl } from '../access/access-control.interface.js';
@@ -82,7 +82,7 @@ export function createWorkspaceRoutes(
82
82
  workflowService: IWorkflowService,
83
83
  eventBus: WorkflowEventBus,
84
84
  accessControl: IAccessControl,
85
- kbDirName: string,
85
+ kb: KbContext,
86
86
  creatorAccess: ICreatorAccess,
87
87
  adminAccess: IAdminAccessService,
88
88
  disk: ITreeWalker,
@@ -97,6 +97,7 @@ export function createWorkspaceRoutes(
97
97
  changeGate?: IChangeReadGate,
98
98
  ): express.Router {
99
99
  const router = express.Router();
100
+ const { kbDirName } = kb;
100
101
  const gitInternalsRouteGuard = createGitInternalsRouteGuard(workspaceService);
101
102
 
102
103
  // The git folder is refused ahead of every route here by
@@ -463,7 +464,7 @@ export function createWorkspaceRoutes(
463
464
  }
464
465
  const branch = typeof req.query.branch === 'string' && req.query.branch.length > 0
465
466
  ? req.query.branch
466
- : DEFAULT_BRANCH;
467
+ : kb.defaultBranch;
467
468
  const workspace = await workspaceService.getOrCreateForBranch(branch);
468
469
  // Bootstrap must use the same read-filtered tree as GET /workspace/:id/files.
469
470
  // Otherwise the first sidebar render can expose restricted nodes until the
@@ -663,7 +664,7 @@ export function createWorkspaceRoutes(
663
664
  // same treatment so legacy clones don't collapse. Only the folders
664
665
  // themselves are forced visible — their contents stay gated by the
665
666
  // verdict above.
666
- const structuralRoots = new Set([...reservedRootDirNames(), KNOWLEDGE_DIR]);
667
+ const structuralRoots = new Set([...reservedRootDirNames(kb.layout), KNOWLEDGE_DIR]);
667
668
  for (const wp of wsRelPaths) {
668
669
  const rel = toKbRelative(wp, kbDirName);
669
670
  if (rel !== null && structuralRoots.has(rel)) verdict.set(wp, true);
@@ -1128,11 +1129,11 @@ export function createWorkspaceRoutes(
1128
1129
  const oldRel = toKbRelative(oldPath, kbDirName);
1129
1130
  const newRel = toKbRelative(newPath, kbDirName);
1130
1131
  let platformRestore = false;
1131
- if (oldRel !== null && newRel !== null && isPlatformRestoreShape(oldRel, newRel)) {
1132
+ if (oldRel !== null && newRel !== null && isPlatformRestoreShape(oldRel, newRel, kb.layout)) {
1132
1133
  platformRestore = await accessControl.canRestorePlatformFile(id, user.email, newRel);
1133
1134
  }
1134
1135
  // A platform file stays where the platform reads it …
1135
- if (oldRel !== null && isPlatformFile(oldRel) && !platformRestore) {
1136
+ if (oldRel !== null && isPlatformFile(oldRel, kb.layout) && !platformRestore) {
1136
1137
  res.status(409).json({ error: platformFileRefusal(oldRel) });
1137
1138
  return;
1138
1139
  }
@@ -1145,7 +1146,7 @@ export function createWorkspaceRoutes(
1145
1146
  // from the same place. Only the restore lands on a platform path, and
1146
1147
  // only where the file is missing (`canRestorePlatformFile` checked the
1147
1148
  // disk; the destination lock below checks it again, under the lock).
1148
- if (newRel !== null && isPlatformFile(newRel) && !platformRestore) {
1149
+ if (newRel !== null && isPlatformFile(newRel, kb.layout) && !platformRestore) {
1149
1150
  res.status(409).json({ error: platformFileCreationRefusal(newRel) });
1150
1151
  return;
1151
1152
  }
@@ -9,7 +9,6 @@ import { AsyncLocalStorage } from 'node:async_hooks';
9
9
  import {
10
10
  validateRelativePath,
11
11
  validateFilename,
12
- DEFAULT_BRANCH,
13
12
  FOLDER_PLACEHOLDER,
14
13
  isFolderPlaceholder,
15
14
  entryExistsMessage,
@@ -22,6 +21,7 @@ import {
22
21
  renameNoReplace,
23
22
  } from '../../shared/rename-no-replace.js';
24
23
  import type { IGitRunner } from '../../shared/git.contract.js';
24
+ import type { KbContext } from '../../shared/kb-context.js';
25
25
  import { NodeGitRunner } from '../workflow/git/node-git-runner.js';
26
26
  import { assertWithinDirectory } from '../../shared/path-containment.js';
27
27
  import { assertRepoRootNameFree, normalizeWorkspacePath } from '../kb-fs/repo-path.js';
@@ -149,9 +149,8 @@ const CLONE_TIMEOUT_MS = 600_000;
149
149
  const BOT_NAME = 'Bevel Workflow';
150
150
  const BOT_EMAIL = 'bevel-workflow@bevel.software';
151
151
 
152
- /** Redact any token that might leak into an error message. */
153
- function redactError(err: unknown): string {
154
- const token = process.env.GITHUB_TOKEN;
152
+ /** Redact the token the runner authenticates with from an error message. */
153
+ function redactError(err: unknown, token: string | null): string {
155
154
  const msg = err instanceof Error ? err.message : String(err);
156
155
  return token ? msg.replaceAll(token, '***') : msg;
157
156
  }
@@ -297,23 +296,26 @@ export class WorkspaceService implements IWorkspaceService {
297
296
  * buy an inconsistency rather than a feature.
298
297
  */
299
298
  kbRepoUrl: string | (() => string),
300
- private readonly kbDirName: string,
299
+ /** The checkout folder's name, and which branch a caller lands on when naming none. */
300
+ private readonly kb: Pick<KbContext, 'kbDirName' | 'defaultBranch'>,
301
301
  private readonly disk: ITreeWalker,
302
- gitUsername: string | (() => string) = 'x-access-token',
303
302
  /**
304
303
  * How git is run — see `shared/git.contract.ts`. The composition root
305
- * passes the one runner carrying the deployment's deadline; the default
306
- * is the same runner on its default deadline, for a directly constructed
307
- * service.
304
+ * passes the one runner carrying the deployment's deadline and the
305
+ * credentials every clone authenticates with; the default is the same
306
+ * runner on its default deadline and no token, for a directly
307
+ * constructed service.
308
308
  */
309
309
  private readonly gitRunner: IGitRunner = new NodeGitRunner(),
310
310
  ) {
311
311
  this.kbRepoUrl = typeof kbRepoUrl === 'function' ? kbRepoUrl : () => kbRepoUrl;
312
- this.gitUsername = typeof gitUsername === 'function' ? gitUsername : () => gitUsername;
312
+ }
313
+
314
+ private get kbDirName(): string {
315
+ return this.kb.kbDirName;
313
316
  }
314
317
 
315
318
  private readonly kbRepoUrl: () => string;
316
- private readonly gitUsername: () => string;
317
319
 
318
320
  setDiffService(diffService: IDiffService): void {
319
321
  this.diffService = diffService;
@@ -417,7 +419,7 @@ export class WorkspaceService implements IWorkspaceService {
417
419
  // Persisted into the new repo's config (`clone --config`), not just applied
418
420
  // to this invocation — every later push from this clone depends on finding
419
421
  // the helper there. See `cloneCredentialArgs`.
420
- args.push(...cloneCredentialArgs(this.gitUsername()));
422
+ args.push(...cloneCredentialArgs(this.gitRunner.credentials));
421
423
  args.push(this.kbRepoUrl(), targetDir);
422
424
  return args;
423
425
  }
@@ -531,7 +533,7 @@ export class WorkspaceService implements IWorkspaceService {
531
533
  cloned.push({
532
534
  id: entry.name,
533
535
  branch,
534
- unreadable: `Could not read this branch's clone: ${redactError(err)}`,
536
+ unreadable: `Could not read this branch's clone: ${redactError(err, this.gitRunner.credentials.token())}`,
535
537
  });
536
538
  continue;
537
539
  }
@@ -566,7 +568,7 @@ export class WorkspaceService implements IWorkspaceService {
566
568
  return;
567
569
  } catch (err) {
568
570
  log.warn(`referenced clone for "${branch}" failed, retrying without reference:`, {
569
- detail: redactError(err),
571
+ detail: redactError(err, this.gitRunner.credentials.token()),
570
572
  });
571
573
  // Clear any partial output so the retry clones into a clean dir.
572
574
  await fs.rm(targetDir, { recursive: true, force: true }).catch(() => {});
@@ -675,7 +677,7 @@ export class WorkspaceService implements IWorkspaceService {
675
677
  * `branch` is passed.
676
678
  */
677
679
  async getOrCreateForUser(_user: AuthUser, branch?: string): Promise<WorkspaceInfo> {
678
- return this.getOrCreateForBranch(branch ?? DEFAULT_BRANCH);
680
+ return this.getOrCreateForBranch(branch ?? this.kb.defaultBranch);
679
681
  }
680
682
 
681
683
  private buildWorkspaceInfo(branch: string, workspaceDir: string): WorkspaceInfo {
@@ -741,7 +743,7 @@ export class WorkspaceService implements IWorkspaceService {
741
743
  } catch (err) {
742
744
  // One line per branch, not per key: every key writes to the same
743
745
  // `.git/config`, so what fails for one fails for all.
744
- log.warn(`could not normalize the config of the "${branch}" clone:`, { detail: redactError(err) });
746
+ log.warn(`could not normalize the config of the "${branch}" clone:`, { detail: redactError(err, this.gitRunner.credentials.token()) });
745
747
  }
746
748
  await this.stampCredentialHelper(repoDir, branch);
747
749
  }
@@ -750,12 +752,13 @@ export class WorkspaceService implements IWorkspaceService {
750
752
  * Fingerprint of the credential state a clone's helper is derived from: the
751
753
  * username (baked into the helper literal) and whether a token exists at all
752
754
  * (the helper is present-or-absent on that). The token VALUE is deliberately
753
- * excluded — the helper reads `$GITHUB_TOKEN` at call time, so rotating the
754
- * token needs no re-stamp; only a username change or a token appearing /
755
- * disappearing does.
755
+ * excluded — the helper reads `$GITHUB_TOKEN` from the environment the
756
+ * runner hands each git call, so rotating the token needs no re-stamp; only
757
+ * a username change or a token appearing / disappearing does.
756
758
  */
757
759
  private credentialFingerprint(): string {
758
- return `${this.gitUsername()}::${process.env.GITHUB_TOKEN ? '1' : '0'}`;
760
+ const { credentials } = this.gitRunner;
761
+ return `${credentials.username()}::${credentials.token() ? '1' : '0'}`;
759
762
  }
760
763
 
761
764
  /** Last credential fingerprint stamped into each branch's clone, this process. */
@@ -772,7 +775,7 @@ export class WorkspaceService implements IWorkspaceService {
772
775
  private async stampCredentialHelper(repoDir: string, branch: string): Promise<void> {
773
776
  let argLists: string[][];
774
777
  try {
775
- argLists = cloneCredentialConfigArgs(this.gitUsername());
778
+ argLists = cloneCredentialConfigArgs(this.gitRunner.credentials);
776
779
  } catch (err) {
777
780
  // `credentialHelperValue` fails closed on a username outside its safe
778
781
  // charset. Normally unreachable (CoreConfig and the settings service
@@ -782,7 +785,7 @@ export class WorkspaceService implements IWorkspaceService {
782
785
  // re-clone that fails on the same validation — masking the real cause.
783
786
  // Contain it as a loud non-stamp instead; `normalizeCloneConfig`'s
784
787
  // never-throws contract stays true.
785
- log.warn(`refusing to stamp the credential helper of the "${branch}" clone:`, { detail: redactError(err) });
788
+ log.warn(`refusing to stamp the credential helper of the "${branch}" clone:`, { detail: redactError(err, this.gitRunner.credentials.token()) });
786
789
  this.stampedCredentialFingerprint.delete(branch);
787
790
  return;
788
791
  }
@@ -798,7 +801,7 @@ export class WorkspaceService implements IWorkspaceService {
798
801
  if (!(args.includes('--unset-all') && code === 5)) {
799
802
  stamped = false;
800
803
  log.warn(`could not stamp the credential helper of the "${branch}" clone:`, {
801
- detail: redactError(err),
804
+ detail: redactError(err, this.gitRunner.credentials.token()),
802
805
  });
803
806
  }
804
807
  }
@@ -914,11 +917,11 @@ export class WorkspaceService implements IWorkspaceService {
914
917
  this.onWorkspaceCloned?.(workspaceIdForBranch(branch));
915
918
  } catch (listenerErr) {
916
919
  log.error(`onWorkspaceCloned listener failed for branch "${branch}":`, {
917
- detail: redactError(listenerErr),
920
+ detail: redactError(listenerErr, this.gitRunner.credentials.token()),
918
921
  });
919
922
  }
920
923
  } catch (err) {
921
- const redacted = redactError(err);
924
+ const redacted = redactError(err, this.gitRunner.credentials.token());
922
925
  // Roll back partial state so the next bootstrap retries cleanly.
923
926
  await fs.rm(targetDir, { recursive: true, force: true }).catch(() => {});
924
927
  // A branch origin does not have is a fact about the branch, not a
@@ -936,7 +939,7 @@ export class WorkspaceService implements IWorkspaceService {
936
939
  // two stories this is — and guessing either one states something
937
940
  // about the branch that we do not know. Our failure, so: 500.
938
941
  log.error(`Could not tell whether branch "${branch}" was ever known here:`, {
939
- detail: redactError(probeErr),
942
+ detail: redactError(probeErr, this.gitRunner.credentials.token()),
940
943
  });
941
944
  throw new Error(`Failed to clone process map: ${redacted}`);
942
945
  }
@@ -995,6 +998,14 @@ export class WorkspaceService implements IWorkspaceService {
995
998
  * is the path the operation will use, and a spelling that normalises inside
996
999
  * and resolves outside is exactly the miss this check exists to catch.
997
1000
  *
1001
+ * The git rule reads the caller's RAW spelling before either step — the
1002
+ * whole rule, links resolved, not just the spelling. Those two refuse
1003
+ * `.`/`..`, backslashes and absolute paths with a message that quotes the
1004
+ * path back and names a corrected one, and a git path spelled any of those
1005
+ * ways was answered by THAT rather than by the one sanitized refusal
1006
+ * (measured in production), as was a LINK into the folder spelled the same
1007
+ * ways. Which rule speaks is not the caller's to choose.
1008
+ *
998
1009
  * Callers use the returned `relativePath` for everything downstream — the
999
1010
  * git-internals check, the diff baseline, the lock-free path turns, their own
1000
1011
  * error messages — so what is checked is what is written. Nine inline
@@ -1006,6 +1017,7 @@ export class WorkspaceService implements IWorkspaceService {
1006
1017
  wsPath: string,
1007
1018
  ): Promise<{ workspaceDir: string; relativePath: string; absolutePath: string }> {
1008
1019
  const workspaceDir = await this.resolveWorkspaceDir(workspaceId);
1020
+ await assertNotGitInternals(workspaceDir, wsPath); // The RAW spelling, first — see above.
1009
1021
  const relativePath = normalizeWorkspacePath(wsPath, this.kbDirName);
1010
1022
  const absolutePath = path.resolve(workspaceDir, relativePath);
1011
1023
  assertWithinDirectory(absolutePath, this.repoRoot(workspaceDir));
@@ -1246,7 +1258,7 @@ export class WorkspaceService implements IWorkspaceService {
1246
1258
  })
1247
1259
  .catch((err) => {
1248
1260
  this.lastFetchOk.set(repoDir, false);
1249
- log.warn('git fetch origin failed:', { detail: redactError(err) });
1261
+ log.warn('git fetch origin failed:', { detail: redactError(err, this.gitRunner.credentials.token()) });
1250
1262
  })
1251
1263
  .finally(() => {
1252
1264
  if (this.inFlightFetches.get(repoDir) === promise) {
@@ -23,7 +23,7 @@ import { workspaceIdForBranch } from '../../shared/workspace-id.js';
23
23
  import { assertValidBranchName } from '../kb-fs/branch-name.js';
24
24
  import { assertInsideRepo, assertRepoRootNameFreeArgs, normalizePathArgs } from '../kb-fs/repo-path.js';
25
25
  import { GitGuardedFilesystem } from '../kb-fs/git-guarded-filesystem.js';
26
- import { assertNoGitInternalsSegment, hasGitInternalsSegment } from '../../shared/git-internals.js';
26
+ import { assertNoGitInternalsSegment, assertNotGitInternals, hasGitInternalsSegment } from '../../shared/git-internals.js';
27
27
  import { isRolesYamlPath } from '../access-model/roles-yaml-guard.js';
28
28
  import type { ISessionSink } from './session-sink.js';
29
29
  import { isAbsence } from '../../shared/fs.contract.js';
@@ -39,21 +39,20 @@ import { createFileReaderRegistry } from './file-readers/file-reader.registry.js
39
39
  import { DocumentReader } from './file-readers/document-reader.js';
40
40
  import { mcpImageResult } from '@bevel-software/platform-mcp-core';
41
41
  import {
42
- AGENTS_FILE,
43
42
  LEGACY_AGENTS_FILE,
44
43
  folderPlaceholderPath,
45
44
  isFolderPlaceholder,
46
45
  isPlatformFile,
47
46
  isPlatformFolder,
48
- isProtectedBranch,
49
- onKbLayoutApplied,
50
47
  platformFileCreationRefusal,
51
48
  platformFileNames,
52
49
  platformFileRefusal,
53
50
  platformFolderRefusal,
54
51
  entryExistsMessage,
55
52
  type ExistingEntryKind,
53
+ type KbLayout,
56
54
  } from '@bevel-software/platform-shared';
55
+ import type { KbContext } from '../../shared/kb-context.js';
57
56
  import { AccessDeniedError } from '../access-model/access-errors.js';
58
57
  import { removeEmptyDirs } from './empty-dirs.js';
59
58
  import { PROPOSAL_ROUTE_NOTE, rethrowAsWriteDenial } from './write-denial.js';
@@ -216,22 +215,23 @@ async function keepFolderOf(
216
215
  * working in their knowledge base. Under the default name the wording collapses
217
216
  * to the one file it has always named.
218
217
  *
219
- * A FUNCTION, called at mount time: the name is a deployment setting applied at
220
- * boot, and a module-scope string would snapshot the default.
218
+ * A FUNCTION of the layout, called when a description is built: the name is a
219
+ * deployment setting, and a module-scope string would snapshot the default.
221
220
  */
222
- function kbConventionsNote(): string {
223
- if (AGENTS_FILE === LEGACY_AGENTS_FILE) {
221
+ function kbConventionsNote(layout: KbLayout): string {
222
+ const agentsFile = layout.agentsFile ?? LEGACY_AGENTS_FILE;
223
+ if (agentsFile === LEGACY_AGENTS_FILE) {
224
224
  return ' Before your first read or change in a workspace, read `AGENTS.md` at the KB root — or `CLAUDE.md` on a knowledge base seeded before it was renamed — if either exists: it holds the author\'s conventions for this knowledge base, and you should follow them.';
225
225
  }
226
226
  return (
227
- ` Before your first read or change in a workspace, read \`${AGENTS_FILE}\` at the KB root, then ` +
227
+ ` Before your first read or change in a workspace, read \`${agentsFile}\` at the KB root, then ` +
228
228
  '`AGENTS.md` if it also exists (the organisation\'s own conventions) — or `CLAUDE.md` on a knowledge base seeded before it was renamed: together they hold the conventions for this knowledge base, and you should follow them.'
229
229
  );
230
230
  }
231
231
 
232
232
  /** The platform files as a tool description lists them — the guide under its own name. */
233
- function platformFileList(): string {
234
- return platformFileNames()
233
+ function platformFileList(layout: KbLayout): string {
234
+ return platformFileNames(layout)
235
235
  .map((name) => `\`${name}\``)
236
236
  .join(', ');
237
237
  }
@@ -679,7 +679,7 @@ export function registerWorkspaceTools(
679
679
  spillStore: SpillStore,
680
680
  docExtract: DocExtractService,
681
681
  accessControl: IAccessControl,
682
- kbDirName: string,
682
+ kb: KbContext,
683
683
  sessionOntologyGate: SessionOntologyGate,
684
684
  writePolicy: IRoutineWritePolicy,
685
685
  sessionSink: ISessionSink,
@@ -697,6 +697,7 @@ export function registerWorkspaceTools(
697
697
  */
698
698
  changeGate?: IChangeReadGate,
699
699
  ): void {
700
+ const { kbDirName } = kb;
700
701
  /**
701
702
  * The one extension→reader registry every read-shaped decision routes
702
703
  * through: read_file dispatches on it, grep asks it for searchable text,
@@ -782,7 +783,7 @@ export function registerWorkspaceTools(
782
783
  * not cloned yet (or that does not resolve) is left to the handler; the
783
784
  * filesystem refuses again underneath regardless.
784
785
  */
785
- const assertToolPathsNotGitInternals = async (args: Record<string, unknown>, ctx: ToolContext): Promise<void> => {
786
+ const toolPathArgs = (args: Record<string, unknown>): string[] => {
786
787
  const paths: string[] = [];
787
788
  for (const key of ['path', 'src', 'dest', 'destination'] as const) {
788
789
  if (typeof args[key] === 'string') paths.push(args[key]);
@@ -793,18 +794,52 @@ export function registerWorkspaceTools(
793
794
  if (typeof fp === 'string') paths.push(fp);
794
795
  }
795
796
  }
796
- for (const p of paths) assertNoGitInternalsSegment(p);
797
- const onDisk = paths.filter((p) => !spillStore.isSpillRef(p));
798
- if (onDisk.length === 0 || typeof args.branch !== 'string' || args.branch === '') return;
799
- let fs: LocalFilesystem;
797
+ return paths;
798
+ };
799
+
800
+ /**
801
+ * The branch's workspace root, to judge a spelling against what is on disk
802
+ * — or null when there is nothing to judge it against yet. Only a branch
803
+ * ALREADY cloned is used: bootstrapping one here would clone before the
804
+ * handler's access and ontology gates have had their say.
805
+ */
806
+ const gitCheckRootFor = async (args: Record<string, unknown>, ctx: ToolContext): Promise<string | null> => {
807
+ if (typeof args.branch !== 'string' || args.branch === '') return null;
800
808
  try {
801
- if (!(await ctx.workspaceService.hasBootstrappedWorkspace(workspaceIdForBranch(args.branch)))) return;
802
- fs = await ctx.getFilesystem(args.branch);
809
+ if (!(await ctx.workspaceService.hasBootstrappedWorkspace(workspaceIdForBranch(args.branch)))) return null;
810
+ const fs = await ctx.getFilesystem(args.branch);
811
+ return fs instanceof GitGuardedFilesystem ? fs.basePath : null;
803
812
  } catch {
804
- return;
813
+ return null;
805
814
  }
806
- if (!(fs instanceof GitGuardedFilesystem)) return;
807
- for (const p of onDisk) await fs.assertNotGitInternals(p);
815
+ };
816
+
817
+ /**
818
+ * The WHOLE rule — the spelling and where it lands, links resolved — over
819
+ * the caller's own arguments, before any gate, lock or read.
820
+ *
821
+ * Run before `normalizePathArgs`, which refuses a `..` segment, a `.`
822
+ * segment, a backslash and an absolute path as PATHS: a 400 that quotes the
823
+ * spelling back and names a corrected one. That answer used to arrive first
824
+ * for five families of spelling, so `knowledge-base/Notes/../.git/config`
825
+ * was told which path it meant instead of being refused. Running only the
826
+ * LEXICAL half here fixed those and left the same hole one step along: a
827
+ * link into the folder (`Notes/../gitlink/config`) has no `.git` to read in
828
+ * its spelling, so it took the path rule's answer too. Both halves therefore
829
+ * read the caller's spelling before anything may rewrite or refuse it.
830
+ *
831
+ * Nothing under the folder was reachable through any of it — the filesystem
832
+ * refuses again underneath — but which rule answers is not the caller's to
833
+ * choose by how they spell the path.
834
+ */
835
+ const assertToolPathsNotGitInternals = async (args: Record<string, unknown>, ctx: ToolContext): Promise<void> => {
836
+ const paths = toolPathArgs(args);
837
+ for (const p of paths) assertNoGitInternalsSegment(p);
838
+ const onDisk = paths.filter((p) => !spillStore.isSpillRef(p));
839
+ if (onDisk.length === 0) return;
840
+ const root = await gitCheckRootFor(args, ctx);
841
+ if (root === null) return;
842
+ for (const p of onDisk) await assertNotGitInternals(root, p);
808
843
  };
809
844
 
810
845
  // ── preflight for moves and deletes ─────────────────────────────────────
@@ -837,7 +872,7 @@ export function registerWorkspaceTools(
837
872
  * branch only through a change request.
838
873
  */
839
874
  const writeBlocked = async (branch: string, ctx: ToolContext, paths: string[]): Promise<string[]> => {
840
- if (!isProtectedBranch(branch)) return [];
875
+ if (!kb.isProtectedBranch(branch)) return [];
841
876
  const byRel = new Map<string, string>();
842
877
  for (const p of paths) {
843
878
  const rel = toKbRelative(p, kbDirName);
@@ -900,11 +935,11 @@ export function registerWorkspaceTools(
900
935
  if (isGitMetadata(norm)) return `"${norm}" is git metadata and cannot be moved or deleted.`;
901
936
  if (kind === 'file') {
902
937
  const rel = toKbRelative(norm, kbDirName);
903
- return rel !== null && isPlatformFile(rel) ? platformFileRefusal(rel) : undefined;
938
+ return rel !== null && isPlatformFile(rel, kb.layout) ? platformFileRefusal(rel) : undefined;
904
939
  }
905
940
  if (norm === '' || norm === kbDirName) return platformFolderRefusal('');
906
941
  const rel = toKbRelative(norm, kbDirName);
907
- return rel !== null && isPlatformFolder(rel) ? platformFolderRefusal(rel) : undefined;
942
+ return rel !== null && isPlatformFolder(rel, kb.layout) ? platformFolderRefusal(rel) : undefined;
908
943
  };
909
944
 
910
945
  /** The workspace root on disk for `branch`. */
@@ -1147,7 +1182,7 @@ export function registerWorkspaceTools(
1147
1182
  (typeof spec.description === 'function' ? spec.description() : spec.description) +
1148
1183
  (spec.proposable ? PROPOSAL_ROUTE_NOTE : '') +
1149
1184
  (spec.fileTool === false ? '' : CONTENT_RULE) +
1150
- kbConventionsNote();
1185
+ kbConventionsNote(kb.layout);
1151
1186
  const def = toolDef({
1152
1187
  name: spec.name,
1153
1188
  description: describe(),
@@ -1165,7 +1200,7 @@ export function registerWorkspaceTools(
1165
1200
  // move the text with them. Rewritten in place: the registry holds this
1166
1201
  // object, both surfaces hold the same one, and re-registering would be a
1167
1202
  // duplicate name.
1168
- onKbLayoutApplied(() => {
1203
+ kb.onLayoutApplied(() => {
1169
1204
  def.description = describe();
1170
1205
  });
1171
1206
  // Internal-only tools (e.g. `execute_command`) keep their route mounted —
@@ -1188,6 +1223,8 @@ export function registerWorkspaceTools(
1188
1223
  // never reached the repository — the whole bug, spelled with a prefix.
1189
1224
  toolHandler(
1190
1225
  async (args, ctx) => {
1226
+ // BEFORE the normaliser: see `assertToolPathsNotGitInternals`.
1227
+ if (spec.fileTool !== false) await assertToolPathsNotGitInternals(args, ctx);
1191
1228
  const normalized = normalizePathArgs(
1192
1229
  args,
1193
1230
  kbDirName,
@@ -1214,7 +1251,7 @@ export function registerWorkspaceTools(
1214
1251
  err,
1215
1252
  { tool: spec.name, branch: args.branch, userEmail: ctx.user.email, userId: ctx.user.id },
1216
1253
  accessControl,
1217
- kbDirName,
1254
+ kb,
1218
1255
  changeGate,
1219
1256
  );
1220
1257
  }
@@ -1396,7 +1433,7 @@ export function registerWorkspaceTools(
1396
1433
  description: () =>
1397
1434
  'Get a file/directory\'s metadata (name, type, size, …) without returning content. A file also reports `contentMode`: `text` (read, write and edit it as text), `document` (read returns an extraction; replace it by upload) or `binary` (bytes: copy, move, delete, or replace by upload), plus `kind` (`text` | `document` | `image` | `binary`), `mime`, `mimeSource` and `textEditable` — decided by the same file readers read_file, grep and the write tools use, so an extensionless text file is `text/plain`.' +
1398
1435
  ' Every entry also reports what you may DO with it. ' +
1399
- `\`managed\` is true for a platform item — a platform file (${platformFileList()}) or a platform folder (the repository root or a reserved root folder such as \`KnowledgeBase/\`); managed items are never movable or deletable through these tools. ` +
1436
+ `\`managed\` is true for a platform item — a platform file (${platformFileList(kb.layout)}) or a platform folder (the repository root or a reserved root folder such as \`KnowledgeBase/\`); managed items are never movable or deletable through these tools. ` +
1400
1437
  '`access: { read, write, download, owner }` is your own verdict under the access rules; pass `explainAccess: true` to learn why, and who else holds each verb. `movable` and `deletable` say whether `move_file` / `delete_file` / `delete_folder` would be allowed for you, judged like their dry runs: not managed, no symbolic link, and on a protected branch you hold write on the item AND on every file under a folder (on a draft branch writes are not gated). `movable` judges the source side only; the destination is judged by a `move_file` dry run. ' +
1401
1438
  'For a folder, `descendants` is the number of files under it at any depth; counting stops at 10000 and `descendantsTruncated` says so, and past that point `movable` and `deletable` are false because a folder that large was not judged in full — run the `move_file` or `delete_folder` dry run for the real verdict. ' +
1402
1439
  'Call this before a move or delete to see what it would touch.' +
@@ -2006,7 +2043,7 @@ export function registerWorkspaceTools(
2006
2043
  name: 'delete_file',
2007
2044
  description: () =>
2008
2045
  'Delete ONE workspace file (a symbolic link is refused: links are never followed or removed). Committed + pushed as you. Its folder stays, even when this was its last file. Files only: a folder is refused with a pointer to `delete_folder`. ' +
2009
- `A platform file (\`access.md\` or \`.bevelignore\` in any folder, \`roles.yaml\` or \`${AGENTS_FILE}\` at the repository root) and git metadata are refused.` +
2046
+ `A platform file (\`access.md\` or \`.bevelignore\` in any folder, \`roles.yaml\` or \`${kb.layout.agentsFile}\` at the repository root) and git metadata are refused.` +
2010
2047
  ONTOLOGY_BOUNDARY_NOTE,
2011
2048
  inputs: {
2012
2049
  type: 'object',
@@ -2226,7 +2263,7 @@ export function registerWorkspaceTools(
2226
2263
  name: 'move_file',
2227
2264
  description: () =>
2228
2265
  'Move or rename a workspace FILE or FOLDER; a folder moves recursively, with everything under it. `dest` is the full new path, not the folder to move into. Lands as a delete + create, committed + pushed as you. ' +
2229
- `Rules: the destination must not exist — a move never overwrites a file or merges into a folder; a platform file (\`access.md\` or \`.bevelignore\` in any folder, \`roles.yaml\` or \`${AGENTS_FILE}\` at the repository root) is refused with "<name> is a platform file and stays in its folder." — a folder that moves takes its own platform files along, still in their folder; a platform folder (the repository root or a reserved root folder such as \`KnowledgeBase/\`) and git metadata are refused; a move cannot create a platform file or folder at \`dest\` either (renaming a note to \`access.md\` is refused); a path through a symbolic link is refused, since links are never followed; on a protected branch you must be able to write both ends — for a folder, every file under it at its old and its new path. ` +
2266
+ `Rules: the destination must not exist — a move never overwrites a file or merges into a folder; a platform file (\`access.md\` or \`.bevelignore\` in any folder, \`roles.yaml\` or \`${kb.layout.agentsFile}\` at the repository root) is refused with "<name> is a platform file and stays in its folder." — a folder that moves takes its own platform files along, still in their folder; a platform folder (the repository root or a reserved root folder such as \`KnowledgeBase/\`) and git metadata are refused; a move cannot create a platform file or folder at \`dest\` either (renaming a note to \`access.md\` is refused); a path through a symbolic link is refused, since links are never followed; on a protected branch you must be able to write both ends — for a folder, every file under it at its old and its new path. ` +
2230
2267
  'Access follows the destination folder. Preflight first: `dryRun: true` changes nothing and answers `{ src, dest, kind, descendants, access: { before, after }, accessChanges, allowed, reason? }` — `access` is your own `{ read, write, download, owner }` at the source and at the destination. ' +
2231
2268
  'A move whose `accessChanges` is true runs only with `confirm: true`; without it the call moves nothing and returns the same impact with `confirmationRequired: true`. Do NOT set `confirm: true` on your first call — dry-run, check the impact, then confirm.' +
2232
2269
  ONTOLOGY_BOUNDARY_NOTE,
@@ -1,4 +1,5 @@
1
- import { branchAuthorLocalpart, isProtectedBranch, suggestionsBranchPrefixFor } from '@bevel-software/platform-shared';
1
+ import { branchAuthorLocalpart, suggestionsBranchPrefixFor } from '@bevel-software/platform-shared';
2
+ import type { KbContext } from '../../shared/kb-context.js';
2
3
  import { ToolError } from '../tool-helpers/tool.contract.js';
3
4
  import { AccessDeniedError } from '../access-model/access-errors.js';
4
5
  import type { IChangeReadGate } from '../access-model/change-gate.js';
@@ -111,9 +112,10 @@ export async function writeDenial(
111
112
  err: AccessDeniedError,
112
113
  input: { tool: string; branch: string; userEmail: string; userId: string },
113
114
  accessControl: IAccessControl,
114
- kbDirName: string,
115
+ kb: Pick<KbContext, 'kbDirName' | 'isProtectedBranch'>,
115
116
  changeGate?: IChangeReadGate,
116
117
  ): Promise<ToolError> {
118
+ const { kbDirName } = kb;
117
119
  const path = err.access.path;
118
120
  const base = { kind: 'write-denied' as const, path, reason: reasonOf(err) };
119
121
  const refuse = (cannotProposeReason: string): ToolError => {
@@ -121,7 +123,7 @@ export async function writeDenial(
121
123
  return new ToolError(`${err.message} ${cannotProposeReason}`, 403, { ...details });
122
124
  };
123
125
 
124
- if (!isProtectedBranch(input.branch)) {
126
+ if (!kb.isProtectedBranch(input.branch)) {
125
127
  return refuse(`Proposing is not available: "${input.branch}" is not a branch that accepts change requests.`);
126
128
  }
127
129
  // The lock gate reports workspace paths, the git gates repo-relative ones.
@@ -179,11 +181,11 @@ export async function rethrowAsWriteDenial(
179
181
  err: unknown,
180
182
  input: { tool: string; branch: unknown; userEmail: string; userId: string },
181
183
  accessControl: IAccessControl,
182
- kbDirName: string,
184
+ kb: Pick<KbContext, 'kbDirName' | 'isProtectedBranch'>,
183
185
  changeGate?: IChangeReadGate,
184
186
  ): Promise<never> {
185
187
  if (err instanceof AccessDeniedError && typeof input.branch === 'string') {
186
- throw await writeDenial(err, { ...input, branch: input.branch }, accessControl, kbDirName, changeGate);
188
+ throw await writeDenial(err, { ...input, branch: input.branch }, accessControl, kb, changeGate);
187
189
  }
188
190
  throw err;
189
191
  }
@@ -98,6 +98,47 @@ describe('assertNotGitInternals — the resolved form', () => {
98
98
  await expect(assertNotGitInternals(root, 'knowledge-base/dangling')).rejects.toMatchObject({ code: 'EACCES' });
99
99
  });
100
100
 
101
+ it.each(['/etc/shadow', '../../../../etc/shadow', '/proc/1/root/secret'])(
102
+ 'never probes a spelling that lands outside the workspace on disk: %s',
103
+ async (outside) => {
104
+ // A raw spelling fans out into candidates, and one can name somewhere
105
+ // the server may not read. Probing it would answer with that place's own
106
+ // EACCES instead of the path rule's typed refusal for a spelling that
107
+ // was never a workspace path — so the disk is only asked about
108
+ // candidates that land under the root. The lexical reading still judges
109
+ // the rest, which is what catches a climb into another checkout's .git.
110
+ const realpath = fs.realpath.bind(fs);
111
+ const asked: string[] = [];
112
+ vi.spyOn(fs, 'realpath').mockImplementation((async (p: string) => {
113
+ asked.push(p);
114
+ if (!p.startsWith(root)) throw Object.assign(new Error('EACCES: permission denied'), { code: 'EACCES' });
115
+ return realpath(p);
116
+ }) as typeof fs.realpath);
117
+
118
+ await expect(assertNotGitInternals(root, outside)).resolves.toBeUndefined();
119
+ expect(asked.filter((p) => !p.startsWith(root))).toEqual([]);
120
+ },
121
+ );
122
+
123
+ it.each(['..link', '..link/config', './..link/config', '..dir/deeper'])(
124
+ 'probes a name that merely BEGINS with dots, and refuses it when it lands in the folder: %s',
125
+ async (p) => {
126
+ // Judged from the folder these sit DIRECTLY in, which is where the
127
+ // outside-the-root test can mistake them: relative to that root, the
128
+ // whole path is `..link/…`. `..link` is not a climb — it is a file name
129
+ // — so it is probed like any other entry, or a link by that name reaches
130
+ // the folder unchecked.
131
+ const kb = path.join(root, 'knowledge-base');
132
+ await fs.symlink('.git', path.join(kb, '..link'));
133
+ await fs.symlink('.git', path.join(kb, '..dir'));
134
+ await expect(assertNotGitInternals(kb, p)).rejects.toBeInstanceOf(GitInternalsError);
135
+ },
136
+ );
137
+
138
+ it('still refuses a climb into another checkout’s git folder, which is judged without the disk', async () => {
139
+ await expect(assertNotGitInternals(root, '../other-checkout/.git/config')).rejects.toBeInstanceOf(GitInternalsError);
140
+ });
141
+
101
142
  it('a link loop is no refusal of its own, and does not hang', async () => {
102
143
  await expect(assertNotGitInternals(root, 'knowledge-base/loop-a')).resolves.toBeUndefined();
103
144
  });