@bevel-software/platform-core-backend 0.21.0 → 0.23.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 (434) hide show
  1. package/dist/core/core-ports.d.ts +11 -1
  2. package/dist/core/core-ports.d.ts.map +1 -1
  3. package/dist/core/core-ports.js.map +1 -1
  4. package/dist/core/create-core-server.d.ts +3 -3
  5. package/dist/core/create-core-server.d.ts.map +1 -1
  6. package/dist/core/create-core-server.js +62 -13
  7. package/dist/core/create-core-server.js.map +1 -1
  8. package/dist/core/create-core-services.d.ts +22 -3
  9. package/dist/core/create-core-services.d.ts.map +1 -1
  10. package/dist/core/create-core-services.js +158 -23
  11. package/dist/core/create-core-services.js.map +1 -1
  12. package/dist/core/lifecycle.d.ts +34 -1
  13. package/dist/core/lifecycle.d.ts.map +1 -1
  14. package/dist/core/lifecycle.js +89 -13
  15. package/dist/core/lifecycle.js.map +1 -1
  16. package/dist/core-config.d.ts +0 -12
  17. package/dist/core-config.d.ts.map +1 -1
  18. package/dist/core-config.js +11 -13
  19. package/dist/core-config.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/modules/access/access-control.interface.d.ts +42 -12
  25. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  26. package/dist/modules/access/access-control.service.d.ts +67 -10
  27. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  28. package/dist/modules/access/access-control.service.js +214 -35
  29. package/dist/modules/access/access-control.service.js.map +1 -1
  30. package/dist/modules/access/access-requests.contract.d.ts +75 -0
  31. package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
  32. package/dist/modules/access/access-requests.contract.js +20 -0
  33. package/dist/modules/access/access-requests.contract.js.map +1 -0
  34. package/dist/modules/access/access-requests.routes.d.ts +35 -0
  35. package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
  36. package/dist/modules/access/access-requests.routes.js +237 -0
  37. package/dist/modules/access/access-requests.routes.js.map +1 -0
  38. package/dist/modules/access/access-requests.service.d.ts +123 -0
  39. package/dist/modules/access/access-requests.service.d.ts.map +1 -0
  40. package/dist/modules/access/access-requests.service.js +337 -0
  41. package/dist/modules/access/access-requests.service.js.map +1 -0
  42. package/dist/modules/access/access.routes.d.ts.map +1 -1
  43. package/dist/modules/access/access.routes.js +13 -15
  44. package/dist/modules/access/access.routes.js.map +1 -1
  45. package/dist/modules/access-model/access-grammar.d.ts +12 -0
  46. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  47. package/dist/modules/access-model/access-grammar.js +24 -0
  48. package/dist/modules/access-model/access-grammar.js.map +1 -1
  49. package/dist/modules/auth/account-admission.d.ts +58 -7
  50. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  51. package/dist/modules/auth/account-admission.js +44 -1
  52. package/dist/modules/auth/account-admission.js.map +1 -1
  53. package/dist/modules/auth/account.routes.d.ts +1 -1
  54. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  55. package/dist/modules/auth/account.routes.js +61 -5
  56. package/dist/modules/auth/account.routes.js.map +1 -1
  57. package/dist/modules/auth/auth.middleware.d.ts +1 -1
  58. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  59. package/dist/modules/auth/auth.middleware.js +18 -7
  60. package/dist/modules/auth/auth.middleware.js.map +1 -1
  61. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  62. package/dist/modules/auth/auth.routes.js +8 -0
  63. package/dist/modules/auth/auth.routes.js.map +1 -1
  64. package/dist/modules/auth/auth.service.d.ts +96 -3
  65. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  66. package/dist/modules/auth/auth.service.js +187 -9
  67. package/dist/modules/auth/auth.service.js.map +1 -1
  68. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  69. package/dist/modules/auth/oidc-auth-provider.js +7 -2
  70. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  71. package/dist/modules/database/core-schema.d.ts +41 -24
  72. package/dist/modules/database/core-schema.d.ts.map +1 -1
  73. package/dist/modules/database/core-schema.js +35 -27
  74. package/dist/modules/database/core-schema.js.map +1 -1
  75. package/dist/modules/github-app/github-app.client.d.ts +101 -0
  76. package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
  77. package/dist/modules/github-app/github-app.client.js +222 -0
  78. package/dist/modules/github-app/github-app.client.js.map +1 -0
  79. package/dist/modules/github-app/github-app.connection.d.ts +102 -0
  80. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
  81. package/dist/modules/github-app/github-app.connection.js +185 -0
  82. package/dist/modules/github-app/github-app.connection.js.map +1 -0
  83. package/dist/modules/github-app/github-app.routes.d.ts +59 -0
  84. package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
  85. package/dist/modules/github-app/github-app.routes.js +323 -0
  86. package/dist/modules/github-app/github-app.routes.js.map +1 -0
  87. package/dist/modules/github-app/index.d.ts +4 -0
  88. package/dist/modules/github-app/index.d.ts.map +1 -0
  89. package/dist/modules/github-app/index.js +4 -0
  90. package/dist/modules/github-app/index.js.map +1 -0
  91. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  92. package/dist/modules/kb-fs/branch-name.js +12 -2
  93. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  94. package/dist/modules/kb-fs/remote-url.d.ts +22 -0
  95. package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
  96. package/dist/modules/kb-fs/remote-url.js +35 -0
  97. package/dist/modules/kb-fs/remote-url.js.map +1 -0
  98. package/dist/modules/kb-fs/repo-path.d.ts +11 -16
  99. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  100. package/dist/modules/kb-fs/repo-path.js +79 -0
  101. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  102. package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
  103. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  104. package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
  105. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  106. package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
  107. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
  108. package/dist/modules/kb-sync/sync-auth.js +1 -1
  109. package/dist/modules/kb-sync/sync-auth.js.map +1 -1
  110. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  111. package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
  112. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  113. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  114. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
  115. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  116. package/dist/modules/plugins/join-proposals.d.ts +17 -5
  117. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  118. package/dist/modules/plugins/join-proposals.js +76 -22
  119. package/dist/modules/plugins/join-proposals.js.map +1 -1
  120. package/dist/modules/plugins/join-requests.service.d.ts +111 -18
  121. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  122. package/dist/modules/plugins/join-requests.service.js +173 -29
  123. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  124. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  125. package/dist/modules/plugins/plugins.routes.js +3 -2
  126. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  127. package/dist/modules/settings/deployment-settings.service.d.ts +22 -1
  128. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  129. package/dist/modules/settings/deployment-settings.service.js +101 -7
  130. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  131. package/dist/modules/settings/managed-repository.d.ts +43 -0
  132. package/dist/modules/settings/managed-repository.d.ts.map +1 -0
  133. package/dist/modules/settings/managed-repository.js +60 -0
  134. package/dist/modules/settings/managed-repository.js.map +1 -0
  135. package/dist/modules/settings/repository-source.d.ts +128 -0
  136. package/dist/modules/settings/repository-source.d.ts.map +1 -0
  137. package/dist/modules/settings/repository-source.js +150 -0
  138. package/dist/modules/settings/repository-source.js.map +1 -0
  139. package/dist/modules/settings/setup.routes.d.ts +78 -4
  140. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  141. package/dist/modules/settings/setup.routes.js +379 -17
  142. package/dist/modules/settings/setup.routes.js.map +1 -1
  143. package/dist/modules/skills/skill-access-requests.routes.d.ts +6 -8
  144. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  145. package/dist/modules/skills/skill-access-requests.routes.js +24 -63
  146. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  147. package/dist/modules/skills/skills.contract.d.ts +90 -18
  148. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  149. package/dist/modules/skills/skills.contract.js +4 -1
  150. package/dist/modules/skills/skills.contract.js.map +1 -1
  151. package/dist/modules/skills/skills.service.d.ts +98 -9
  152. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  153. package/dist/modules/skills/skills.service.js +239 -37
  154. package/dist/modules/skills/skills.service.js.map +1 -1
  155. package/dist/modules/skills/skills.tools.d.ts +7 -0
  156. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  157. package/dist/modules/skills/skills.tools.js +71 -7
  158. package/dist/modules/skills/skills.tools.js.map +1 -1
  159. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  160. package/dist/modules/tool-auth/external-api-key.service.js +4 -2
  161. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  162. package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
  163. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
  164. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  165. package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
  166. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  167. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  168. package/dist/modules/tool-helpers/tool-context.js +15 -0
  169. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  170. package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
  171. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  172. package/dist/modules/tool-helpers/tool-handler.js +25 -4
  173. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  174. package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
  175. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  176. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  177. package/dist/modules/tool-helpers/validate-token.js +1 -1
  178. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  179. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
  180. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
  181. package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
  182. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
  183. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  184. package/dist/modules/workflow/agent-tools/workflow.tools.js +113 -37
  185. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  186. package/dist/modules/workflow/file-lock.service.d.ts +24 -0
  187. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  188. package/dist/modules/workflow/file-lock.service.js +30 -0
  189. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  190. package/dist/modules/workflow/git/git.service.d.ts +72 -1
  191. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  192. package/dist/modules/workflow/git/git.service.js +170 -6
  193. package/dist/modules/workflow/git/git.service.js.map +1 -1
  194. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  195. package/dist/modules/workflow/git/node-git-runner.js +5 -0
  196. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  197. package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
  198. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  199. package/dist/modules/workflow/git/pull-request.service.js +99 -0
  200. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  201. package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
  202. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  203. package/dist/modules/workflow/pending-commits.service.js +55 -0
  204. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  205. package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
  206. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
  207. package/dist/modules/workflow/workflow-hooks.js +16 -1
  208. package/dist/modules/workflow/workflow-hooks.js.map +1 -1
  209. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  210. package/dist/modules/workflow/workflow.routes.js +3 -1
  211. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  212. package/dist/modules/workflow/workflow.service.d.ts +94 -13
  213. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  214. package/dist/modules/workflow/workflow.service.js +258 -51
  215. package/dist/modules/workflow/workflow.service.js.map +1 -1
  216. package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
  217. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
  218. package/dist/modules/workspace/agent-access.gate.js +123 -0
  219. package/dist/modules/workspace/agent-access.gate.js.map +1 -0
  220. package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
  221. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
  222. package/dist/modules/workspace/routine-write-policy.js +5 -6
  223. package/dist/modules/workspace/routine-write-policy.js.map +1 -1
  224. package/dist/modules/workspace/session-sink.d.ts +5 -5
  225. package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
  226. package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
  227. package/dist/modules/workspace/set-aside-clone.js +92 -0
  228. package/dist/modules/workspace/set-aside-clone.js.map +1 -0
  229. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +117 -1
  230. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  231. package/dist/modules/workspace/startup/kb-startup-runner.js +190 -1
  232. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  233. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  234. package/dist/modules/workspace/workspace.routes.js +92 -3
  235. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  236. package/dist/modules/workspace/workspace.service.d.ts +115 -5
  237. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  238. package/dist/modules/workspace/workspace.service.js +272 -26
  239. package/dist/modules/workspace/workspace.service.js.map +1 -1
  240. package/dist/modules/workspace/workspace.tools.d.ts +2 -2
  241. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  242. package/dist/modules/workspace/workspace.tools.js +337 -117
  243. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  244. package/dist/modules/write-access/write-access.d.ts +60 -0
  245. package/dist/modules/write-access/write-access.d.ts.map +1 -0
  246. package/dist/modules/write-access/write-access.js +129 -0
  247. package/dist/modules/write-access/write-access.js.map +1 -0
  248. package/dist/shared/domain-errors.d.ts +64 -0
  249. package/dist/shared/domain-errors.d.ts.map +1 -1
  250. package/dist/shared/domain-errors.js +83 -0
  251. package/dist/shared/domain-errors.js.map +1 -1
  252. package/dist/shared/git.contract.d.ts +30 -0
  253. package/dist/shared/git.contract.d.ts.map +1 -1
  254. package/dist/shared/git.contract.js +26 -0
  255. package/dist/shared/git.contract.js.map +1 -1
  256. package/dist/tenancy/static-tenant-source.d.ts +0 -1
  257. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  258. package/dist/tenancy/static-tenant-source.js +0 -2
  259. package/dist/tenancy/static-tenant-source.js.map +1 -1
  260. package/migrations/0014_change_request_closed_reason.sql +1 -0
  261. package/migrations/0015_account_deactivation.sql +3 -0
  262. package/migrations/meta/0014_snapshot.json +2265 -0
  263. package/migrations/meta/0015_snapshot.json +2271 -0
  264. package/migrations/meta/_journal.json +14 -0
  265. package/package.json +3 -3
  266. package/src/__tests__/kb-layout-config.test.ts +0 -2
  267. package/src/__tests__/retired-settings.test.ts +96 -0
  268. package/src/core/__tests__/gated-boot.test.ts +132 -0
  269. package/src/core/__tests__/lifecycle.test.ts +170 -1
  270. package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
  271. package/src/core/core-ports.ts +11 -1
  272. package/src/core/create-core-server.ts +69 -15
  273. package/src/core/create-core-services.ts +190 -27
  274. package/src/core/lifecycle.ts +98 -13
  275. package/src/core-config.ts +13 -14
  276. package/src/index.ts +12 -1
  277. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
  278. package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
  279. package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
  280. package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
  281. package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
  282. package/src/modules/access/access-control.interface.ts +43 -12
  283. package/src/modules/access/access-control.service.ts +234 -36
  284. package/src/modules/access/access-requests.contract.ts +93 -0
  285. package/src/modules/access/access-requests.routes.ts +286 -0
  286. package/src/modules/access/access-requests.service.ts +420 -0
  287. package/src/modules/access/access.routes.ts +13 -15
  288. package/src/modules/access-model/access-grammar.ts +22 -0
  289. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +3 -1
  290. package/src/modules/auth/__tests__/account-deactivation.test.ts +252 -0
  291. package/src/modules/auth/__tests__/account.routes.test.ts +81 -3
  292. package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
  293. package/src/modules/auth/__tests__/auth.service.test.ts +39 -1
  294. package/src/modules/auth/account-admission.ts +78 -9
  295. package/src/modules/auth/account.routes.ts +63 -7
  296. package/src/modules/auth/auth.middleware.ts +19 -8
  297. package/src/modules/auth/auth.routes.ts +8 -0
  298. package/src/modules/auth/auth.service.ts +207 -7
  299. package/src/modules/auth/oidc-auth-provider.ts +7 -2
  300. package/src/modules/database/core-schema.ts +35 -27
  301. package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
  302. package/src/modules/github-app/github-app.client.ts +268 -0
  303. package/src/modules/github-app/github-app.connection.ts +204 -0
  304. package/src/modules/github-app/github-app.routes.ts +359 -0
  305. package/src/modules/github-app/index.ts +19 -0
  306. package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
  307. package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
  308. package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
  309. package/src/modules/kb-fs/branch-name.ts +14 -1
  310. package/src/modules/kb-fs/remote-url.ts +35 -0
  311. package/src/modules/kb-fs/repo-path.ts +85 -0
  312. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
  313. package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
  314. package/src/modules/kb-sync/sync-auth.ts +2 -2
  315. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
  316. package/src/modules/mcp/__tests__/mcp.service.test.ts +68 -6
  317. package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
  318. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
  319. package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
  320. package/src/modules/plugins/__tests__/join-requests.service.test.ts +27 -11
  321. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
  322. package/src/modules/plugins/__tests__/plugins.routes.test.ts +1 -1
  323. package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
  324. package/src/modules/plugins/join-proposals.ts +87 -19
  325. package/src/modules/plugins/join-requests.service.ts +199 -39
  326. package/src/modules/plugins/plugins.routes.ts +3 -2
  327. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
  328. package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
  329. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +386 -0
  330. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +299 -0
  331. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +236 -0
  332. package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
  333. package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
  334. package/src/modules/settings/deployment-settings.service.ts +117 -6
  335. package/src/modules/settings/managed-repository.ts +68 -0
  336. package/src/modules/settings/repository-source.ts +197 -0
  337. package/src/modules/settings/setup.routes.ts +463 -15
  338. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
  339. package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
  340. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +5 -1
  341. package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
  342. package/src/modules/skills/skill-access-requests.routes.ts +25 -75
  343. package/src/modules/skills/skills.contract.ts +91 -18
  344. package/src/modules/skills/skills.service.ts +278 -41
  345. package/src/modules/skills/skills.tools.ts +80 -8
  346. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
  347. package/src/modules/tool-auth/external-api-key.service.ts +4 -2
  348. package/src/modules/tool-auth/internal-token.service.ts +3 -3
  349. package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
  350. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
  351. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
  352. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
  353. package/src/modules/tool-helpers/tool-context.ts +15 -0
  354. package/src/modules/tool-helpers/tool-handler.ts +24 -4
  355. package/src/modules/tool-helpers/tool.contract.ts +3 -2
  356. package/src/modules/tool-helpers/validate-token.ts +1 -1
  357. package/src/modules/workflow/__tests__/apply-failure.test.ts +9 -1
  358. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
  359. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
  360. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +159 -8
  361. package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
  362. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +146 -40
  363. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +405 -7
  364. package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
  365. package/src/modules/workflow/agent-tools/workflow.tools.ts +135 -38
  366. package/src/modules/workflow/file-lock.service.ts +31 -0
  367. package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
  368. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +121 -0
  369. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
  370. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
  371. package/src/modules/workflow/git/git.service.ts +193 -4
  372. package/src/modules/workflow/git/node-git-runner.ts +6 -0
  373. package/src/modules/workflow/git/pull-request.service.ts +119 -0
  374. package/src/modules/workflow/pending-commits.service.ts +59 -1
  375. package/src/modules/workflow/workflow-hooks.ts +64 -26
  376. package/src/modules/workflow/workflow.routes.ts +3 -1
  377. package/src/modules/workflow/workflow.service.ts +290 -55
  378. package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
  379. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +384 -0
  380. package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
  381. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
  382. package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
  383. package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
  384. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
  385. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
  386. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +326 -0
  387. package/src/modules/workspace/__tests__/workspace.service.test.ts +20 -9
  388. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +43 -0
  389. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +2 -3
  390. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +235 -10
  391. package/src/modules/workspace/__tests__/workspace.tools.test.ts +720 -17
  392. package/src/modules/workspace/agent-access.gate.ts +164 -0
  393. package/src/modules/workspace/routine-write-policy.ts +5 -6
  394. package/src/modules/workspace/session-sink.ts +5 -5
  395. package/src/modules/workspace/set-aside-clone.ts +96 -0
  396. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +297 -0
  397. package/src/modules/workspace/startup/kb-startup-runner.ts +242 -2
  398. package/src/modules/workspace/workspace.routes.ts +95 -3
  399. package/src/modules/workspace/workspace.service.ts +273 -25
  400. package/src/modules/workspace/workspace.tools.ts +373 -119
  401. package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
  402. package/src/modules/write-access/write-access.ts +153 -0
  403. package/src/shared/domain-errors.ts +89 -0
  404. package/src/shared/git.contract.ts +37 -0
  405. package/src/tenancy/__tests__/static-tenant-source.test.ts +0 -1
  406. package/src/tenancy/static-tenant-source.ts +0 -3
  407. package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
  408. package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
  409. package/dist/modules/workflow/session-ontology.policy.js +0 -62
  410. package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
  411. package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
  412. package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
  413. package/dist/modules/workflow/session-ontology.service.js +0 -147
  414. package/dist/modules/workflow/session-ontology.service.js.map +0 -1
  415. package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
  416. package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
  417. package/dist/modules/workspace/session-ontology.gate.js +0 -161
  418. package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
  419. package/dist/shared/kb-layout.d.ts +0 -39
  420. package/dist/shared/kb-layout.d.ts.map +0 -1
  421. package/dist/shared/kb-layout.js +0 -103
  422. package/dist/shared/kb-layout.js.map +0 -1
  423. package/dist/shared/kb-layout.test.d.ts +0 -2
  424. package/dist/shared/kb-layout.test.d.ts.map +0 -1
  425. package/dist/shared/kb-layout.test.js +0 -75
  426. package/dist/shared/kb-layout.test.js.map +0 -1
  427. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
  428. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
  429. package/src/modules/workflow/session-ontology.policy.ts +0 -70
  430. package/src/modules/workflow/session-ontology.service.ts +0 -183
  431. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
  432. package/src/modules/workspace/session-ontology.gate.ts +0 -191
  433. package/src/shared/kb-layout.test.ts +0 -98
  434. package/src/shared/kb-layout.ts +0 -102
@@ -10,6 +10,7 @@ import { createDiffRoutes } from '../modules/diff/diff.routes.js';
10
10
  import { createWorkflowRoutes } from '../modules/workflow/workflow.routes.js';
11
11
  import { createEventsRoutes } from '../modules/workflow/events.routes.js';
12
12
  import { createAccessRoutes } from '../modules/access/access.routes.js';
13
+ import { createAccessRequestRoutes } from '../modules/access/access-requests.routes.js';
13
14
  import { createMcpRoutes } from '../modules/mcp/mcp.routes.js';
14
15
  import { createOAuthConsentRoutes } from '../modules/mcp/oauth/oauth-consent.routes.js';
15
16
  import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';
@@ -36,7 +37,7 @@ import {
36
37
  registerPluginsTools,
37
38
  } from '../modules/plugins/index.js';
38
39
  import { keyOrSessionAuth } from '../modules/tool-auth/key-or-session.middleware.js';
39
- import type { SessionOntologyGate } from '../modules/workspace/session-ontology.gate.js';
40
+ import { ToolDescriptionNotes, type AgentAccessGate } from '../modules/workspace/agent-access.gate.js';
40
41
  import {
41
42
  createSecretsVaultRoutes,
42
43
  createSecretsVaultPublicRoutes,
@@ -50,7 +51,8 @@ import { createConnectionKeysAdminRoutes } from '../modules/tool-auth/connection
50
51
  import { createAuditRoutes } from '../modules/audit/audit.routes.js';
51
52
  import { createAgentRestAuditMiddleware } from '../modules/audit/agent-rest-audit.middleware.js';
52
53
  import { EXTERNAL_KB_MANUAL_NAME } from '../modules/tool-manuals/tool-manuals.contract.js';
53
- import { createSetupRoutes } from '../modules/settings/setup.routes.js';
54
+ import { createSetupRoutes, isComplete } from '../modules/settings/setup.routes.js';
55
+ import { createGitHubAppRoutes } from '../modules/github-app/index.js';
54
56
  import { oidcRedirectUri } from '../modules/auth/oidc-auth-provider.js';
55
57
  import { repositoryConnectionCheck } from '../modules/settings/connection-check.js';
56
58
  import { rootFolderListerFor } from '../modules/settings/git-root-folders.js';
@@ -70,6 +72,7 @@ import { publicConfig } from './public-config.js';
70
72
  import { createReadiness } from './readiness.js';
71
73
  import { createAgentInstructionsRoutes } from '../modules/agent-instructions/index.js';
72
74
  import type { CoreServices } from './create-core-services.js';
75
+ import { createWriteAccessRoutes, createWriteGateMiddleware } from '../modules/write-access/write-access.js';
73
76
 
74
77
  type ExpressApp = ReturnType<typeof express>;
75
78
 
@@ -83,8 +86,8 @@ export interface ToolSurfaceCtx {
83
86
  router: Router;
84
87
  toolAuth: RequestHandler;
85
88
  toolHandler: CoreServices['toolHandlerFactory'];
86
- /** Shared ontology-session boundary gate config (file tools + graph tools). */
87
- sessionOntologyGate: SessionOntologyGate;
89
+ /** Shared agent-access gate config (file tools + graph tools). */
90
+ agentAccessGate: AgentAccessGate;
88
91
  core: CoreServices;
89
92
  }
90
93
 
@@ -198,6 +201,14 @@ export async function createCoreServer(
198
201
  // is mounted again below, once the body is parsed and once the caller is
199
202
  // known (see the two mounts under `/api/workspace/:id`).
200
203
  app.use('/api/workspace/:id', createGitInternalsRouteGuard(core.workspaceService));
204
+ // The read-only gate, ahead of every route — core's, the tool surface's
205
+ // and an overlay's alike — so a read-only deployment refuses a change
206
+ // wherever it would enter. It lets through what signs people in, manages
207
+ // accounts and configures the deployment (see `ALWAYS_WRITABLE`); write
208
+ // tools are judged by the tool layer. A no-op unless a host fills
209
+ // `ports.writeAccess`. Ahead of the body parser too: a refused write is
210
+ // refused with its own 403, never parsed first (and answered 400 or 413).
211
+ app.use(createWriteGateMiddleware(core.writeAccess));
201
212
  app.use((req, res, next) => {
202
213
  if (jsonExemptPaths.has(req.path) || isSyncRawBodyPath(req.path)) return next();
203
214
  return globalJson(req, res, next);
@@ -418,24 +429,23 @@ export async function createCoreServer(
418
429
  },
419
430
  }),
420
431
  );
421
- // Shared ontology-session boundary gate config, consumed by every tool
422
- // surface that touches the KB (file tools + graph tools). The gate's
423
- // blocking decision runs through the workflow hooks: core registers none
424
- // (tracking only); the enterprise root registers the ontology block on
425
- // `workflowService.hooks` before this server is built.
426
- const sessionOntologyGate = {
427
- service: core.sessionOntologyService,
428
- enabled: core.config.ontologySessionBlock,
429
- kb: core.kb,
432
+ // Shared agent-access gate config, consumed by every tool surface that
433
+ // touches the KB (file tools + graph tools). The gate decides nothing
434
+ // itself: it calls the read and write hooks a deployment registered on
435
+ // `workflowService.hooks`, and carries the notes that deployment wants
436
+ // agents to read about the gated tools. Core registers neither, so a
437
+ // core-only deployment refuses nothing and says nothing extra.
438
+ const agentAccessGate: AgentAccessGate = {
430
439
  recoveryBotEmail: RECOVERY_BOT_EMAIL,
431
440
  hooks: core.workflowService.hooks,
441
+ notes: new ToolDescriptionNotes(),
432
442
  };
433
443
  // A skill's `allowed-tools`, checked against what the caller can see — on
434
444
  // every save surface (agent write tools, the app's PUT /file) and on
435
445
  // `get_skill`. Warnings only; it never refuses a save.
436
446
  const allowedToolsChecker = new AllowedToolsChecker(core.toolRegistry, core.toolManualService, core.kb);
437
447
  registerWorkflowTools(core.toolRegistry, toolsRouter, ta, th, core.kb);
438
- registerWorkspaceTools(core.toolRegistry, toolsRouter, ta, th, core.spillStore, core.docExtractService, core.accessControl, core.kb, sessionOntologyGate, core.routineWritePolicy, core.sessionSink, allowedToolsChecker, core.changeGate);
448
+ registerWorkspaceTools(core.toolRegistry, toolsRouter, ta, th, core.spillStore, core.docExtractService, core.accessControl, core.kb, agentAccessGate, core.routineWritePolicy, core.sessionSink, allowedToolsChecker, core.changeGate);
439
449
  registerSkillsTools(core.toolRegistry, toolsRouter, ta, th, core.skillService, allowedToolsChecker);
440
450
  // Definitions only: the endpoints they describe are the app's own plugin
441
451
  // creation routes, mounted below behind the key-or-session gate.
@@ -453,7 +463,7 @@ export async function createCoreServer(
453
463
  router: toolsRouter,
454
464
  toolAuth: ta,
455
465
  toolHandler: th,
456
- sessionOntologyGate,
466
+ agentAccessGate,
457
467
  core,
458
468
  });
459
469
  toolsRouter.use(createManualRoutes(
@@ -544,6 +554,7 @@ export async function createCoreServer(
544
554
  // request also gets the resolved form judged — a link in the repository that
545
555
  // points into the git folder — before any read gate or lock.
546
556
  app.use('/api/workspace/:id', core.authMiddleware, createGitInternalsRouteGuard(core.workspaceService));
557
+ app.use('/api', core.authMiddleware, createWriteAccessRoutes(core.writeAccess));
547
558
  app.use('/api', core.authMiddleware, createWorkspaceRoutes(
548
559
  core.workspaceService,
549
560
  core.authService,
@@ -591,6 +602,18 @@ export async function createCoreServer(
591
602
  core.kb,
592
603
  [core.config.adminEmail],
593
604
  ));
605
+ // Asking for Can edit or Owner on an item from its Manage access dialog, and
606
+ // the editors' Accept / Decline. Mounted beside the access routes it shares
607
+ // its gate with: the request is refused by the same canWrite it would
608
+ // otherwise have to wait for.
609
+ app.use('/api', core.authMiddleware, createAccessRequestRoutes({
610
+ accessControl: core.accessControl,
611
+ workspaceService: core.workspaceService,
612
+ authService: core.authService,
613
+ workflow: core.workflowService,
614
+ lifecycle: core.joinRequestsService,
615
+ kb: core.kb,
616
+ }));
594
617
  app.use(
595
618
  '/api',
596
619
  core.authMiddleware,
@@ -700,8 +723,39 @@ export async function createCoreServer(
700
723
  repositoryConnectionCheck(core.gitRunner),
701
724
  rootFolderListerFor(core.gitRunner),
702
725
  oidcRedirectUri(core.config.publicBackendUrl),
726
+ // The two sign-in checks keep their defaults.
727
+ undefined,
728
+ undefined,
729
+ {
730
+ source: core.repositorySource,
731
+ ensureManaged: (branch) => core.managedRepository.ensure(core.gitRunner, branch),
732
+ githubApp: core.githubApp,
733
+ },
734
+ // What a move to another repository has to decide about, and what it
735
+ // runs under. Bound here rather than handed the whole workflow service:
736
+ // the setup routes ask two questions, hold the commit worker for the
737
+ // move, and know nothing else about a request or a commit.
738
+ {
739
+ countOpen: () => core.workflowService.countOpenChangeRequests(),
740
+ closeAsRepositoryReplaced: () => core.workflowService.closeOpenChangeRequestsAsRepositoryReplaced(),
741
+ whileCommitsHeld: (work) => core.whileCommitsHeld(work),
742
+ },
703
743
  ),
704
744
  );
745
+ // Connecting a repository on GitHub through a GitHub App: the round trips
746
+ // to GitHub the setup screen starts, behind the same sign-in.
747
+ app.use(
748
+ '/api',
749
+ core.authMiddleware,
750
+ createGitHubAppRoutes({
751
+ settings: core.settings,
752
+ connection: core.githubApp,
753
+ adminAccess: core.adminAccess,
754
+ publicBackendUrl: core.config.publicBackendUrl,
755
+ publicFrontendUrl: core.config.publicFrontendUrl,
756
+ isComplete: () => isComplete(core.settings, core.kb, core.repositorySource),
757
+ }),
758
+ );
705
759
  const toolPageUser = async (userId: string): Promise<AuthUser | undefined> => {
706
760
  const u = await core.authService.getUserById(userId);
707
761
  return u ? ({ id: u.id, email: u.email, name: u.name } as AuthUser) : undefined;
@@ -12,6 +12,7 @@ import { sql } from 'drizzle-orm';
12
12
  import { DEFAULT_DB_SCHEMA, assertSearchPath } from '../modules/database/connection.js';
13
13
  import { DEFAULT_SECRETS_SCOPE } from '../modules/secrets-vault/secrets-variable-loader.js';
14
14
  import { KbContext } from '../shared/kb-context.js';
15
+ import { branchForWorkspaceId } from '../shared/workspace-id.js';
15
16
  import { getDb, type Database } from '../modules/database/connection.js';
16
17
  import { runCoreMigrations } from '../modules/database/migrate.js';
17
18
  import { coreMigrationsDir } from '../assets.js';
@@ -30,9 +31,12 @@ import { KbSyncService } from '../modules/kb-sync/kb-sync.service.js';
30
31
  import { NodeFs } from '../modules/kb-fs/node-fs.js';
31
32
  import { assertKbDirNameFree } from '../modules/kb-fs/repo-path.js';
32
33
  import type { IFsProbe, ITreeWalker } from '../shared/fs.contract.js';
33
- import { gitCredentials, type IGitRunner } from '../shared/git.contract.js';
34
+ import type { IGitRunner } from '../shared/git.contract.js';
35
+ import { ManagedRepository, MANAGED_DEFAULT_BRANCH } from '../modules/settings/managed-repository.js';
36
+ import { RepositorySource } from '../modules/settings/repository-source.js';
37
+ import { GitHubAppConnection } from '../modules/github-app/index.js';
34
38
  import { AdvisoryLease, AdvisoryLock } from '../modules/database/advisory-lock.js';
35
- import { holdCommitWorkerLease, withStartupTask, type LeaseLoopHandle } from './lifecycle.js';
39
+ import { holdCommitWorkerLease, holdable, withStartupTask, type LeaseLoopHandle } from './lifecycle.js';
36
40
 
37
41
  /** The hosted MCP endpoint at a deployment address, with any userinfo stripped. */
38
42
  function mcpEndpointUrl(publicBackendUrl: string): string {
@@ -107,7 +111,6 @@ import { WorkflowEventBus } from '../modules/workflow/event-bus.js';
107
111
  import { FileChangeNotifier } from '../modules/kb-fs/file-change-notifier.js';
108
112
  import { WorkflowService } from '../modules/workflow/workflow.service.js';
109
113
  import { WorkflowHooks } from '../modules/workflow/workflow-hooks.js';
110
- import { SessionOntologyService } from '../modules/workflow/session-ontology.service.js';
111
114
  import { PendingCommitsService } from '../modules/workflow/pending-commits.service.js';
112
115
  import {
113
116
  PendingCommitsWorker,
@@ -139,6 +142,7 @@ import { BevelOAuthProvider } from '../modules/mcp/oauth/bevel-oauth-provider.js
139
142
  import { getOAuthProtectedResourceMetadataUrl } from '@modelcontextprotocol/sdk/server/auth/router.js';
140
143
  import { ToolRegistry } from '../modules/tool-registry/tool-registry.js';
141
144
  import { createToolContextResolver } from '../modules/tool-helpers/tool-context.js';
145
+ import { alwaysWritable, type IWriteAccess } from '../modules/write-access/write-access.js';
142
146
  import { createToolHandlerFactory } from '../modules/tool-helpers/tool-handler.js';
143
147
  import { TokenCrypto } from '../shared/token-crypto.js';
144
148
  import { UpdateCheckService } from '../modules/update-check/update-check.service.js';
@@ -179,6 +183,12 @@ export interface CoreServices {
179
183
  * lease to the replacement.
180
184
  */
181
185
  commitWorker: LeaseLoopHandle;
186
+ /**
187
+ * Run something with no queued commit being written: the commit worker is
188
+ * stopped for its duration and started again afterwards, the lease kept.
189
+ * What a move to another repository runs the startup phase under.
190
+ */
191
+ whileCommitsHeld<T>(work: () => Promise<T>): Promise<T>;
182
192
  workspaceService: WorkspaceService;
183
193
  /**
184
194
  * The KB startup phase (see `startup/on-server-start.ts`): run at the
@@ -186,6 +196,15 @@ export interface CoreServices {
186
196
  * first-time setup completion — and never again while the process serves.
187
197
  */
188
198
  kbStartupRunner: KbStartupRunner;
199
+ /**
200
+ * Where the knowledge base's repository is and what git presents to it,
201
+ * whichever way the deployment was given one (see `RepositorySource`).
202
+ */
203
+ repositorySource: RepositorySource;
204
+ /** The repository the deployment keeps for itself, when that is the way it chose. */
205
+ managedRepository: ManagedRepository;
206
+ /** The connection to GitHub through a GitHub App, when that is the way it chose. */
207
+ githubApp: GitHubAppConnection;
189
208
  /**
190
209
  * The startup phase's retry when the boot survived an unreachable remote
191
210
  * — set by `startCore`, stopped by `stopCore` (see `core/lifecycle.ts`);
@@ -206,7 +225,6 @@ export interface CoreServices {
206
225
  creatorAccess: CreatorAccessService;
207
226
  /** Read-before-write, for the surfaces that ask ahead of the lock (see `access-model/change-gate.ts`). */
208
227
  changeGate: ChangeReadGate;
209
- sessionOntologyService: SessionOntologyService;
210
228
  routineWritePolicy: RoutineWritePolicyService;
211
229
  skillService: SkillService;
212
230
  pendingSkillsService: PendingSkillsService;
@@ -315,6 +333,8 @@ export interface CoreServices {
315
333
  toolAuthMiddleware: ReturnType<typeof createToolAuthMiddleware>;
316
334
  manualAuthMiddleware: ReturnType<typeof createManualAuthMiddleware>;
317
335
  toolHandlerFactory: ReturnType<typeof createToolHandlerFactory>;
336
+ /** Whether the deployment may be changed right now — `ports.writeAccess`, or always. */
337
+ writeAccess: IWriteAccess;
318
338
  // ── Server-time seams (enterprise overwrites after construction) ────────
319
339
  /** `start_session` backing — core default {@link UuidSessionSink}. */
320
340
  sessionSink: ISessionSink;
@@ -434,21 +454,123 @@ export async function createCoreServices(
434
454
  // settings catalogue knows `GIT_TOKEN` alone. Handed to the runner, which
435
455
  // puts the token into each child's environment — never into this
436
456
  // process's, which several knowledge bases may share.
437
- const gitCredentialsInEffect = gitCredentials(
438
- () => settings.resolve('gitUsername') || config.gitUsername,
439
- () => settings.resolve('gitToken') || config.gitToken || null,
440
- );
457
+ //
458
+ // Both come from the repository source, which is the one place that knows
459
+ // a deployment can be given its repository in more than one way; what
460
+ // comes back is the address and the credential, as it always was.
461
+ const managedRepository = new ManagedRepository(path.join(config.backupsRoot, 'managed-repository'));
462
+ const githubApp = new GitHubAppConnection({
463
+ read: (key) => settings.resolve(key),
464
+ sourceOf: (key) => settings.sourceOf(key),
465
+ });
466
+ const repositorySource = new RepositorySource({
467
+ read: (key) => settings.resolve(key),
468
+ fallback: { username: config.gitUsername, token: config.gitToken },
469
+ managed: managedRepository,
470
+ githubApp,
471
+ });
472
+ const gitCredentialsInEffect = repositorySource.credentials;
441
473
  // How git is run, for every module that runs it: one environment, one buffer
442
474
  // ceiling, one error shape, and — the reason it exists — one deadline, so a
443
475
  // git that never returns cannot hold a workspace (and with it the commit
444
476
  // queue) open indefinitely. See `shared/git.contract.ts`.
445
477
  const gitRunner = new NodeGitRunner(config.gitTimeoutMs, gitCredentialsInEffect);
478
+ // A deployment that keeps its own repository has it before anything asks
479
+ // for it: one that chose the mode through the environment never passes
480
+ // through the save that would have created it, and the startup phase's
481
+ // first question would be asked of a folder that is not there. Left to
482
+ // that phase to report if it cannot be made: its failures are classified
483
+ // and shown on the setup screen, where a throw here would take the screen
484
+ // down with the boot.
485
+ if (repositorySource.mode() === 'managed') {
486
+ await managedRepository
487
+ .ensure(gitRunner, settings.resolve('defaultBranch') || MANAGED_DEFAULT_BRANCH)
488
+ .catch((err: unknown) => logger('setup').error('the managed repository could not be created:', { err }));
489
+ }
490
+ // One reached through a GitHub App has a token in hand before anything
491
+ // reads it: a working copy's credential helper is stamped according to
492
+ // whether there is a token, by code that cannot wait for one. A failure is
493
+ // the startup phase's to report, for the same reason as above.
494
+ if (repositorySource.mode() === 'github-app') {
495
+ await githubApp.prepare().catch(() => undefined);
496
+ }
497
+ /**
498
+ * Where a working copy of a replaced repository is kept, for BOTH the places
499
+ * that find one: the KB startup phase sweeping the workspaces root, and the
500
+ * workspace service refusing to adopt one on a branch open. One variable,
501
+ * passed to both, because they move the same clones for the same reason and
502
+ * an admin looking for their work must have one folder to look in — the two
503
+ * drifted apart once already, and the copy the workspace service set aside
504
+ * landed in the image's own filesystem and was gone at the next recreate.
505
+ *
506
+ * Under the backups root: a persistent volume of its own, and one nothing
507
+ * sweeps (see the runner's `reconcileClonesWithConfiguredRepository`).
508
+ */
509
+ const replacedWorkingCopiesRoot = path.join(config.backupsRoot, 'replaced-working-copies');
510
+ /**
511
+ * What must leave with a working copy that is set aside, whoever sets it
512
+ * aside: the startup phase, or the workspace service on a branch open.
513
+ *
514
+ * Its queue: whatever still waits to be committed into that copy was
515
+ * written against the repository that was left, and the path it names is
516
+ * about to hold a fresh clone of another one. Held for a person, on every
517
+ * branch, never written and never deleted.
518
+ *
519
+ * And the locks on its branch: each is a claim on a file of the copy that
520
+ * is going. Left standing, a holder still connected keeps one alive with
521
+ * heartbeats, and the same path in the new repository refuses everybody
522
+ * else. Dropped without enqueueing anything, as when change requests are
523
+ * closed as "repository replaced".
524
+ *
525
+ * Throws when either cannot be done, and the caller then leaves the copy
526
+ * where it is. Reaches FORWARD to both services, like `gitService` below:
527
+ * it is only ever called long after this function has returned them.
528
+ */
529
+ const releaseWorkOnSetAsideCopy = async (workspaceId: string): Promise<void> => {
530
+ const held = await pendingCommitsService.markNeedsAttentionInWorkspace(
531
+ workspaceId,
532
+ 'The knowledge-base repository was replaced while this commit was still queued, so it was never ' +
533
+ 'written. The bytes are kept here: the working copy it was meant for belongs to the previous repository.',
534
+ );
535
+ if (held > 0) {
536
+ logger('kb-startup').warn(
537
+ `${held} queued commit(s) for the working copy "${workspaceId}" need attention: the repository was ` +
538
+ 'replaced before they landed, so they were not written to the new one.',
539
+ );
540
+ }
541
+ await fileLockService.releaseAllOnBranch(branchForWorkspaceId(workspaceId));
542
+ };
446
543
  const workspaceService = new WorkspaceService(
447
544
  config.workspacesRoot,
448
- () => settings.resolve('kbRepoUrl'),
545
+ () => repositorySource.url(),
449
546
  kb,
450
547
  disk,
451
548
  gitRunner,
549
+ replacedWorkingCopiesRoot,
550
+ // A branch opened onto a clone of another repository sets it aside on
551
+ // the spot, with nobody having stopped the commit worker for it. So it
552
+ // is held here, for the one move, and the copy's work leaves first and
553
+ // is looked for again once the copy has gone: a release that queued its
554
+ // bytes in between was writing to the copy that left.
555
+ //
556
+ // Only the first look may refuse the branch: the copy has not moved yet.
557
+ // The second runs after the move, so its failure is tried once more and
558
+ // then logged, as the startup phase logs the same step. Refusing then
559
+ // would report a move that did happen as one that did not.
560
+ (workspaceId, move) =>
561
+ leased.whileHeld(async () => {
562
+ await releaseWorkOnSetAsideCopy(workspaceId);
563
+ await move();
564
+ await releaseWorkOnSetAsideCopy(workspaceId)
565
+ .catch(() => releaseWorkOnSetAsideCopy(workspaceId))
566
+ .catch((err: unknown) => {
567
+ logger('workspace').error(
568
+ `the working copy "${workspaceId}" was set aside, but what was queued or locked on it meanwhile ` +
569
+ 'could not be looked for again:',
570
+ { err },
571
+ );
572
+ });
573
+ }),
452
574
  );
453
575
  // The KB startup phase: every seeding, scaffolding and migration concern,
454
576
  // run through one runner at the deployment's quiet moments (boot + setup
@@ -484,8 +606,11 @@ export async function createCoreServices(
484
606
  // Getters, not values: the remote and the branch model can arrive from the
485
607
  // setup screen after this object exists, and the setup-completion run is
486
608
  // the first thing that needs them.
487
- kbRepoUrl: () => settings.resolve('kbRepoUrl'),
609
+ kbRepoUrl: () => repositorySource.url(),
488
610
  workspacesRoot: config.workspacesRoot,
611
+ // The same folder the workspace service sets aside into — see
612
+ // `replacedWorkingCopiesRoot` above for why the two must agree.
613
+ setAsideRoot: replacedWorkingCopiesRoot,
489
614
  kbDirName,
490
615
  templateDir: config.kbTemplateDir,
491
616
  defaultBranch: () => kb.defaultBranch,
@@ -496,6 +621,33 @@ export async function createCoreServices(
496
621
  steps: kbStartupSteps,
497
622
  buildSeedTree: buildSeedTree(disk, config.kbTemplateDir, extraDirs, [config.adminEmail], kb),
498
623
  gitRunner,
624
+ // The work on a working copy that is about to be set aside leaves first:
625
+ // its queued commits and the locks on its branch (see
626
+ // `releaseWorkOnSetAsideCopy`). A failure here stops the phase before
627
+ // the copy moves: a replacement cloned over an untouched queue is how
628
+ // those bytes would land in the wrong repository.
629
+ beforeCloneSetAside: releaseWorkOnSetAsideCopy,
630
+ // And once it is set aside it must leave the workspace service's cache:
631
+ // on the SAVE that moves the deployment the process is already running,
632
+ // and a cached path to a directory that is gone is how the next reader
633
+ // gets an ENOENT instead of a fresh clone.
634
+ //
635
+ // Then the copy's work is looked for once more. A release that queued
636
+ // its bytes between the first look and the move was writing to the copy
637
+ // that has now gone; nothing can queue against it after this.
638
+ onCloneDiscarded: async (workspaceId) => {
639
+ workspaceService.forgetClone(workspaceId);
640
+ await releaseWorkOnSetAsideCopy(workspaceId);
641
+ },
642
+ // And the replacement it cloned in its place: a fresh clone holds every
643
+ // ref, so the git layer's per-workspace fetch record is told so. Without
644
+ // it that record still holds the FAILED fetch of the repository that was
645
+ // replaced, and a strict branch listing within its TTL refuses the new
646
+ // clone's refs as unproven. Reaches FORWARD to `gitService`, which is
647
+ // built further down: safe because the runner only ever calls this from
648
+ // `runAll`, which the server builder invokes long after this function has
649
+ // returned every service.
650
+ onCloneCreated: (workspaceId) => gitService.noteWorkspaceFetched(workspaceId),
499
651
  });
500
652
  // Shared, workspace-independent store for oversized `call_tool_chain` results,
501
653
  // read back via `read_file`. Sibling of `workspacesRoot`, never committed.
@@ -527,14 +679,10 @@ export async function createCoreServices(
527
679
  // except that new folder at a root (see `access-model/change-gate.ts`).
528
680
  const changeGate = new ChangeReadGate(workspaceService, accessControl, kb, disk);
529
681
 
530
- // Ontology-session boundary: records each agent run's touched ontologies and
531
- // blocks writes once a run has crossed ontologies. Postgres-backed so the
532
- // boundary survives a restart.
533
- const sessionOntologyService = new SessionOntologyService(db, kb);
534
682
  // Per-run write restriction (by file extension). Shared by the workspace tool
535
683
  // surface (which enforces it) and the routine runner (which sets it for
536
- // dashboard-only `watchlist_check` runs). In-memory: a restriction lives only for
537
- // one run, unlike the Postgres-backed ontology touched-set above.
684
+ // dashboard-only `watchlist_check` runs). In-memory: a restriction lives only
685
+ // for one run.
538
686
  const routineWritePolicy = new RoutineWritePolicyService();
539
687
  // Skills: discovered from the default-branch workspace only (global catalog).
540
688
  const skillService = new SkillService(workspaceService, accessControl, kb, disk);
@@ -585,11 +733,11 @@ export async function createCoreServices(
585
733
  // against each other — a backup-reseed races a concurrent commit otherwise.
586
734
  const workspaceMutex = new WorkspaceMutex();
587
735
  // Workflow lifecycle hooks — the ONE registry this composition shares
588
- // between GitService (advisory commit validation), the session-ontology
589
- // gate (blocking preWrite), and the enterprise composition root, which
590
- // registers module-owned handlers on `workflowService.hooks` right after
591
- // this function returns. Core registers none: no commit-time validation
592
- // (advisory anyway) and no ontology write block.
736
+ // between GitService (advisory commit validation), the agent-access gate
737
+ // (the blocking agentRead / preWrite hooks), and the composition root,
738
+ // which registers module-owned handlers on `workflowService.hooks` right
739
+ // after this function returns. Core registers none: no commit-time
740
+ // validation (advisory anyway) and nothing that refuses an agent call.
593
741
  const workflowHooks = new WorkflowHooks();
594
742
  const gitService = new GitService(
595
743
  workspaceService,
@@ -684,7 +832,7 @@ export async function createCoreServices(
684
832
  eventBus,
685
833
  fileChangeNotifier,
686
834
  // Exposed as `workflowService.hooks` — the SAME instance GitService and
687
- // the session-ontology gate consult, so enterprise registrations against
835
+ // the agent-access gate consult, so a deployment's registrations against
688
836
  // it reach every hook point.
689
837
  workflowHooks,
690
838
  );
@@ -693,7 +841,14 @@ export async function createCoreServices(
693
841
  // (the request's branch vs the default branch), so it holds no state — it
694
842
  // only needs to read files at refs and to close a request whose proposals
695
843
  // have all landed.
696
- const joinRequestsService = new JoinRequestsService(workspaceService, workflowService, kb);
844
+ const joinRequestsService = new JoinRequestsService(
845
+ workspaceService,
846
+ workflowService,
847
+ kb,
848
+ // Read-only: the one question a request asks of the resolver is whether
849
+ // the person it names already holds what it proposes.
850
+ accessControl,
851
+ );
697
852
  // The OTHER half of a join request: the row the subscribe endpoint writes
698
853
  // before it answers, and the branch/clone/commit/push/change-request work
699
854
  // that runs against it afterwards. The row is what lets the click be
@@ -1080,8 +1235,9 @@ export async function createCoreServices(
1080
1235
  creatorAccess,
1081
1236
  loadActiveGroups,
1082
1237
  });
1083
- const toolHandlerFactory = createToolHandlerFactory(resolveToolContext);
1084
- const toolAuthMiddleware = createToolAuthMiddleware(externalApiKeyService, internalTokenService);
1238
+ const writeAccess = ports.writeAccess ?? alwaysWritable;
1239
+ const toolHandlerFactory = createToolHandlerFactory(resolveToolContext, writeAccess);
1240
+ const toolAuthMiddleware = createToolAuthMiddleware(externalApiKeyService, internalTokenService, authService);
1085
1241
  // Read-only manual endpoints accept the above PLUS a browser JWT, so a
1086
1242
  // logged-in user can browse the catalog with their session. Execution routes
1087
1243
  // keep `toolAuthMiddleware` (no JWT), so a session can read but not invoke.
@@ -1133,7 +1289,10 @@ export async function createCoreServices(
1133
1289
  name: recoveryBot.name,
1134
1290
  },
1135
1291
  });
1136
- const leased = withStartupTask(pendingCommitsWorker, reconcileQueue);
1292
+ // Holdable: a move to another repository stops the worker for as long as
1293
+ // working copies are being set aside and cloned again, without giving up
1294
+ // the lease (see `holdable` in `core/lifecycle.ts`).
1295
+ const leased = holdable(withStartupTask(pendingCommitsWorker, reconcileQueue));
1137
1296
 
1138
1297
  // SSO providers. The array REFERENCE is shared with the caller's port — an
1139
1298
  // overlay pushes its own plugins into it after construction (they mount when
@@ -1229,6 +1388,7 @@ export async function createCoreServices(
1229
1388
  db,
1230
1389
  gitRunner,
1231
1390
  commitWorker,
1391
+ whileCommitsHeld: (work) => leased.whileHeld(work),
1232
1392
  startupRetry: null,
1233
1393
  tenantKey,
1234
1394
  secretsScope,
@@ -1237,6 +1397,9 @@ export async function createCoreServices(
1237
1397
  toolDeleteService,
1238
1398
  workspaceService,
1239
1399
  kbStartupRunner,
1400
+ repositorySource,
1401
+ managedRepository,
1402
+ githubApp,
1240
1403
  settings,
1241
1404
  kb,
1242
1405
  kbDirName,
@@ -1245,7 +1408,6 @@ export async function createCoreServices(
1245
1408
  accessControl,
1246
1409
  creatorAccess,
1247
1410
  changeGate,
1248
- sessionOntologyService,
1249
1411
  routineWritePolicy,
1250
1412
  skillService,
1251
1413
  pendingSkillsService,
@@ -1296,6 +1458,7 @@ export async function createCoreServices(
1296
1458
  toolAuthMiddleware,
1297
1459
  manualAuthMiddleware,
1298
1460
  toolHandlerFactory,
1461
+ writeAccess,
1299
1462
  // Server-time seams — defaults here; the enterprise overlay overwrites
1300
1463
  // (or, for the array, pushes into) these after construction.
1301
1464
  sessionSink: ports.sessionSink ?? new UuidSessionSink(),