@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
@@ -1,7 +1,8 @@
1
1
  import path from 'node:path';
2
- import { PLUGINS_DIR, PLUGIN_MANIFEST_FILE, pluginManifestName } from '@bevel-software/platform-shared';
2
+ import { PLUGIN_MANIFEST_FILE, pluginManifestName, type KbLayout } from '@bevel-software/platform-shared';
3
3
  import type { DiscoveredPlugin, Discovery, PluginSource, PluginSourceWalk } from './plugin-source.js';
4
4
  import type { IFsProbe, ITreeWalker, WalkListener } from '../../../shared/fs.contract.js';
5
+ import type { KbContext } from '../../../shared/kb-context.js';
5
6
  import { readNativePlugin } from './native.source.js';
6
7
  import { BUNDLE_FILE, loadRegistry, readBundlePlugin } from './bundle-dialect/bundle.source.js';
7
8
  import type { McpRegistry } from './bundle-dialect/registry.js';
@@ -38,7 +39,11 @@ import type { McpRegistry } from './bundle-dialect/registry.js';
38
39
  export class KbPluginSource implements PluginSource {
39
40
  readonly dialect = 'kb';
40
41
 
41
- constructor(private readonly disk: ITreeWalker & IFsProbe) {}
42
+ constructor(
43
+ private readonly disk: ITreeWalker & IFsProbe,
44
+ /** Read per walk, never captured: the plugins root is renameable from the setup screen. */
45
+ private readonly kb: Pick<KbContext, 'layout'>,
46
+ ) {}
42
47
 
43
48
  async discover(kbRoot: string): Promise<Discovery> {
44
49
  return (await this.walkWith(kbRoot, [])).discovery;
@@ -47,7 +52,7 @@ export class KbPluginSource implements PluginSource {
47
52
  async walkWith(kbRoot: string, listeners: readonly WalkListener[]): Promise<PluginSourceWalk> {
48
53
  const warnings: string[] = [];
49
54
  const registry = await loadRegistry(kbRoot, warnings);
50
- const listener = pluginListener(this.disk, kbRoot, registry, warnings);
55
+ const listener = pluginListener(this.disk, kbRoot, this.kb.layout, registry, warnings);
51
56
  const { holes } = await this.disk.walkKb(kbRoot, [listener.listener, ...listeners]);
52
57
  return { discovery: listener.result(), holes };
53
58
  }
@@ -57,16 +62,18 @@ export class KbPluginSource implements PluginSource {
57
62
  function pluginListener(
58
63
  disk: IFsProbe,
59
64
  kbRoot: string,
65
+ layout: KbLayout,
60
66
  registry: McpRegistry | null,
61
67
  warnings: string[],
62
68
  ): { listener: WalkListener; result(): Discovery } {
69
+ const { pluginsDir } = layout;
63
70
  const unreadable: string[] = [];
64
71
  const plugins: DiscoveredPlugin[] = [];
65
72
  const seen = new Map<string, string>();
66
73
  /** Folders claimed as plugins (or as holes standing where a plugin may be): nothing beneath is looked at. */
67
74
  const claimed: string[] = [];
68
75
 
69
- const underRoot = (rel: string) => rel === PLUGINS_DIR || rel.startsWith(`${PLUGINS_DIR}/`);
76
+ const underRoot = (rel: string) => rel === pluginsDir || rel.startsWith(`${pluginsDir}/`);
70
77
  const beneathClaimed = (rel: string) => claimed.some((c) => rel === c || rel.startsWith(`${c}/`));
71
78
 
72
79
  // Uniqueness is judged on the MANIFEST SLUG, not the raw name: `Sales Team`
@@ -87,18 +94,18 @@ function pluginListener(
87
94
  return {
88
95
  listener: {
89
96
  async onDir(rel, entries) {
90
- if (!underRoot(rel) || rel === PLUGINS_DIR || beneathClaimed(rel)) return;
97
+ if (!underRoot(rel) || rel === pluginsDir || beneathClaimed(rel)) return;
91
98
  const has = (name: string) => entries.some((e) => e.isFile() && e.name === name);
92
99
  const dir = path.join(kbRoot, rel);
93
- const relFolder = rel.slice(PLUGINS_DIR.length + 1);
100
+ const relFolder = rel.slice(pluginsDir.length + 1);
94
101
  if (has(PLUGIN_MANIFEST_FILE)) {
95
102
  claimed.push(rel);
96
103
  // A manifest that could not be read: a hole, and still claimed.
97
- const native = await readNativePlugin(disk, dir, rel, relFolder, warnings, unreadable);
104
+ const native = await readNativePlugin(disk, dir, rel, relFolder, warnings, unreadable, layout);
98
105
  if (native) claim(native);
99
106
  } else if (has(BUNDLE_FILE)) {
100
107
  claimed.push(rel);
101
- const bundle = await readBundlePlugin(dir, rel, relFolder, registry, warnings, unreadable);
108
+ const bundle = await readBundlePlugin(dir, rel, relFolder, registry, warnings, unreadable, layout);
102
109
  if (bundle) claim(bundle);
103
110
  }
104
111
  },
@@ -8,6 +8,7 @@ import {
8
8
  linkedSkillRoots,
9
9
  pluginDisplayNameOf,
10
10
  pluginIdentityOf,
11
+ type KbLayout,
11
12
  } from '@bevel-software/platform-shared';
12
13
  import { isAbsence, type IFsProbe } from '../../../shared/fs.contract.js';
13
14
  import type { DiscoveredPlugin } from './plugin-source.js';
@@ -31,6 +32,8 @@ export async function readNativePlugin(
31
32
  relFolder: string,
32
33
  warnings: string[],
33
34
  unreadable: string[],
35
+ /** Names the plugins root a personal folder sits directly under. */
36
+ layout: KbLayout,
34
37
  ): Promise<DiscoveredPlugin | null> {
35
38
  const folderName = path.posix.basename(relFolder);
36
39
  const manifestRead = await readText(path.join(dir, PLUGIN_MANIFEST_FILE), folder, warnings);
@@ -97,7 +100,7 @@ export async function readNativePlugin(
97
100
  folder,
98
101
  relFolder,
99
102
  // The one structural rule: a direct child of the root with the prefix.
100
- personal: isPersonalPluginDir(folder),
103
+ personal: isPersonalPluginDir(folder, layout),
101
104
  exists,
102
105
  manifest,
103
106
  manifestText,
@@ -1,4 +1,4 @@
1
- export { PluginIndexService, pluginsWorkspaceId, pluginFolderBelowRoot } from './plugins.service.js';
1
+ export { PluginIndexService, pluginFolderBelowRoot } from './plugins.service.js';
2
2
  export {
3
3
  PluginProvisionService,
4
4
  PluginProvisionError,
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  KNOWN_VERBS,
3
+ ownAccessReadable,
3
4
  parseAccessFile,
5
+ parseOwnAccessEntries,
4
6
  type ParsedEntry,
5
7
  type Verb,
6
8
  } from '../access-model/access-grammar.js';
9
+ import type { TargetKind } from '../access/access-mutation.service.js';
7
10
 
8
11
  /**
9
12
  * What a join branch is PROPOSING, relative to the default branch.
@@ -50,23 +53,75 @@ function identityOf(entry: ParsedEntry): string {
50
53
  }
51
54
 
52
55
  /**
53
- * Folder rules from one copy of an `access.md`, per verb, grants only.
56
+ * The per-verb entries one copy of an item's rules declares, or null when the
57
+ * text could not be read as rules at all.
54
58
  *
55
- * A file that fails to parse yields NO grants. On the branch side that means
56
- * a malformed proposal offers nothing to accept (fail-closed). On the default
57
- * side it means every branch grant looks incoming — which is the safe
58
- * direction too: the manager is shown proposals to consider rather than
59
- * having them silently swallowed by an unreadable baseline.
59
+ * THE GRAMMAR FOLLOWS THE ITEM, because there are two of them and the
60
+ * resolver picks by item as well:
61
+ *
62
+ * folder a `access.md`, whose folder rules are a BODY of `verb:` block
63
+ * lists — `parseAccessFile`, which fails the whole file if a verb
64
+ * is not a list.
65
+ * file the node's OWN frontmatter — `parseOwnAccessEntries`, which also
66
+ * accepts the single-value scalar form (`write: Rita <r@x.io>`),
67
+ * ignores non-access keys like `nodeType:`, and drops a bad entry
68
+ * rather than failing the file.
69
+ *
70
+ * Reading a file's frontmatter with the FOLDER grammar is what broke every
71
+ * request whose target was a file: `spliceGrant` writes one grant into a
72
+ * node's frontmatter as exactly that scalar — the same bytes the dialog's own
73
+ * grant writes, which the resolver honours — and the folder grammar rejects it
74
+ * with "'write:' must be a list", failing the parse, yielding no grants, which
75
+ * reads as a branch with nothing left to propose. The request was then closed
76
+ * on the editors' first listing, before anyone had seen it.
77
+ *
78
+ * Null is reserved for "could not be read": a folder `access.md` that does
79
+ * not parse, or a file whose frontmatter is broken (never closed, or not a
80
+ * mapping). A file with no frontmatter, or with frontmatter naming no verb,
81
+ * is perfectly readable and simply grants nothing. The file grammar's own
82
+ * null does not tell those apart, so the question is asked separately: a
83
+ * broken block read as "grants nothing" would close the request on a
84
+ * reading that failed, the same mistake by another door.
60
85
  */
61
- function grantsByVerb(text: string | null, path: string): Map<Verb, Map<string, ParsedEntry>> {
62
- const out = new Map<Verb, Map<string, ParsedEntry>>();
63
- for (const verb of KNOWN_VERBS) out.set(verb, new Map());
64
- if (text === null) return out;
86
+ function entriesOf(
87
+ text: string,
88
+ path: string,
89
+ kind: TargetKind,
90
+ ): Record<Verb, ParsedEntry[]> | null {
91
+ if (kind === 'file') {
92
+ return parseOwnAccessEntries(text) ?? (ownAccessReadable(text) ? emptyGrants() : null);
93
+ }
65
94
  const parsed = parseAccessFile(text, path);
66
- if (!parsed.ok) return out;
95
+ return parsed.ok ? parsed.file.entries : null;
96
+ }
97
+
98
+ function emptyGrants(): Record<Verb, ParsedEntry[]> {
99
+ const out = {} as Record<Verb, ParsedEntry[]>;
100
+ for (const verb of KNOWN_VERBS) out[verb] = [];
101
+ return out;
102
+ }
103
+
104
+ /** An index that grants nothing — the baseline a copy nobody could read stands in as. */
105
+ function emptyIndex(): Map<Verb, Map<string, ParsedEntry>> {
106
+ return new Map(KNOWN_VERBS.map((verb) => [verb, new Map<string, ParsedEntry>()]));
107
+ }
108
+
109
+ /**
110
+ * Grants only, indexed by canonical identity, from one copy of the rules.
111
+ * Null in, null out — an unreadable copy is not an empty one.
112
+ */
113
+ function grantsByVerb(
114
+ text: string | null,
115
+ path: string,
116
+ kind: TargetKind,
117
+ ): Map<Verb, Map<string, ParsedEntry>> | null {
118
+ if (text === null) return null;
119
+ const entries = entriesOf(text, path, kind);
120
+ if (entries === null) return null;
121
+ const out = emptyIndex();
67
122
  for (const verb of KNOWN_VERBS) {
68
123
  const byId = out.get(verb)!;
69
- for (const entry of parsed.file.entries[verb]) {
124
+ for (const entry of entries[verb]) {
70
125
  if (entry.deny) continue;
71
126
  byId.set(identityOf(entry), entry);
72
127
  }
@@ -75,19 +130,32 @@ function grantsByVerb(text: string | null, path: string): Map<Verb, Map<string,
75
130
  }
76
131
 
77
132
  /**
78
- * The grants `branchText` adds over `defaultText` — the proposals a manager
79
- * can accept. Empty ⇒ the branch's rules are a subset of the default's.
133
+ * The grants `branchText` adds over `defaultText` — the proposals an editor
134
+ * can accept. Empty ⇒ the branch's rules are a subset of the default's, which
135
+ * is the settled state.
136
+ *
137
+ * NULL when the branch's own copy is absent or unreadable, which is a
138
+ * different fact entirely and the one this surface keeps getting wrong: an
139
+ * empty answer closes somebody's request, so it has to mean "the branch asks
140
+ * for nothing more", never "the branch would not tell us".
141
+ *
142
+ * An unreadable DEFAULT copy stays what it was — every branch grant looks
143
+ * incoming. That is the safe direction on that side: an editor is shown
144
+ * proposals to consider rather than having them swallowed by a baseline
145
+ * nobody could read.
80
146
  *
81
- * `path` is only used to label parse errors; both texts are the same file at
82
- * two refs.
147
+ * `path` labels parse errors; both texts are the same file at two refs, and
148
+ * `kind` says which grammar that file is written in.
83
149
  */
84
150
  export function pendingProposals(
85
151
  branchText: string | null,
86
152
  defaultText: string | null,
87
153
  path: string,
88
- ): JoinProposal[] {
89
- const branch = grantsByVerb(branchText, path);
90
- const base = grantsByVerb(defaultText, path);
154
+ kind: TargetKind,
155
+ ): JoinProposal[] | null {
156
+ const branch = grantsByVerb(branchText, path, kind);
157
+ if (branch === null) return null;
158
+ const base = grantsByVerb(defaultText, path, kind) ?? emptyIndex();
91
159
  const out: JoinProposal[] = [];
92
160
  for (const verb of KNOWN_VERBS) {
93
161
  const baseIds = base.get(verb)!;
@@ -1,5 +1,4 @@
1
1
  import {
2
- DEFAULT_BRANCH,
3
2
  joinBranchFor,
4
3
  type AuthUser,
5
4
  type ChangeRequestState,
@@ -9,8 +8,8 @@ import { spliceGrant } from '../access-model/access-splice.js';
9
8
  import { sanitizeError } from '../workflow/sanitize-error.js';
10
9
  import { logger } from '../../shared/logging.js';
11
10
  import { printable } from '../../shared/printable.js';
11
+ import type { KbContext } from '../../shared/kb-context.js';
12
12
  import type { WorkspaceService } from '../workspace/workspace.service.js';
13
- import { pluginsWorkspaceId } from './plugins.service.js';
14
13
  import type {
15
14
  ClaimedJoinRequest,
16
15
  JoinRequestRecord,
@@ -148,7 +147,8 @@ export interface JoinRequestJobsDeps {
148
147
  | 'listChangeRequestsAuthoredBy'
149
148
  >;
150
149
  workspaceService: Pick<WorkspaceService, 'getOrCreateForBranch' | 'readFile' | 'writeFile'>;
151
- kbDirName: string;
150
+ /** The checkout folder, and which branch the request is cut from and aimed at. */
151
+ kb: Pick<KbContext, 'kbDirName' | 'defaultBranch' | 'defaultWorkspaceId'>;
152
152
  /**
153
153
  * The plugin a recorded key still names, or null when nothing does — it was
154
154
  * deleted, or its folder moved out from under the record.
@@ -508,7 +508,8 @@ export class PluginJoinRequestJobs {
508
508
  claim: ClaimedJoinRequest,
509
509
  stillOurs: () => Promise<void>,
510
510
  ): Promise<void> {
511
- const { workflow, workspaceService, kbDirName } = this.deps;
511
+ const { workflow, workspaceService, kb } = this.deps;
512
+ const { kbDirName } = kb;
512
513
  const target = await this.deps.target(record.pluginKey);
513
514
  if (!target) throw new Error('the plugin is no longer available');
514
515
  const user = await this.deps.requester(record.requesterEmail);
@@ -607,7 +608,7 @@ export class PluginJoinRequestJobs {
607
608
  }
608
609
  const detail = await workflow.openChangeRequest(ws.id, user, {
609
610
  sourceBranch: branch,
610
- targetBranch: DEFAULT_BRANCH,
611
+ targetBranch: kb.defaultBranch,
611
612
  // People read these: the display name, not the identifier.
612
613
  title: `Join request: ${target.displayName}`,
613
614
  description:
@@ -637,9 +638,9 @@ export class PluginJoinRequestJobs {
637
638
  * leaves the next click free to retry the same row.
638
639
  */
639
640
  private async ensureBranch(branch: string): Promise<void> {
640
- const wsId = pluginsWorkspaceId();
641
+ const wsId = this.deps.kb.defaultWorkspaceId();
641
642
  try {
642
- await this.deps.workflow.createBranch(wsId, branch, DEFAULT_BRANCH);
643
+ await this.deps.workflow.createBranch(wsId, branch, this.deps.kb.defaultBranch);
643
644
  return;
644
645
  } catch (err) {
645
646
  const branches = await this.deps.workflow.listBranches(wsId, {
@@ -1,5 +1,4 @@
1
1
  import {
2
- DEFAULT_BRANCH,
3
2
  isJoinBranchFor,
4
3
  type AuthUser,
5
4
  type ChangeRequest,
@@ -9,7 +8,14 @@ import { type WorkspaceService } from '../workspace/workspace.service.js';
9
8
  import { logger } from '../../shared/logging.js';
10
9
 
11
10
  const log = logger('plugins');
12
- import { workspaceIdForBranch } from '../../shared/workspace-id.js';
11
+ import type { KbContext } from '../../shared/kb-context.js';
12
+ import type { IAccessControl } from '../access/access-control.interface.js';
13
+ import {
14
+ rulesFileFor,
15
+ type AccessRequestTarget,
16
+ type IAccessRequestLifecycle,
17
+ } from '../access/access-requests.contract.js';
18
+ import type { Verb } from '../access-model/access-grammar.js';
13
19
  import { pendingProposals, type JoinProposal } from './join-proposals.js';
14
20
 
15
21
  /** One open join change request, with what it still proposes. */
@@ -23,56 +29,185 @@ export interface JoinRequest {
23
29
  }
24
30
 
25
31
  /**
26
- * Join requests as PROPOSALS, and the reconciliation that retires them.
32
+ * Access requests as PROPOSALS, and the reconciliation that retires them.
27
33
  *
28
34
  * The lifecycle has no state of its own — it is derived, every time, from two
29
- * copies of one file:
35
+ * copies of one file (a folder's `access.md`, or a file's own frontmatter):
30
36
  *
31
- * open the branch's `access.md` grants something the default's does not
37
+ * open the branch proposes something the person does not already hold
32
38
  * settled it does not
33
39
  *
34
- * so "approve" is not an operation on the request at all. A manager grants a
40
+ * so "approve" is not an operation on the request at all. An editor grants a
35
41
  * principal through the ordinary access path (the same lock + commit every
36
42
  * other access edit uses, gated the same way), the default branch gains that
37
- * entry, and the proposal stops being pending because the diff no longer
38
- * contains it. When the last one goes, the change request has nothing left to
39
- * say and is rejected, its branch deleted.
43
+ * entry, and the proposal stops being pending.
44
+ *
45
+ * "Does not already hold" is stronger than "is not in the file", and
46
+ * deliberately so: Owner covers Can edit, a parent folder's grant covers this
47
+ * one, and a role or group carries access without naming anybody. Each of
48
+ * those would leave a proposal nobody can usefully accept — accepting writes
49
+ * a grant that changes nothing — and the request would sit open forever. See
50
+ * {@link JoinRequestsService.unmetProposals}.
40
51
  *
41
- * That reconciliation runs LAZILY on every listing as well as on demand, so a
42
- * grant made anywhere — this banner, the Manage-access dialog, a hand edit —
43
- * settles the request that asked for it. Nothing has to remember to.
52
+ * That reconciliation runs LAZILY on every listing as well as on demand, so
53
+ * access gained anywhere — this banner, the Manage access dialog, a hand
54
+ * edit, a role someone joined — settles the request that asked for it.
55
+ * Nothing has to remember to.
44
56
  */
45
- export class JoinRequestsService {
57
+ export class JoinRequestsService implements IAccessRequestLifecycle {
46
58
  constructor(
47
59
  private readonly workspaceService: WorkspaceService,
48
60
  private readonly workflow: IWorkflowService,
61
+ private readonly kb: Pick<KbContext, 'defaultBranch' | 'defaultWorkspaceId'>,
62
+ /**
63
+ * Read-only, and asked one question only: does this person already hold
64
+ * this verb on the item, on the DEFAULT branch? See {@link unmetProposals}.
65
+ */
66
+ private readonly accessControl: Pick<
67
+ IAccessControl,
68
+ 'canRead' | 'canWrite' | 'canOwner' | 'canDownload'
69
+ >,
49
70
  ) {}
50
71
 
51
72
  /**
52
- * Open join requests for `plugin`, each carrying only its still-pending
53
- * proposals. Requests with none left are reconciled away (rejected, branch
54
- * deleted) and omitted.
73
+ * What `branch` still proposes for `target`, minus everything the person it
74
+ * names already holds — or NULL when the branch's own copy could not be read
75
+ * at all.
76
+ *
77
+ * That distinction is the whole point of the return type. "The branch says
78
+ * nothing" is NOT "the branch proposes nothing", and the two arrive as the
79
+ * same empty diff: `pendingProposals` reads an unreadable file as carrying
80
+ * no grants (fail-closed, which is right for deciding what to SHOW). Used as
81
+ * the settle test it is fail-open in the one direction that cannot be taken
82
+ * back — it closes a request nobody answered.
83
+ *
84
+ * And unreadable is the normal case for a few seconds. These reads go
85
+ * through `origin/<branch>` in the DEFAULT branch's clone, whose fetch is
86
+ * throttled; a request opened moments ago names a ref that clone has not
87
+ * heard of. So a miss is retried behind a FORCED fetch before it is believed,
88
+ * and if it still misses the caller is told nothing rather than something
89
+ * false.
90
+ */
91
+ async proposalsOn(
92
+ branch: string,
93
+ target: AccessRequestTarget,
94
+ opts: { fresh?: boolean } = {},
95
+ ): Promise<JoinProposal[] | null> {
96
+ const rulesPath = rulesFileFor(target);
97
+ await this.refresh({ force: opts.fresh });
98
+ let branchText = await this.readAt(branch, rulesPath);
99
+ if (branchText === null && !opts.fresh) {
100
+ await this.refresh({ force: true });
101
+ branchText = await this.readAt(branch, rulesPath);
102
+ }
103
+ if (branchText === null) return null;
104
+ // `kind` picks the grammar: a folder's rules are block lists in an
105
+ // access.md body, a file's are its own frontmatter, where one grant is
106
+ // written as a scalar. Reading one with the other's grammar yields no
107
+ // grants, which reads as a finished request.
108
+ const proposals = pendingProposals(
109
+ branchText,
110
+ await this.readAt(this.kb.defaultBranch, rulesPath),
111
+ rulesPath,
112
+ target.kind,
113
+ );
114
+ // Unreadable rules on the branch: say nothing rather than "nothing left".
115
+ if (proposals === null) return null;
116
+ return this.unmetProposals(proposals, target);
117
+ }
118
+
119
+ /**
120
+ * Drop every proposal the live workspace has already ANSWERED, however it
121
+ * was answered.
122
+ *
123
+ * The file diff alone cannot see this. A request asking for `write` is
124
+ * satisfied by an `owner:` line, by a grant on a parent folder, or by a role
125
+ * or group the person has since joined — none of which put their email into
126
+ * this file, so the diff keeps reporting a proposal nobody can usefully
127
+ * accept (accepting writes a grant that changes nothing, and the request
128
+ * never retires). Asking the resolver the question the dialog's own grant
129
+ * would have to satisfy closes those requests instead of stranding them.
130
+ *
131
+ * A proposal naming a ROLE keeps the literal rule: "does this role hold the
132
+ * verb here" is not a question the per-person resolver answers, and a role
133
+ * proposal is answered by the live rules carrying it.
134
+ */
135
+ private async unmetProposals(
136
+ proposals: JoinProposal[],
137
+ target: AccessRequestTarget,
138
+ ): Promise<JoinProposal[]> {
139
+ const out: JoinProposal[] = [];
140
+ for (const proposal of proposals) {
141
+ if (proposal.principal.kind !== 'user') {
142
+ out.push(proposal);
143
+ continue;
144
+ }
145
+ if (await this.holds(proposal.principal.email, proposal.verb, target.path)) continue;
146
+ out.push(proposal);
147
+ }
148
+ return out;
149
+ }
150
+
151
+ /**
152
+ * Does `email` hold `verb` on the ITEM, on the default branch? The verb
153
+ * folds are the resolver's own (owner covers write, write covers read), so
154
+ * this asks exactly what an accepted grant would have to achieve.
155
+ *
156
+ * Best-effort in ONE direction: a resolver that fails answers "no", which
157
+ * leaves the request open. A failure must never close somebody's request for
158
+ * them.
159
+ */
160
+ private async holds(email: string, verb: Verb, itemPath: string): Promise<boolean> {
161
+ const wsId = this.kb.defaultWorkspaceId();
162
+ try {
163
+ switch (verb) {
164
+ case 'owner':
165
+ return await this.accessControl.canOwner(wsId, email, itemPath);
166
+ case 'write':
167
+ return await this.accessControl.canWrite(wsId, email, itemPath);
168
+ case 'download':
169
+ return await this.accessControl.canDownload(wsId, email, itemPath);
170
+ default:
171
+ return await this.accessControl.canRead(wsId, email, itemPath);
172
+ }
173
+ } catch (err) {
174
+ log.warn(
175
+ `could not resolve ${verb} on "${itemPath}" for a request: ${
176
+ err instanceof Error ? err.message : String(err)
177
+ }`,
178
+ );
179
+ return false;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Open requests for one item, each carrying only what it still proposes.
185
+ * Requests with nothing left are reconciled away (rejected, branch deleted)
186
+ * and omitted.
55
187
  *
56
- * `actor` is who the reconciliation acts as — a manager of the plugin, since
188
+ * `key` is what named the branch (`joinBranchFor`) — a plugin's name for a
189
+ * Subscribe request, the item's own path for everything the Manage access
190
+ * dialog opens. `target` is the item whose rules those proposals edit; the
191
+ * two differ only for plugins, whose folder is not their name.
192
+ *
193
+ * `actor` is who the reconciliation acts as — an editor of the item, since
57
194
  * rejecting a change request requires write on everything it touches.
58
195
  */
59
196
  async list(
60
- plugin: string,
61
- folder: string,
197
+ key: string,
198
+ target: AccessRequestTarget,
62
199
  crs: ChangeRequest[],
63
200
  actor: AuthUser,
64
201
  ): Promise<JoinRequest[]> {
65
- await this.refresh();
66
- const accessPath = `${folder}/access.md`;
67
- const baseText = await this.readAt(DEFAULT_BRANCH, accessPath);
68
202
  const out: JoinRequest[] = [];
69
203
  for (const cr of crs) {
70
- if (cr.state !== 'open' || !isJoinBranchFor(cr.branch, plugin)) continue;
71
- const proposals = pendingProposals(
72
- await this.readAt(cr.branch, accessPath),
73
- baseText,
74
- accessPath,
75
- );
204
+ if (cr.state !== 'open' || !isJoinBranchFor(cr.branch, key)) continue;
205
+ const proposals = await this.confirmedProposals(cr.branch, target);
206
+ // Unreadable: leave it exactly as it is. There is nothing to offer an
207
+ // editor and nothing that says it is finished, and closing it on a read
208
+ // that failed would end somebody's request for them. It stays in Change
209
+ // requests for you, where it can still be answered by hand.
210
+ if (proposals === null) continue;
76
211
  if (proposals.length === 0) {
77
212
  await this.settle(cr, actor);
78
213
  continue;
@@ -94,24 +229,46 @@ export class JoinRequestsService {
94
229
  * same round-trip instead of waiting for the next listing.
95
230
  */
96
231
  async reconcile(
97
- plugin: string,
98
- folder: string,
232
+ key: string,
233
+ target: AccessRequestTarget,
99
234
  cr: ChangeRequest,
100
235
  actor: AuthUser,
101
236
  ): Promise<boolean> {
102
- if (cr.state !== 'open' || !isJoinBranchFor(cr.branch, plugin)) return false;
103
- await this.refresh();
104
- const accessPath = `${folder}/access.md`;
105
- const proposals = pendingProposals(
106
- await this.readAt(cr.branch, accessPath),
107
- await this.readAt(DEFAULT_BRANCH, accessPath),
108
- accessPath,
109
- );
110
- if (proposals.length > 0) return false;
237
+ if (cr.state !== 'open' || !isJoinBranchFor(cr.branch, key)) return false;
238
+ const proposals = await this.confirmedProposals(cr.branch, target);
239
+ // Same rule as the listing: only a branch that was READ and, on a fresh
240
+ // read, proposes nothing is finished.
241
+ if (proposals === null || proposals.length > 0) return false;
111
242
  await this.settle(cr, actor);
112
243
  return true;
113
244
  }
114
245
 
246
+ /**
247
+ * What the branch proposes, with an EMPTY answer confirmed against freshly
248
+ * fetched refs before anyone acts on it. Null keeps its meaning: nothing
249
+ * could be read, so nothing can be said.
250
+ *
251
+ * The confirmation is there because an empty answer is the terminal one.
252
+ * These reads go through `origin/<branch>` in the default branch's clone,
253
+ * and that clone's fetch is throttled — so a branch whose remote-tracking
254
+ * ref is a few seconds old answers with the commit BEFORE the proposal was
255
+ * pushed. That is a file identical to live, which is an empty diff, which
256
+ * reads as "every grant has landed". A request nobody answered is then
257
+ * closed, its branch deleted, and the person told it was not accepted.
258
+ *
259
+ * One extra fetch, on the one path that cannot be undone — and when that
260
+ * fetch turns up a proposal after all, it is the answer, not a discarded
261
+ * second opinion.
262
+ */
263
+ private async confirmedProposals(
264
+ branch: string,
265
+ target: AccessRequestTarget,
266
+ ): Promise<JoinProposal[] | null> {
267
+ const first = await this.proposalsOn(branch, target);
268
+ if (first === null || first.length > 0) return first;
269
+ return this.proposalsOn(branch, target, { fresh: true });
270
+ }
271
+
115
272
  /**
116
273
  * Close a request whose proposals have all landed, and delete its branch.
117
274
  *
@@ -125,7 +282,7 @@ export class JoinRequestsService {
125
282
  * next pass tries again.
126
283
  */
127
284
  private async settle(cr: ChangeRequest, actor: AuthUser): Promise<void> {
128
- const wsId = workspaceIdForBranch(DEFAULT_BRANCH);
285
+ const wsId = this.kb.defaultWorkspaceId();
129
286
  try {
130
287
  await this.workflow.rejectChangeRequest(
131
288
  cr.number,
@@ -167,7 +324,7 @@ export class JoinRequestsService {
167
324
  private async readAt(branch: string, repoRelPath: string): Promise<string | null> {
168
325
  try {
169
326
  return await this.workspaceService.readFileAtRef(
170
- workspaceIdForBranch(DEFAULT_BRANCH),
327
+ this.kb.defaultWorkspaceId(),
171
328
  `origin/${branch}`,
172
329
  repoRelPath,
173
330
  );
@@ -180,10 +337,13 @@ export class JoinRequestsService {
180
337
  * Refresh remote-tracking refs before reading them. Throttled inside, and
181
338
  * best-effort: a request pushed seconds ago is worth one fetch, but a fetch
182
339
  * failure must degrade to "read what we have" rather than empty the list.
340
+ *
341
+ * `force` skips the throttle — for the one caller that has already missed
342
+ * and must not mistake a stale ref list for an answer.
183
343
  */
184
- private async refresh(): Promise<void> {
344
+ private async refresh(opts: { force?: boolean } = {}): Promise<void> {
185
345
  await this.workspaceService
186
- .ensureRemotesFetched(workspaceIdForBranch(DEFAULT_BRANCH))
346
+ .ensureRemotesFetched(this.kb.defaultWorkspaceId(), opts)
187
347
  .catch(() => undefined);
188
348
  }
189
349
  }