@bevel-software/platform-core-backend 0.20.0 → 0.22.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 (666) 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 +55 -107
  11. package/dist/core/create-core-server.js.map +1 -1
  12. package/dist/core/create-core-services.d.ts +41 -4
  13. package/dist/core/create-core-services.d.ts.map +1 -1
  14. package/dist/core/create-core-services.js +183 -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-requests.contract.d.ts +75 -0
  37. package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
  38. package/dist/modules/access/access-requests.contract.js +20 -0
  39. package/dist/modules/access/access-requests.contract.js.map +1 -0
  40. package/dist/modules/access/access-requests.routes.d.ts +35 -0
  41. package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
  42. package/dist/modules/access/access-requests.routes.js +237 -0
  43. package/dist/modules/access/access-requests.routes.js.map +1 -0
  44. package/dist/modules/access/access-requests.service.d.ts +123 -0
  45. package/dist/modules/access/access-requests.service.d.ts.map +1 -0
  46. package/dist/modules/access/access-requests.service.js +337 -0
  47. package/dist/modules/access/access-requests.service.js.map +1 -0
  48. package/dist/modules/access/access.routes.d.ts +2 -1
  49. package/dist/modules/access/access.routes.d.ts.map +1 -1
  50. package/dist/modules/access/access.routes.js +10 -10
  51. package/dist/modules/access/access.routes.js.map +1 -1
  52. package/dist/modules/access/change-read-gate.d.ts +6 -3
  53. package/dist/modules/access/change-read-gate.d.ts.map +1 -1
  54. package/dist/modules/access/change-read-gate.js +9 -6
  55. package/dist/modules/access/change-read-gate.js.map +1 -1
  56. package/dist/modules/access/creator-access.d.ts +4 -2
  57. package/dist/modules/access/creator-access.d.ts.map +1 -1
  58. package/dist/modules/access/creator-access.js +7 -4
  59. package/dist/modules/access/creator-access.js.map +1 -1
  60. package/dist/modules/access-model/access-grammar.d.ts +12 -0
  61. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  62. package/dist/modules/access-model/access-grammar.js +24 -0
  63. package/dist/modules/access-model/access-grammar.js.map +1 -1
  64. package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
  65. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
  66. package/dist/modules/agent-instructions/read-preamble.js +6 -9
  67. package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
  68. package/dist/modules/auth/account-admission.d.ts +40 -0
  69. package/dist/modules/auth/account-admission.d.ts.map +1 -0
  70. package/dist/modules/auth/account-admission.js +17 -0
  71. package/dist/modules/auth/account-admission.js.map +1 -0
  72. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  73. package/dist/modules/auth/account.routes.js +4 -1
  74. package/dist/modules/auth/account.routes.js.map +1 -1
  75. package/dist/modules/auth/auth.service.d.ts +45 -2
  76. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  77. package/dist/modules/auth/auth.service.js +44 -3
  78. package/dist/modules/auth/auth.service.js.map +1 -1
  79. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  80. package/dist/modules/auth/oidc-auth-provider.js +9 -0
  81. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  82. package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
  83. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  84. package/dist/modules/connection-probe/connection-probe.service.js +10 -2
  85. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  86. package/dist/modules/database/advisory-lock.d.ts +17 -0
  87. package/dist/modules/database/advisory-lock.d.ts.map +1 -1
  88. package/dist/modules/database/advisory-lock.js +24 -3
  89. package/dist/modules/database/advisory-lock.js.map +1 -1
  90. package/dist/modules/database/connection.d.ts +67 -2
  91. package/dist/modules/database/connection.d.ts.map +1 -1
  92. package/dist/modules/database/connection.js +118 -12
  93. package/dist/modules/database/connection.js.map +1 -1
  94. package/dist/modules/database/migrate.d.ts +1 -1
  95. package/dist/modules/database/migrate.d.ts.map +1 -1
  96. package/dist/modules/database/migrate.js +27 -3
  97. package/dist/modules/database/migrate.js.map +1 -1
  98. package/dist/modules/github-app/github-app.client.d.ts +101 -0
  99. package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
  100. package/dist/modules/github-app/github-app.client.js +222 -0
  101. package/dist/modules/github-app/github-app.client.js.map +1 -0
  102. package/dist/modules/github-app/github-app.connection.d.ts +102 -0
  103. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
  104. package/dist/modules/github-app/github-app.connection.js +185 -0
  105. package/dist/modules/github-app/github-app.connection.js.map +1 -0
  106. package/dist/modules/github-app/github-app.routes.d.ts +59 -0
  107. package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
  108. package/dist/modules/github-app/github-app.routes.js +323 -0
  109. package/dist/modules/github-app/github-app.routes.js.map +1 -0
  110. package/dist/modules/github-app/index.d.ts +4 -0
  111. package/dist/modules/github-app/index.d.ts.map +1 -0
  112. package/dist/modules/github-app/index.js +4 -0
  113. package/dist/modules/github-app/index.js.map +1 -0
  114. package/dist/modules/kb-fs/branch-name.d.ts +0 -1
  115. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  116. package/dist/modules/kb-fs/branch-name.js +3 -3
  117. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  118. package/dist/modules/kb-fs/clone-config.d.ts +9 -8
  119. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
  120. package/dist/modules/kb-fs/clone-config.js +14 -12
  121. package/dist/modules/kb-fs/clone-config.js.map +1 -1
  122. package/dist/modules/kb-fs/remote-url.d.ts +22 -0
  123. package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
  124. package/dist/modules/kb-fs/remote-url.js +35 -0
  125. package/dist/modules/kb-fs/remote-url.js.map +1 -0
  126. package/dist/modules/mcp/mcp.service.d.ts +7 -0
  127. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  128. package/dist/modules/mcp/mcp.service.js +4 -4
  129. package/dist/modules/mcp/mcp.service.js.map +1 -1
  130. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
  131. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  132. package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
  133. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  134. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
  135. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  136. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
  137. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  138. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
  139. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  140. package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
  141. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  142. package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
  143. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  144. package/dist/modules/plugins/discovery/native.source.js +4 -2
  145. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  146. package/dist/modules/plugins/index.d.ts +1 -1
  147. package/dist/modules/plugins/index.d.ts.map +1 -1
  148. package/dist/modules/plugins/index.js +1 -1
  149. package/dist/modules/plugins/index.js.map +1 -1
  150. package/dist/modules/plugins/join-proposals.d.ts +17 -5
  151. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  152. package/dist/modules/plugins/join-proposals.js +76 -22
  153. package/dist/modules/plugins/join-proposals.js.map +1 -1
  154. package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
  155. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  156. package/dist/modules/plugins/join-request-jobs.service.js +6 -6
  157. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  158. package/dist/modules/plugins/join-requests.service.d.ts +113 -18
  159. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  160. package/dist/modules/plugins/join-requests.service.js +178 -33
  161. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  162. package/dist/modules/plugins/plugin-links.d.ts +65 -10
  163. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  164. package/dist/modules/plugins/plugin-links.js +136 -31
  165. package/dist/modules/plugins/plugin-links.js.map +1 -1
  166. package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
  167. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  168. package/dist/modules/plugins/plugin-links.service.js +151 -21
  169. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  170. package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
  171. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  172. package/dist/modules/plugins/plugin-provision.service.js +33 -26
  173. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  174. package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
  175. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  176. package/dist/modules/plugins/plugin-rename.service.js +10 -12
  177. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  178. package/dist/modules/plugins/plugins.routes.d.ts +3 -0
  179. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  180. package/dist/modules/plugins/plugins.routes.js +48 -17
  181. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  182. package/dist/modules/plugins/plugins.service.d.ts +4 -4
  183. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  184. package/dist/modules/plugins/plugins.service.js +7 -10
  185. package/dist/modules/plugins/plugins.service.js.map +1 -1
  186. package/dist/modules/plugins/teams.routes.d.ts +4 -1
  187. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  188. package/dist/modules/plugins/teams.routes.js +11 -9
  189. package/dist/modules/plugins/teams.routes.js.map +1 -1
  190. package/dist/modules/secrets-vault/index.d.ts +1 -1
  191. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  192. package/dist/modules/secrets-vault/index.js +1 -1
  193. package/dist/modules/secrets-vault/index.js.map +1 -1
  194. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
  195. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  196. package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
  197. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  198. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
  199. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  200. package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
  201. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  202. package/dist/modules/settings/deployment-settings.service.d.ts +34 -9
  203. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  204. package/dist/modules/settings/deployment-settings.service.js +113 -26
  205. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  206. package/dist/modules/settings/managed-repository.d.ts +43 -0
  207. package/dist/modules/settings/managed-repository.d.ts.map +1 -0
  208. package/dist/modules/settings/managed-repository.js +60 -0
  209. package/dist/modules/settings/managed-repository.js.map +1 -0
  210. package/dist/modules/settings/repository-source.d.ts +128 -0
  211. package/dist/modules/settings/repository-source.d.ts.map +1 -0
  212. package/dist/modules/settings/repository-source.js +150 -0
  213. package/dist/modules/settings/repository-source.js.map +1 -0
  214. package/dist/modules/settings/setup.routes.d.ts +34 -4
  215. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  216. package/dist/modules/settings/setup.routes.js +198 -18
  217. package/dist/modules/settings/setup.routes.js.map +1 -1
  218. package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
  219. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
  220. package/dist/modules/skills/allowed-tools-check.js +8 -8
  221. package/dist/modules/skills/allowed-tools-check.js.map +1 -1
  222. package/dist/modules/skills/pending-skills.service.d.ts +3 -1
  223. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  224. package/dist/modules/skills/pending-skills.service.js +9 -6
  225. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  226. package/dist/modules/skills/skill-access-requests.routes.d.ts +8 -9
  227. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  228. package/dist/modules/skills/skill-access-requests.routes.js +26 -65
  229. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  230. package/dist/modules/skills/skills.routes.d.ts +4 -1
  231. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  232. package/dist/modules/skills/skills.routes.js +4 -4
  233. package/dist/modules/skills/skills.routes.js.map +1 -1
  234. package/dist/modules/skills/skills.service.d.ts +4 -2
  235. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  236. package/dist/modules/skills/skills.service.js +13 -11
  237. package/dist/modules/skills/skills.service.js.map +1 -1
  238. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  239. package/dist/modules/tool-helpers/tool-context.js +15 -0
  240. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  241. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
  242. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  243. package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
  244. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  245. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
  246. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  247. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
  248. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  249. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
  250. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  251. package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
  252. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  253. package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
  254. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
  255. package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
  256. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  257. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
  258. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  259. package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
  260. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  261. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
  262. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  263. package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
  264. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  265. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
  266. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  267. package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
  268. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  269. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
  270. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  271. package/dist/modules/workflow/agent-tools/workflow.tools.js +47 -11
  272. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  273. package/dist/modules/workflow/git/git.service.d.ts +14 -6
  274. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  275. package/dist/modules/workflow/git/git.service.js +40 -18
  276. package/dist/modules/workflow/git/git.service.js.map +1 -1
  277. package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
  278. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  279. package/dist/modules/workflow/git/node-git-runner.js +40 -5
  280. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  281. package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
  282. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  283. package/dist/modules/workflow/git/pull-request.service.js +99 -0
  284. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  285. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  286. package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
  287. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  288. package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
  289. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
  290. package/dist/modules/workflow/session-ontology.service.js +6 -2
  291. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  292. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  293. package/dist/modules/workflow/workflow.routes.js +3 -1
  294. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  295. package/dist/modules/workflow/workflow.service.d.ts +42 -10
  296. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  297. package/dist/modules/workflow/workflow.service.js +181 -71
  298. package/dist/modules/workflow/workflow.service.js.map +1 -1
  299. package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
  300. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  301. package/dist/modules/workspace/session-ontology.gate.js +3 -3
  302. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  303. package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
  304. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  305. package/dist/modules/workspace/startup/kb-git.js +15 -13
  306. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  307. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +70 -7
  308. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  309. package/dist/modules/workspace/startup/kb-startup-runner.js +183 -38
  310. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  311. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
  312. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  313. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
  314. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  315. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
  316. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
  317. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
  318. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
  319. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
  320. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
  321. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
  322. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
  323. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  324. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  325. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
  326. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  327. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
  328. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  329. package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
  330. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  331. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
  332. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  333. package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
  334. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  335. package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
  336. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  337. package/dist/modules/workspace/startup/steps/template-source.js +10 -6
  338. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  339. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  340. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  341. package/dist/modules/workspace/workspace.routes.js +8 -7
  342. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  343. package/dist/modules/workspace/workspace.service.d.ts +18 -12
  344. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  345. package/dist/modules/workspace/workspace.service.js +49 -29
  346. package/dist/modules/workspace/workspace.service.js.map +1 -1
  347. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  348. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  349. package/dist/modules/workspace/workspace.tools.js +48 -20
  350. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  351. package/dist/modules/workspace/write-denial.d.ts +3 -2
  352. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  353. package/dist/modules/workspace/write-denial.js +6 -5
  354. package/dist/modules/workspace/write-denial.js.map +1 -1
  355. package/dist/shared/domain-errors.d.ts +39 -0
  356. package/dist/shared/domain-errors.d.ts.map +1 -1
  357. package/dist/shared/domain-errors.js +55 -0
  358. package/dist/shared/domain-errors.js.map +1 -1
  359. package/dist/shared/git.contract.d.ts +62 -7
  360. package/dist/shared/git.contract.d.ts.map +1 -1
  361. package/dist/shared/git.contract.js +30 -8
  362. package/dist/shared/git.contract.js.map +1 -1
  363. package/dist/shared/kb-context.d.ts +76 -0
  364. package/dist/shared/kb-context.d.ts.map +1 -0
  365. package/dist/shared/kb-context.js +108 -0
  366. package/dist/shared/kb-context.js.map +1 -0
  367. package/dist/shared/kb-layout.d.ts +3 -2
  368. package/dist/shared/kb-layout.d.ts.map +1 -1
  369. package/dist/shared/kb-layout.js +4 -3
  370. package/dist/shared/kb-layout.js.map +1 -1
  371. package/dist/shared/kb-layout.test.js +35 -27
  372. package/dist/shared/kb-layout.test.js.map +1 -1
  373. package/dist/shared/pending-proposals.d.ts +4 -1
  374. package/dist/shared/pending-proposals.d.ts.map +1 -1
  375. package/dist/shared/pending-proposals.js +3 -4
  376. package/dist/shared/pending-proposals.js.map +1 -1
  377. package/dist/shared/redact-secret.d.ts +5 -5
  378. package/dist/shared/redact-secret.js +5 -5
  379. package/dist/tenancy/index.d.ts +13 -0
  380. package/dist/tenancy/index.d.ts.map +1 -0
  381. package/dist/tenancy/index.js +13 -0
  382. package/dist/tenancy/index.js.map +1 -0
  383. package/dist/tenancy/static-tenant-source.d.ts +98 -0
  384. package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
  385. package/dist/tenancy/static-tenant-source.js +220 -0
  386. package/dist/tenancy/static-tenant-source.js.map +1 -0
  387. package/dist/tenancy/tenant-host.d.ts +80 -0
  388. package/dist/tenancy/tenant-host.d.ts.map +1 -0
  389. package/dist/tenancy/tenant-host.js +217 -0
  390. package/dist/tenancy/tenant-host.js.map +1 -0
  391. package/dist/tenancy/tenant-runtime.d.ts +91 -0
  392. package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
  393. package/dist/tenancy/tenant-runtime.js +174 -0
  394. package/dist/tenancy/tenant-runtime.js.map +1 -0
  395. package/dist/tenancy/tenant-secrets.d.ts +24 -0
  396. package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
  397. package/dist/tenancy/tenant-secrets.js +30 -0
  398. package/dist/tenancy/tenant-secrets.js.map +1 -0
  399. package/dist/tenancy/tenant-source.contract.d.ts +45 -0
  400. package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
  401. package/dist/tenancy/tenant-source.contract.js +21 -0
  402. package/dist/tenancy/tenant-source.contract.js.map +1 -0
  403. package/migrations/0000_core_init.sql +14 -14
  404. package/migrations/0003_deployment_settings.sql +1 -1
  405. package/migrations/0005_github_facade.sql +1 -1
  406. package/migrations/0012_agent_audit.sql +6 -6
  407. package/package.json +3 -3
  408. package/src/__tests__/branch-model.test.ts +7 -6
  409. package/src/__tests__/kb-context.ts +30 -0
  410. package/src/__tests__/kb-layout-config.test.ts +35 -30
  411. package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
  412. package/src/core/__tests__/lifecycle.test.ts +78 -1
  413. package/src/core/__tests__/public-config.test.ts +5 -1
  414. package/src/core/catalog-cache-invalidation.ts +16 -11
  415. package/src/core/core-ports.ts +19 -0
  416. package/src/core/create-core-server.ts +64 -107
  417. package/src/core/create-core-services.ts +238 -96
  418. package/src/core/lifecycle.ts +214 -29
  419. package/src/core/public-config.ts +8 -5
  420. package/src/core-config.ts +107 -8
  421. package/src/index.ts +25 -2
  422. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
  423. package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
  424. package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
  425. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
  426. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
  427. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
  428. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  429. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
  430. package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
  431. package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
  432. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
  433. package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
  434. package/src/modules/access/__tests__/creator-access.test.ts +3 -2
  435. package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
  436. package/src/modules/access/access-control.service.ts +20 -6
  437. package/src/modules/access/access-requests.contract.ts +93 -0
  438. package/src/modules/access/access-requests.routes.ts +286 -0
  439. package/src/modules/access/access-requests.service.ts +420 -0
  440. package/src/modules/access/access.routes.ts +10 -13
  441. package/src/modules/access/change-read-gate.ts +10 -4
  442. package/src/modules/access/creator-access.ts +7 -2
  443. package/src/modules/access-model/access-grammar.ts +22 -0
  444. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
  445. package/src/modules/agent-instructions/read-preamble.ts +10 -12
  446. package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
  447. package/src/modules/auth/__tests__/auth.service.test.ts +98 -0
  448. package/src/modules/auth/account-admission.ts +44 -0
  449. package/src/modules/auth/account.routes.ts +4 -1
  450. package/src/modules/auth/auth.service.ts +61 -2
  451. package/src/modules/auth/oidc-auth-provider.ts +9 -0
  452. package/src/modules/connection-probe/connection-probe.service.ts +7 -1
  453. package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
  454. package/src/modules/database/__tests__/connection.test.ts +98 -0
  455. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
  456. package/src/modules/database/advisory-lock.ts +28 -4
  457. package/src/modules/database/connection.ts +143 -13
  458. package/src/modules/database/migrate.ts +48 -14
  459. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  460. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  461. package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
  462. package/src/modules/github-app/github-app.client.ts +268 -0
  463. package/src/modules/github-app/github-app.connection.ts +204 -0
  464. package/src/modules/github-app/github-app.routes.ts +359 -0
  465. package/src/modules/github-app/index.ts +19 -0
  466. package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
  467. package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
  468. package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
  469. package/src/modules/kb-fs/branch-name.ts +3 -3
  470. package/src/modules/kb-fs/clone-config.ts +15 -12
  471. package/src/modules/kb-fs/remote-url.ts +35 -0
  472. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
  473. package/src/modules/mcp/mcp.service.ts +11 -4
  474. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
  475. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
  476. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
  477. package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
  478. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
  479. package/src/modules/plugins/__tests__/join-requests.service.test.ts +28 -11
  480. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
  481. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
  482. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
  483. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
  484. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
  485. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
  486. package/src/modules/plugins/__tests__/plugins.routes.test.ts +12 -3
  487. package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
  488. package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
  489. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
  490. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
  491. package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
  492. package/src/modules/plugins/discovery/native.source.ts +4 -1
  493. package/src/modules/plugins/index.ts +1 -1
  494. package/src/modules/plugins/join-proposals.ts +87 -19
  495. package/src/modules/plugins/join-request-jobs.service.ts +8 -7
  496. package/src/modules/plugins/join-requests.service.ts +203 -43
  497. package/src/modules/plugins/plugin-links.service.ts +192 -19
  498. package/src/modules/plugins/plugin-links.ts +147 -27
  499. package/src/modules/plugins/plugin-provision.service.ts +54 -27
  500. package/src/modules/plugins/plugin-rename.service.ts +10 -10
  501. package/src/modules/plugins/plugins.routes.ts +48 -17
  502. package/src/modules/plugins/plugins.service.ts +7 -11
  503. package/src/modules/plugins/teams.routes.ts +16 -8
  504. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
  505. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
  506. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
  507. package/src/modules/secrets-vault/index.ts +2 -0
  508. package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
  509. package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
  510. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +24 -11
  511. package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
  512. package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
  513. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
  514. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +353 -0
  515. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +282 -0
  516. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
  517. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +233 -0
  518. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
  519. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
  520. package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
  521. package/src/modules/settings/deployment-settings.service.ts +130 -23
  522. package/src/modules/settings/managed-repository.ts +68 -0
  523. package/src/modules/settings/repository-source.ts +197 -0
  524. package/src/modules/settings/setup.routes.ts +233 -19
  525. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
  526. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
  527. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
  528. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +7 -2
  529. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
  530. package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
  531. package/src/modules/skills/allowed-tools-check.ts +7 -6
  532. package/src/modules/skills/pending-skills.service.ts +9 -8
  533. package/src/modules/skills/skill-access-requests.routes.ts +29 -79
  534. package/src/modules/skills/skills.routes.ts +4 -3
  535. package/src/modules/skills/skills.service.ts +13 -9
  536. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
  537. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
  538. package/src/modules/tool-helpers/tool-context.ts +15 -0
  539. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
  540. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  541. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
  542. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
  543. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
  544. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
  545. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
  546. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
  547. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
  548. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
  549. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
  550. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
  551. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
  552. package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
  553. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
  554. package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
  555. package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
  556. package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
  557. package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
  558. package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
  559. package/src/modules/workflow/__tests__/apply-failure.test.ts +11 -2
  560. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
  561. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
  562. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
  563. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
  564. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
  565. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
  566. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
  567. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +187 -35
  568. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
  569. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
  570. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
  571. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
  572. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
  573. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +148 -41
  574. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +143 -2
  575. package/src/modules/workflow/agent-tools/workflow.tools.ts +60 -12
  576. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
  577. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
  578. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
  579. package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
  580. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
  581. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
  582. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
  583. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
  584. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
  585. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
  586. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
  587. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
  588. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
  589. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
  590. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
  591. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
  592. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
  593. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
  594. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +130 -8
  595. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
  596. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
  597. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
  598. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
  599. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
  600. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
  601. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
  602. package/src/modules/workflow/git/git.service.ts +49 -17
  603. package/src/modules/workflow/git/node-git-runner.ts +43 -4
  604. package/src/modules/workflow/git/pull-request.service.ts +119 -0
  605. package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
  606. package/src/modules/workflow/session-ontology.service.ts +7 -2
  607. package/src/modules/workflow/workflow.routes.ts +3 -1
  608. package/src/modules/workflow/workflow.service.ts +201 -76
  609. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
  610. package/src/modules/workspace/__tests__/git-internals.security.test.ts +10 -9
  611. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
  612. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
  613. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
  614. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
  615. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
  616. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
  617. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
  618. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
  619. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
  620. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
  621. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
  622. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  623. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  624. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  625. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  626. package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
  627. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +45 -1
  628. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
  629. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +237 -10
  630. package/src/modules/workspace/__tests__/workspace.tools.test.ts +44 -11
  631. package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
  632. package/src/modules/workspace/session-ontology.gate.ts +6 -4
  633. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
  634. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +240 -21
  635. package/src/modules/workspace/startup/kb-git.ts +15 -22
  636. package/src/modules/workspace/startup/kb-startup-runner.ts +198 -45
  637. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
  638. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
  639. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
  640. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
  641. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
  642. package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
  643. package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
  644. package/src/modules/workspace/startup/steps/template-source.ts +8 -5
  645. package/src/modules/workspace/workspace.routes.ts +8 -7
  646. package/src/modules/workspace/workspace.service.ts +47 -28
  647. package/src/modules/workspace/workspace.tools.ts +56 -20
  648. package/src/modules/workspace/write-denial.ts +7 -5
  649. package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
  650. package/src/shared/domain-errors.ts +58 -0
  651. package/src/shared/git.contract.ts +80 -8
  652. package/src/shared/kb-context.ts +130 -0
  653. package/src/shared/kb-layout.test.ts +37 -27
  654. package/src/shared/kb-layout.ts +4 -3
  655. package/src/shared/pending-proposals.ts +7 -4
  656. package/src/shared/redact-secret.ts +5 -5
  657. package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
  658. package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
  659. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
  660. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
  661. package/src/tenancy/index.ts +35 -0
  662. package/src/tenancy/static-tenant-source.ts +296 -0
  663. package/src/tenancy/tenant-host.ts +261 -0
  664. package/src/tenancy/tenant-runtime.ts +208 -0
  665. package/src/tenancy/tenant-secrets.ts +41 -0
  666. package/src/tenancy/tenant-source.contract.ts +59 -0
@@ -6,6 +6,7 @@ import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
6
6
  import { workspaceIdForBranch } from '../../../shared/workspace-id.js';
7
7
  import { readAgentPreamble, type PreambleWorkspace } from '../read-preamble.js';
8
8
  import { NodeFs } from '../../kb-fs/node-fs.js';
9
+ import { testKbContext } from '../../../__tests__/kb-context.js';
9
10
 
10
11
  /** The one disk, as production wires it. */
11
12
  const disk = new NodeFs();
@@ -42,7 +43,7 @@ describe('readAgentPreamble', () => {
42
43
  it('reads the default branch copy at <kbDirName>/mcp-description.md, creating the workspace first', async () => {
43
44
  await fs.writeFile(path.join(wsDir, KB, 'mcp-description.md'), 'Acme.\n', 'utf8');
44
45
  const ws = workspace();
45
- expect(await readAgentPreamble(ws, KB, disk)).toBe('Acme.\n');
46
+ expect(await readAgentPreamble(ws, testKbContext({ kbDirName: KB }), disk)).toBe('Acme.\n');
46
47
  expect(ws.getOrCreateForBranch).toHaveBeenCalledWith(DEFAULT_BRANCH);
47
48
  expect(ws.getWorkspacePath).toHaveBeenCalledWith(workspaceIdForBranch(DEFAULT_BRANCH));
48
49
  });
@@ -50,19 +51,19 @@ describe('readAgentPreamble', () => {
50
51
  it("spells the path with the deployment's own KB dir name", async () => {
51
52
  await fs.mkdir(path.join(wsDir, 'kb'), { recursive: true });
52
53
  await fs.writeFile(path.join(wsDir, 'kb', 'mcp-description.md'), 'Renamed root.', 'utf8');
53
- expect(await readAgentPreamble(workspace(), 'kb', disk)).toBe('Renamed root.');
54
- expect(await readAgentPreamble(workspace(), KB, disk)).toBeNull(); // the default-named root has none
54
+ expect(await readAgentPreamble(workspace(), testKbContext({ kbDirName: 'kb' }), disk)).toBe('Renamed root.');
55
+ expect(await readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).toBeNull(); // the default-named root has none
55
56
  });
56
57
 
57
58
  it('a file that is not there is an absence, not a fault', async () => {
58
- expect(await readAgentPreamble(workspace(), KB, disk)).toBeNull();
59
+ expect(await readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).toBeNull();
59
60
  });
60
61
 
61
62
  it('refuses a symlink: the secret it points at is never read, and it is not an absence either', async () => {
62
63
  const secret = path.join(root, 'secret.txt');
63
64
  await fs.writeFile(secret, 'DATABASE_URL=postgres://…', 'utf8');
64
65
  await fs.symlink(secret, path.join(wsDir, KB, 'mcp-description.md'));
65
- await expect(readAgentPreamble(workspace(), KB, disk)).rejects.toThrow(/symlink, not a regular file/);
66
+ await expect(readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).rejects.toThrow(/symlink, not a regular file/);
66
67
  });
67
68
 
68
69
  it('refuses a repository folder reached through a link — the whole path must be its own, not only the file', async () => {
@@ -72,7 +73,7 @@ describe('readAgentPreamble', () => {
72
73
  await fs.writeFile(path.join(elsewhere, 'mcp-description.md'), 'Not the repository.', 'utf8');
73
74
  await fs.rm(path.join(wsDir, KB), { recursive: true });
74
75
  await fs.symlink(elsewhere, path.join(wsDir, KB), process.platform === 'win32' ? 'junction' : 'dir');
75
- await expect(readAgentPreamble(workspace(), KB, disk)).rejects.toThrow(/through a symlink/);
76
+ await expect(readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).rejects.toThrow(/through a symlink/);
76
77
  });
77
78
 
78
79
  it('reads through a link ABOVE the workspace — a mounted volume is the operator\'s, not the repository\'s', async () => {
@@ -81,12 +82,12 @@ describe('readAgentPreamble', () => {
81
82
  await fs.symlink(wsDir, mount, process.platform === 'win32' ? 'junction' : 'dir');
82
83
  const ws = workspace();
83
84
  ws.getWorkspacePath.mockImplementation(async () => mount);
84
- expect(await readAgentPreamble(ws, KB, disk)).toBe('Acme.\n');
85
+ expect(await readAgentPreamble(ws, testKbContext({ kbDirName: KB }), disk)).toBe('Acme.\n');
85
86
  });
86
87
 
87
88
  it('refuses a directory squatting the name', async () => {
88
89
  await fs.mkdir(path.join(wsDir, KB, 'mcp-description.md'));
89
- await expect(readAgentPreamble(workspace(), KB, disk)).rejects.toThrow(/directory, not a regular file/);
90
+ await expect(readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).rejects.toThrow(/directory, not a regular file/);
90
91
  });
91
92
 
92
93
  it('a KB directory that is a regular file is an ABSENT preamble, the shared rule for "nothing can be there"', async () => {
@@ -96,7 +97,7 @@ describe('readAgentPreamble', () => {
96
97
  // squatted this way is the startup phase's to refuse, loudly, once.
97
98
  await fs.rm(path.join(wsDir, KB), { recursive: true });
98
99
  await fs.writeFile(path.join(wsDir, KB), 'not a directory', 'utf8');
99
- expect(await readAgentPreamble(workspace(), KB, disk)).toBeNull();
100
+ expect(await readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).toBeNull();
100
101
  });
101
102
 
102
103
  it('an lstat that fails for any reason but absence throws, never an empty preamble', async () => {
@@ -110,7 +111,7 @@ describe('readAgentPreamble', () => {
110
111
  ? Promise.reject(Object.assign(new Error('EACCES: permission denied'), { code: 'EACCES' }))
111
112
  : (real as (p: string) => Promise<unknown>).call(fs, p)) as never);
112
113
  try {
113
- await expect(readAgentPreamble(workspace(), KB, disk)).rejects.toMatchObject({ code: 'EACCES' });
114
+ await expect(readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).rejects.toMatchObject({ code: 'EACCES' });
114
115
  } finally {
115
116
  spy.mockRestore();
116
117
  }
@@ -121,7 +122,7 @@ describe('readAgentPreamble', () => {
121
122
  const file = path.join(wsDir, KB, 'mcp-description.md');
122
123
  await fs.writeFile(file, 'Acme.', 'utf8');
123
124
  await fs.chmod(file, 0o000); // the lstat still passes; the open is what fails
124
- await expect(readAgentPreamble(workspace(), KB, disk)).rejects.toMatchObject({ code: 'EACCES' });
125
+ await expect(readAgentPreamble(workspace(), testKbContext({ kbDirName: KB }), disk)).rejects.toMatchObject({ code: 'EACCES' });
125
126
  });
126
127
 
127
128
  it('a workspace that cannot be located throws too', async () => {
@@ -130,6 +131,6 @@ describe('readAgentPreamble', () => {
130
131
  ws.getWorkspacePath.mockImplementation(async () => {
131
132
  throw eio;
132
133
  });
133
- await expect(readAgentPreamble(ws, KB, disk)).rejects.toBe(eio);
134
+ await expect(readAgentPreamble(ws, testKbContext({ kbDirName: KB }), disk)).rejects.toBe(eio);
134
135
  });
135
136
  });
@@ -1,7 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
4
- import { workspaceIdForBranch } from '../../shared/workspace-id.js';
3
+ import type { KbContext } from '../../shared/kb-context.js';
5
4
  import { isAbsence, type IFsProbe } from '../../shared/fs.contract.js';
6
5
  import type { WorkspaceService } from '../workspace/workspace.service.js';
7
6
  import { PREAMBLE_FILE } from './compose.js';
@@ -43,11 +42,17 @@ export type PreambleWorkspace = Pick<WorkspaceService, 'getOrCreateForBranch' |
43
42
  */
44
43
  export async function readAgentPreamble(
45
44
  workspace: PreambleWorkspace,
46
- kbDirName: string,
45
+ kb: Pick<KbContext, 'kbDirName' | 'defaultBranch' | 'defaultWorkspaceId'>,
47
46
  disk: IFsProbe,
48
47
  ): Promise<string | null> {
49
- await workspace.getOrCreateForBranch(DEFAULT_BRANCH);
50
- const wsDir = await workspace.getWorkspacePath(workspaceIdForBranch(DEFAULT_BRANCH));
48
+ const { kbDirName, defaultBranch } = kb;
49
+ const notRegular = (kind: string): Error =>
50
+ new Error(
51
+ `${PREAMBLE_FILE} on branch "${defaultBranch}" is a ${kind}, not a regular file at its own place; ` +
52
+ 'refusing to read it. Replace it with a plain markdown file in the repository folder.',
53
+ );
54
+ await workspace.getOrCreateForBranch(defaultBranch);
55
+ const wsDir = await workspace.getWorkspacePath(kb.defaultWorkspaceId());
51
56
  const abs = path.join(wsDir, kbDirName, PREAMBLE_FILE);
52
57
 
53
58
  const found = await disk.lstatOrNull(abs);
@@ -79,10 +84,3 @@ export async function readAgentPreamble(
79
84
  await handle.close();
80
85
  }
81
86
  }
82
-
83
- function notRegular(kind: string): Error {
84
- return new Error(
85
- `${PREAMBLE_FILE} on branch "${DEFAULT_BRANCH}" is a ${kind}, not a regular file at its own place; ` +
86
- 'refusing to read it. Replace it with a plain markdown file in the repository folder.',
87
- );
88
- }
@@ -3,6 +3,7 @@ import express from 'express';
3
3
  import type { Server } from 'node:http';
4
4
  import { createAccountRoutes } from '../account.routes.js';
5
5
  import { AuthService } from '../auth.service.js';
6
+ import { AccountAdmissionRefusedError } from '../account-admission.js';
6
7
  import { hashPassword } from '../password-hash.js';
7
8
  import type { Database } from '../../database/connection.js';
8
9
  import type { IAdminAccessService } from '../../admin/admin.interface.js';
@@ -145,6 +146,20 @@ describe('account routes — admin gate', () => {
145
146
  expect(accountErasure.eraseUser).toHaveBeenLastCalledWith('u2');
146
147
  });
147
148
 
149
+ it('an account the admission port refuses → 403 carrying the port\'s own words', async () => {
150
+ const base = await listen(makeApp({ admin: true }));
151
+ vi.mocked(authService.createAccount).mockRejectedValueOnce(
152
+ new AccountAdmissionRefusedError('No seat left on this plan'),
153
+ );
154
+ const refused = await fetch(`${base}/api/admin/accounts`, {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify({ email: 'b@example.com', password: 'long-enough-pw' }),
158
+ });
159
+ expect(refused.status).toBe(403);
160
+ expect(((await refused.json()) as { error: string }).error).toBe('No seat left on this plan');
161
+ });
162
+
148
163
  it('erasure failure → 500 with a generic body (no internal error text)', async () => {
149
164
  const base = await listen(makeApp({ admin: true }));
150
165
  vi.mocked(accountErasure.eraseUser).mockRejectedValueOnce(
@@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest';
2
2
  import type { Database } from '../../database/connection.js';
3
3
  import type { CoreConfig } from '../../../core-config.js';
4
4
  import { AuthService } from '../auth.service.js';
5
+ import { AccountAdmissionRefusedError, type IAccountAdmission } from '../account-admission.js';
5
6
  import { hashPassword, verifyPassword } from '../password-hash.js';
6
7
 
7
8
  /**
@@ -573,6 +574,44 @@ describe('AuthService — the SSO domain allow-list', () => {
573
574
  ).rejects.toThrow(/domain is not allowed/i);
574
575
  });
575
576
 
577
+ /**
578
+ * One rule per sign-in. A provider that decided for itself who may enter
579
+ * (an invitation, a claimed domain) is not overruled by a list the admin
580
+ * set for the deployment's own provider; the plan still has its say.
581
+ */
582
+ it('is not laid on top of a provider that decided admission itself', async () => {
583
+ const { db } = makeFakeDb([[{ ...ROW, email: 'invited@gmail.com', name: 'Invited' }]]);
584
+ const out = await new AuthService(db, config).loginWithSso('invited@gmail.com', 'Invited', {
585
+ admittedByProvider: true,
586
+ });
587
+ expect(out.user.email).toBe('invited@gmail.com');
588
+ });
589
+
590
+ it('still asks the plan about someone the provider admitted', async () => {
591
+ const asked: Array<[string, string]> = [];
592
+ const plan: IAccountAdmission = {
593
+ canProvision: async (email, reason) => {
594
+ asked.push([email, reason]);
595
+ return { ok: false, message: 'No seat left on this plan' };
596
+ },
597
+ };
598
+ const { db } = makeFakeDb([[]]);
599
+ await expect(
600
+ new AuthService(db, config, plan).loginWithSso('invited@gmail.com', 'Invited', { admittedByProvider: true }),
601
+ ).rejects.toBeInstanceOf(AccountAdmissionRefusedError);
602
+ expect(asked).toEqual([['invited@gmail.com', 'sso']]);
603
+ expect(vi.mocked(db.insert)).not.toHaveBeenCalled();
604
+ });
605
+
606
+ it('governs a provider that says nothing, or says it did not decide', async () => {
607
+ for (const opts of [undefined, {}, { admittedByProvider: false }]) {
608
+ const { db } = makeFakeDb([[]]);
609
+ await expect(new AuthService(db, config).loginWithSso('someone@gmail.com', 'Someone', opts)).rejects.toThrow(
610
+ /domain is not allowed/i,
611
+ );
612
+ }
613
+ });
614
+
576
615
  it('admits a subdomain of an allowed domain', async () => {
577
616
  const { db } = makeFakeDb([[{ ...ROW, email: 'eu@eu.bevel.software', name: 'EU' }]]);
578
617
  const out = await new AuthService(db, config).loginWithSso('eu@eu.bevel.software', 'EU');
@@ -605,3 +644,62 @@ describe('AuthService — the SSO domain allow-list', () => {
605
644
  expect(out.user.email).toBe('root@gmail.com');
606
645
  });
607
646
  });
647
+
648
+ describe('AuthService — the account admission port', () => {
649
+ const refusing: IAccountAdmission = {
650
+ canProvision: async () => ({ ok: false, message: 'No seat left on this plan' }),
651
+ };
652
+ const asked: Array<[string, string]> = [];
653
+ const recording: IAccountAdmission = {
654
+ canProvision: async (email, reason) => {
655
+ asked.push([email, reason]);
656
+ return { ok: true };
657
+ },
658
+ };
659
+
660
+ it('refuses a first SSO sign-in the port turns down, and inserts nothing', async () => {
661
+ // The lookup for an existing row answers empty: this is a new address (once per attempt).
662
+ const { db } = makeFakeDb([[], []]);
663
+ const svc = new AuthService(db, makeConfig(), refusing);
664
+ await expect(svc.loginWithSso('new@example.com', 'New')).rejects.toBeInstanceOf(AccountAdmissionRefusedError);
665
+ await expect(svc.loginWithSso('new@example.com', 'New')).rejects.toThrow('No seat left on this plan');
666
+ expect(vi.mocked(db.insert)).not.toHaveBeenCalled();
667
+ });
668
+
669
+ it('refuses an admin creating an account the port turns down, before any hash is stored', async () => {
670
+ const { db } = makeFakeDb([[]]);
671
+ const svc = new AuthService(db, makeConfig(), refusing);
672
+ await expect(svc.createAccount('new@example.com', 'New', 'a-long-enough-password')).rejects.toThrow(
673
+ 'No seat left on this plan',
674
+ );
675
+ expect(vi.mocked(db.insert)).not.toHaveBeenCalled();
676
+ });
677
+
678
+ it('never asks about an address that already has an account, so a refusal cannot lock anyone out', async () => {
679
+ asked.length = 0;
680
+ const { db } = makeFakeDb([[{ id: ROW.id }], [ROW]]);
681
+ const svc = new AuthService(db, makeConfig(), refusing);
682
+ const { user } = await svc.loginWithSso('alice@example.com', 'Alice');
683
+ expect(user.email).toBe('alice@example.com');
684
+ });
685
+
686
+ it('names the path a new account arrives by, and admits when the port says so', async () => {
687
+ asked.length = 0;
688
+ const { db } = makeFakeDb([[], [ROW], [], [ROW]]);
689
+ const svc = new AuthService(db, makeConfig(), recording);
690
+ await svc.loginWithSso('alice@example.com', 'Alice');
691
+ await svc.createAccount('alice@example.com', 'Alice', 'a-long-enough-password');
692
+ expect(asked).toEqual([
693
+ ['alice@example.com', 'sso'],
694
+ ['alice@example.com', 'admin-create'],
695
+ ]);
696
+ });
697
+
698
+ it('admits everyone by default, without a lookup', async () => {
699
+ const { db } = makeFakeDb([[ROW]]);
700
+ const svc = new AuthService(db, makeConfig());
701
+ const { user } = await svc.loginWithSso('alice@example.com', 'Alice');
702
+ expect(user.email).toBe('alice@example.com');
703
+ expect(vi.mocked(db.select)).not.toHaveBeenCalled();
704
+ });
705
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Whether a NEW account may be provisioned on this deployment, asked before
3
+ * the row is inserted. The one seam a host that sells seats needs: core
4
+ * admits everyone, and a cloud overlay answers from its plan without
5
+ * forking any sign-in path.
6
+ *
7
+ * Asked only for an address with no account yet. An existing user's sign-in
8
+ * is never a provisioning, so a refusal can never lock out someone who is
9
+ * already in.
10
+ */
11
+ export type AccountProvisionReason =
12
+ /** A first sign-in through single sign-on, which provisions on its own. */
13
+ | 'sso'
14
+ /** An admin creating an account from the app. */
15
+ | 'admin-create'
16
+ /** The deployment owner's first password sign-in, which creates their row. */
17
+ | 'bootstrap'
18
+ /** A verified identity from an embedding surface that needs a row without a session. */
19
+ | 'embed';
20
+
21
+ export type AccountAdmissionVerdict = { ok: true } | { ok: false; message: string };
22
+
23
+ export interface IAccountAdmission {
24
+ canProvision(email: string, reason: AccountProvisionReason): Promise<AccountAdmissionVerdict>;
25
+ }
26
+
27
+ /** Core's default: every account is admitted. */
28
+ export const admitEveryone: IAccountAdmission = {
29
+ canProvision: async () => ({ ok: true }),
30
+ };
31
+
32
+ /**
33
+ * The port said no. Carries the port's own words, which the routes surface
34
+ * to the person or admin who asked, and a 403: the identity was fine, the
35
+ * deployment has no place for it.
36
+ */
37
+ export class AccountAdmissionRefusedError extends Error {
38
+ readonly status = 403;
39
+
40
+ constructor(message: string) {
41
+ super(message);
42
+ this.name = 'AccountAdmissionRefusedError';
43
+ }
44
+ }
@@ -6,6 +6,7 @@ import { printable } from '../../shared/printable.js';
6
6
  const log = logger('accounts');
7
7
  import type { AuthUser } from '@bevel-software/platform-shared';
8
8
  import type { AuthService } from './auth.service.js';
9
+ import { AccountAdmissionRefusedError } from './account-admission.js';
9
10
  import { erasedAccountId, type IAccountErasureService } from './account-erasure.service.js';
10
11
  import type { IAdminAccessService } from '../admin/admin.interface.js';
11
12
  import type { UserAccessRemovalService } from '../access/user-access-removal.service.js';
@@ -64,7 +65,9 @@ export function createAccountRoutes(
64
65
  res.status(201).json(user);
65
66
  } catch (error) {
66
67
  const msg = error instanceof Error ? error.message : 'Unknown error';
67
- res.status(400).json({ error: msg });
68
+ // The deployment has no place for the account (a seat limit, say): the
69
+ // port's own words, as a refusal rather than a malformed request.
70
+ res.status(error instanceof AccountAdmissionRefusedError ? 403 : 400).json({ error: msg });
68
71
  }
69
72
  });
70
73
 
@@ -1,10 +1,15 @@
1
1
  import jwt from 'jsonwebtoken';
2
2
  import { eq } from 'drizzle-orm';
3
3
  import type { Database } from '../database/connection.js';
4
- import type { CoreConfig } from '../../core-config.js';
5
4
  import { users } from '../database/schema.js';
6
5
  import type { AuthUser } from '@bevel-software/platform-shared';
7
6
  import { canonicalEmail, hashEmail } from '../../shared/email-identity.js';
7
+ import {
8
+ AccountAdmissionRefusedError,
9
+ admitEveryone,
10
+ type AccountProvisionReason,
11
+ type IAccountAdmission,
12
+ } from './account-admission.js';
8
13
  import {
9
14
  hashPassword,
10
15
  verifyPassword,
@@ -80,12 +85,53 @@ export interface AuthConfig {
80
85
  loginPasswordEnabled?: boolean;
81
86
  }
82
87
 
88
+ /** What a sign-in provider tells {@link AuthService.loginWithSso} about the sign-in it hands over. */
89
+ export interface SsoLoginOptions {
90
+ /**
91
+ * The provider has itself decided that this person may enter: they were
92
+ * invited, their address is on a domain the deployment claimed, whatever
93
+ * its rule is. The domain allow-list (`ALLOWED_EMAIL_DOMAINS`) is then
94
+ * not applied.
95
+ *
96
+ * The allow-list exists for a provider that decides nothing: it is what
97
+ * stands between "the issuer knows this person" and "this person has an
98
+ * account here". Applied on top of a provider's own decision it is a
99
+ * second rule the admin set somewhere else, and the person it refuses was
100
+ * let in by the first. Default false, so every provider that does not say
101
+ * otherwise is governed by the allow-list as before.
102
+ */
103
+ admittedByProvider?: boolean;
104
+ }
105
+
83
106
  export class AuthService {
84
107
  constructor(
85
108
  private readonly db: Database,
86
109
  private readonly config: AuthConfig,
110
+ /**
111
+ * Whether a new account may be provisioned — see `account-admission.ts`.
112
+ * Core admits everyone; a host that sells seats answers from its plan.
113
+ */
114
+ private readonly admission: IAccountAdmission = admitEveryone,
87
115
  ) {}
88
116
 
117
+ /**
118
+ * Ask the admission port before an account is CREATED: an address that
119
+ * already has a row is never asked about, so a refusal cannot lock an
120
+ * existing user out. The default port is not asked at all, which also
121
+ * spares the lookup.
122
+ */
123
+ private async assertAdmitted(normalizedEmail: string, reason: AccountProvisionReason): Promise<void> {
124
+ if (this.admission === admitEveryone) return;
125
+ const [existing] = await this.db
126
+ .select({ id: users.id })
127
+ .from(users)
128
+ .where(eq(users.email, normalizedEmail))
129
+ .limit(1);
130
+ if (existing) return;
131
+ const verdict = await this.admission.canProvision(normalizedEmail, reason);
132
+ if (!verdict.ok) throw new AccountAdmissionRefusedError(verdict.message);
133
+ }
134
+
89
135
  /**
90
136
  * Password login. Two credential sources, in order:
91
137
  *
@@ -155,6 +201,7 @@ export class AuthService {
155
201
  throw new Error('Invalid credentials');
156
202
  }
157
203
  const defaultName = normalizedEmail.split('@')[0] || normalizedEmail;
204
+ await this.assertAdmitted(normalizedEmail, 'bootstrap');
158
205
  const admin = await this.upsertUserByEmail(normalizedEmail, defaultName);
159
206
  return { token: this.signToken(admin.id, admin.email), user: this.toClientUser(admin) };
160
207
  }
@@ -177,16 +224,26 @@ export class AuthService {
177
224
  * verified. Same upsert-by-email + JWT path as password login. Email is the
178
225
  * idempotency key, so a user who first used password login and later signs
179
226
  * in via SSO (same email) keeps the same account/id.
227
+ *
228
+ * WHO MAY ENTER is decided once per sign-in, by whoever is in a position
229
+ * to decide it. For the deployment's own provider that is the domain
230
+ * allow-list: the provider signs in whoever its issuer knows, and nobody
231
+ * has approved the person. A provider that HAS decided (see
232
+ * {@link SsoLoginOptions.admittedByProvider}) is governed by its own rule
233
+ * alone; the allow-list is not laid on top of it. The admission port is
234
+ * asked either way: that is the plan's answer, not a sign-in rule.
180
235
  */
181
236
  async loginWithSso(
182
237
  email: string,
183
238
  name: string,
239
+ opts: SsoLoginOptions = {},
184
240
  ): Promise<{ token: string; user: AuthUser }> {
185
241
  const normalizedEmail = canonicalEmail(email ?? '');
186
242
  if (!EMAIL_REGEX.test(normalizedEmail)) {
187
243
  throw new Error('Sign-in returned an invalid email');
188
244
  }
189
- this.assertAllowedDomain(normalizedEmail);
245
+ if (!opts.admittedByProvider) this.assertAllowedDomain(normalizedEmail);
246
+ await this.assertAdmitted(normalizedEmail, 'sso');
190
247
  const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
191
248
  const user = await this.upsertUserByEmail(normalizedEmail, displayName);
192
249
  return { token: this.signToken(user.id, user.email), user: this.toClientUser(user) };
@@ -221,6 +278,7 @@ export class AuthService {
221
278
  throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
222
279
  }
223
280
  this.assertPasswordPolicy(password);
281
+ await this.assertAdmitted(normalizedEmail, 'admin-create');
224
282
  const suppliedName = (name ?? '').trim();
225
283
  const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
226
284
  const passwordHash = await hashPassword(password);
@@ -382,6 +440,7 @@ export class AuthService {
382
440
  ): Promise<{ id: string; email: string; name: string } | null> {
383
441
  const normalizedEmail = canonicalEmail(email ?? '');
384
442
  if (!EMAIL_REGEX.test(normalizedEmail)) return null;
443
+ await this.assertAdmitted(normalizedEmail, 'embed');
385
444
  const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
386
445
  const user = await this.upsertUserByEmail(normalizedEmail, displayName);
387
446
  return { id: user.id, email: user.email, name: user.name };
@@ -7,6 +7,7 @@ import type { AuthProviderPlugin } from './auth.routes.js';
7
7
  import { AUTH_COOKIE_MAX_AGE_S } from './auth.routes.js';
8
8
  import { AUTH_COOKIE_NAME } from './auth.middleware.js';
9
9
  import type { AuthService } from './auth.service.js';
10
+ import { AccountAdmissionRefusedError } from './account-admission.js';
10
11
  import { normalizeIssuerUrl } from '../settings/oidc-check.js';
11
12
 
12
13
  // Short-lived CSRF state + PKCE verifier for the OAuth round-trip: set before
@@ -319,6 +320,14 @@ export class OidcAuthProvider implements AuthProviderPlugin {
319
320
  });
320
321
  res.redirect(`${publicFrontendUrl}/auth/oidc/callback#token=${encodeURIComponent(token)}`);
321
322
  } catch (error) {
323
+ // A refused provisioning is not a failed sign-in: the identity was
324
+ // verified, the deployment has no seat for it. Named so the login
325
+ // screen can say so instead of "try again".
326
+ if (error instanceof AccountAdmissionRefusedError) {
327
+ log.warn('OIDC sign-in refused by the account admission port:', { detail: error.message });
328
+ fail('admission');
329
+ return;
330
+ }
322
331
  log.error('OIDC callback error:', { err: error });
323
332
  fail('auth');
324
333
  }
@@ -399,6 +399,12 @@ export class ConnectionProbeService implements IConnectionProbeService {
399
399
  constructor(
400
400
  private readonly toolManualService: IToolManualService,
401
401
  private readonly secretsVault: ISecretsVaultService,
402
+ /**
403
+ * The scope this knowledge base's vault was registered under with the
404
+ * UTCP variable loader, so a probe's client resolves the caller's secrets
405
+ * from it — see `secrets-variable-loader.ts`. Default: the single one.
406
+ */
407
+ private readonly secretsScope?: string,
402
408
  ) {}
403
409
 
404
410
  async probe(userId: string, userEmail: string, slug: string): Promise<ProbeVerdict | null> {
@@ -556,7 +562,7 @@ export class ConnectionProbeService implements IConnectionProbeService {
556
562
  // loader, which is exactly the credential under test.
557
563
  const config = new UtcpClientConfigSerializer().validateDict({
558
564
  variables: {},
559
- load_variables_from: [bevelSecretsLoaderConfig(userId)],
565
+ load_variables_from: [bevelSecretsLoaderConfig(userId, this.secretsScope)],
560
566
  });
561
567
  // No late-settle owner: a client that arrives after the deadline has no
562
568
  // manual registered and therefore no session, and neither
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { AdvisoryLock, withAdvisoryLock } from '../advisory-lock.js';
2
+ import { AdvisoryLock, advisoryLockKey, withAdvisoryLock } from '../advisory-lock.js';
3
3
  import type { Database } from '../connection.js';
4
4
 
5
5
  /**
@@ -145,3 +145,25 @@ describe('withAdvisoryLock', () => {
145
145
  expect(clients[0].releasedWith).toBeInstanceOf(Error);
146
146
  });
147
147
  });
148
+
149
+ describe('advisoryLockKey', () => {
150
+ it('keeps the ids a single-tenant deployment always used when no tenant is named', () => {
151
+ expect(advisoryLockKey(AdvisoryLock.Migrations)).toBe(AdvisoryLock.Migrations);
152
+ expect(advisoryLockKey(AdvisoryLock.CommitWorker, '')).toBe(AdvisoryLock.CommitWorker);
153
+ });
154
+
155
+ it('gives each tenant its own key for each lock, stable across calls and inside int4', () => {
156
+ const a = advisoryLockKey(AdvisoryLock.CommitWorker, 't_acme');
157
+ const b = advisoryLockKey(AdvisoryLock.CommitWorker, 't_globex');
158
+ expect(a).toBe(advisoryLockKey(AdvisoryLock.CommitWorker, 't_acme'));
159
+ expect(a).not.toBe(b);
160
+ expect(a).not.toBe(AdvisoryLock.CommitWorker);
161
+ // The two locks of one tenant stay two locks.
162
+ expect(advisoryLockKey(AdvisoryLock.Migrations, 't_acme')).not.toBe(a);
163
+ for (const key of [a, b]) {
164
+ expect(Number.isInteger(key)).toBe(true);
165
+ expect(key).toBeGreaterThanOrEqual(-(2 ** 31));
166
+ expect(key).toBeLessThanOrEqual(2 ** 31 - 1);
167
+ }
168
+ });
169
+ });
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import {
3
+ DEFAULT_DB_SCHEMA,
4
+ assertSchemaName,
5
+ assertSearchPath,
6
+ closeDb,
7
+ createDb,
8
+ dbSchemaOf,
9
+ getDb,
10
+ type Database,
11
+ } from '../connection.js';
12
+
13
+ /**
14
+ * Pools here are never connected: `pg.Pool` opens nothing until a query is
15
+ * asked of it, so what a handle was built with — the schema its connections
16
+ * will search, whether two asks share one pool — is observable without a
17
+ * database, and that is what these cases pin.
18
+ */
19
+ const URL = 'postgresql://u:p@127.0.0.1:1/never';
20
+
21
+ const searchPath = (db: Database): string | undefined =>
22
+ (db.$client.options as { options?: string }).options;
23
+
24
+ const opened: Database[] = [];
25
+ afterEach(async () => {
26
+ await Promise.all(opened.splice(0).map((db) => closeDb(db)));
27
+ });
28
+
29
+ describe('schema per knowledge base', () => {
30
+ it('searches the schema it was built for, and leaves the default schema to the server', () => {
31
+ const acme = createDb(URL, { schema: 'acme' });
32
+ const plain = createDb(URL);
33
+ opened.push(acme, plain);
34
+ expect(searchPath(acme)).toBe('-c search_path=acme');
35
+ expect(dbSchemaOf(acme)).toBe('acme');
36
+ // Nothing set: an operator's own `search_path` stays in force.
37
+ expect(searchPath(plain)).toBeUndefined();
38
+ expect(dbSchemaOf(plain)).toBe(DEFAULT_DB_SCHEMA);
39
+ });
40
+
41
+ it('refuses a schema name that is not a plain lowercase identifier', () => {
42
+ for (const bad of ['Acme', 'a-b', 'a b', '1abc', 'public; drop schema x', '', 'a'.repeat(64)]) {
43
+ expect(() => assertSchemaName(bad), bad).toThrow(/schema name/);
44
+ expect(() => createDb(URL, { schema: bad }), bad).toThrow(/schema name/);
45
+ }
46
+ expect(assertSchemaName('t_acme_2')).toBe('t_acme_2');
47
+ });
48
+
49
+ it('refuses the names Postgres and drizzle already own', () => {
50
+ for (const reserved of ['pg_catalog', 'pg_toast', 'pg_x', 'information_schema', 'drizzle']) {
51
+ expect(() => assertSchemaName(reserved), reserved).toThrow(/reserved/);
52
+ }
53
+ // The default schema is a valid name here: a single-tenant deployment
54
+ // lives on it. A tenant source refuses it for a tenant, not this guard.
55
+ expect(assertSchemaName(DEFAULT_DB_SCHEMA)).toBe(DEFAULT_DB_SCHEMA);
56
+ });
57
+ });
58
+
59
+ describe('assertSearchPath', () => {
60
+ const answering = (schema: string | null) => ({
61
+ execute: async () => ({ rows: [{ schema }] }) as never,
62
+ });
63
+
64
+ it('passes when the server searches the configured schema first', async () => {
65
+ await expect(assertSearchPath(answering('t_acme'), 't_acme')).resolves.toBeUndefined();
66
+ });
67
+
68
+ it('refuses a connection whose schema a pooler dropped, naming both schemas and the cause', async () => {
69
+ // What a transaction-mode pooler leaves behind: the startup parameter
70
+ // never reached the server, so `public` is searched first.
71
+ await expect(assertSearchPath(answering('public'), 't_acme')).rejects.toThrow(
72
+ /search "public" first, not the configured schema "t_acme".*transaction-mode pooler/,
73
+ );
74
+ await expect(assertSearchPath(answering(null), 't_acme')).rejects.toThrow(/\(none\)/);
75
+ });
76
+ });
77
+
78
+ describe('getDb', () => {
79
+ it('shares one pool per (url, schema) and keeps different schemas apart', () => {
80
+ const a1 = getDb(URL, { schema: 'acme' });
81
+ const a2 = getDb(URL, { schema: 'acme' });
82
+ const b = getDb(URL, { schema: 'globex' });
83
+ const plain = getDb(URL);
84
+ opened.push(a1, b, plain);
85
+ expect(a2).toBe(a1);
86
+ expect(b).not.toBe(a1);
87
+ expect(plain).not.toBe(a1);
88
+ expect(getDb(URL, { schema: DEFAULT_DB_SCHEMA })).toBe(plain);
89
+ });
90
+
91
+ it('opens a fresh pool after the cached one was closed', async () => {
92
+ const first = getDb(URL, { schema: 'closing' });
93
+ await closeDb(first);
94
+ const second = getDb(URL, { schema: 'closing' });
95
+ opened.push(second);
96
+ expect(second).not.toBe(first);
97
+ });
98
+ });