@bevel-software/platform-core-backend 0.14.0 → 0.19.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 (1307) hide show
  1. package/THIRD-PARTY-NOTICES.md +8 -8
  2. package/dist/core/catalog-cache-invalidation.d.ts +45 -0
  3. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
  4. package/dist/core/catalog-cache-invalidation.js +76 -0
  5. package/dist/core/catalog-cache-invalidation.js.map +1 -0
  6. package/dist/core/catalog-revision.d.ts +39 -0
  7. package/dist/core/catalog-revision.d.ts.map +1 -0
  8. package/dist/core/catalog-revision.js +94 -0
  9. package/dist/core/catalog-revision.js.map +1 -0
  10. package/dist/core/create-core-server.d.ts +7 -0
  11. package/dist/core/create-core-server.d.ts.map +1 -1
  12. package/dist/core/create-core-server.js +279 -64
  13. package/dist/core/create-core-server.js.map +1 -1
  14. package/dist/core/create-core-services.d.ts +66 -2
  15. package/dist/core/create-core-services.d.ts.map +1 -1
  16. package/dist/core/create-core-services.js +290 -111
  17. package/dist/core/create-core-services.js.map +1 -1
  18. package/dist/core/lifecycle.d.ts +138 -0
  19. package/dist/core/lifecycle.d.ts.map +1 -0
  20. package/dist/core/lifecycle.js +218 -0
  21. package/dist/core/lifecycle.js.map +1 -0
  22. package/dist/core/public-config.d.ts +82 -0
  23. package/dist/core/public-config.d.ts.map +1 -0
  24. package/dist/core/public-config.js +78 -0
  25. package/dist/core/public-config.js.map +1 -0
  26. package/dist/core/readiness.d.ts +86 -0
  27. package/dist/core/readiness.d.ts.map +1 -0
  28. package/dist/core/readiness.js +56 -0
  29. package/dist/core/readiness.js.map +1 -0
  30. package/dist/core-config.d.ts +30 -0
  31. package/dist/core-config.d.ts.map +1 -1
  32. package/dist/core-config.js +64 -2
  33. package/dist/core-config.js.map +1 -1
  34. package/dist/index.d.ts +4 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +6 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/modules/access/access-control.interface.d.ts +231 -15
  39. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.d.ts +83 -12
  41. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  42. package/dist/modules/access/access-control.service.js +815 -212
  43. package/dist/modules/access/access-control.service.js.map +1 -1
  44. package/dist/modules/access/access-mutation.service.d.ts +71 -5
  45. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  46. package/dist/modules/access/access-mutation.service.js +161 -13
  47. package/dist/modules/access/access-mutation.service.js.map +1 -1
  48. package/dist/modules/access/access-view.d.ts +147 -0
  49. package/dist/modules/access/access-view.d.ts.map +1 -0
  50. package/dist/modules/access/access-view.js +211 -0
  51. package/dist/modules/access/access-view.js.map +1 -0
  52. package/dist/modules/access/access.routes.d.ts.map +1 -1
  53. package/dist/modules/access/access.routes.js +312 -84
  54. package/dist/modules/access/access.routes.js.map +1 -1
  55. package/dist/modules/access/admin-locked-commit.d.ts +2 -3
  56. package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
  57. package/dist/modules/access/admin-locked-commit.js +9 -8
  58. package/dist/modules/access/admin-locked-commit.js.map +1 -1
  59. package/dist/modules/access/admin-route-helpers.d.ts.map +1 -1
  60. package/dist/modules/access/admin-route-helpers.js +4 -4
  61. package/dist/modules/access/admin-route-helpers.js.map +1 -1
  62. package/dist/modules/access/change-read-gate.d.ts +53 -0
  63. package/dist/modules/access/change-read-gate.d.ts.map +1 -0
  64. package/dist/modules/access/change-read-gate.js +134 -0
  65. package/dist/modules/access/change-read-gate.js.map +1 -0
  66. package/dist/modules/access/creator-access.d.ts +33 -39
  67. package/dist/modules/access/creator-access.d.ts.map +1 -1
  68. package/dist/modules/access/creator-access.js +92 -119
  69. package/dist/modules/access/creator-access.js.map +1 -1
  70. package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
  71. package/dist/modules/access/directory-sync-bot.js +3 -1
  72. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  73. package/dist/modules/access/groups-admin.service.d.ts +3 -3
  74. package/dist/modules/access/groups-admin.service.js +2 -2
  75. package/dist/modules/access/reference-scan.d.ts +1 -1
  76. package/dist/modules/access/reference-scan.d.ts.map +1 -1
  77. package/dist/modules/access/reference-scan.js +5 -4
  78. package/dist/modules/access/reference-scan.js.map +1 -1
  79. package/dist/modules/access/roles-admin.service.d.ts +72 -13
  80. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  81. package/dist/modules/access/roles-admin.service.js +71 -13
  82. package/dist/modules/access/roles-admin.service.js.map +1 -1
  83. package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
  84. package/dist/modules/access/synced-groups-committer.js +8 -6
  85. package/dist/modules/access/synced-groups-committer.js.map +1 -1
  86. package/dist/modules/access/synced-groups-writer.d.ts.map +1 -1
  87. package/dist/modules/access/synced-groups-writer.js +3 -8
  88. package/dist/modules/access/synced-groups-writer.js.map +1 -1
  89. package/dist/modules/access/user-access-removal.service.d.ts +209 -0
  90. package/dist/modules/access/user-access-removal.service.d.ts.map +1 -0
  91. package/dist/modules/access/user-access-removal.service.js +662 -0
  92. package/dist/modules/access/user-access-removal.service.js.map +1 -0
  93. package/dist/modules/access-model/access-errors.d.ts +32 -1
  94. package/dist/modules/access-model/access-errors.d.ts.map +1 -1
  95. package/dist/modules/access-model/access-errors.js +38 -11
  96. package/dist/modules/access-model/access-errors.js.map +1 -1
  97. package/dist/modules/access-model/access-grammar.d.ts +100 -25
  98. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  99. package/dist/modules/access-model/access-grammar.js +162 -48
  100. package/dist/modules/access-model/access-grammar.js.map +1 -1
  101. package/dist/modules/access-model/access-splice.d.ts.map +1 -1
  102. package/dist/modules/access-model/access-splice.js +18 -18
  103. package/dist/modules/access-model/access-splice.js.map +1 -1
  104. package/dist/modules/access-model/access-template.d.ts +22 -0
  105. package/dist/modules/access-model/access-template.d.ts.map +1 -0
  106. package/dist/modules/access-model/access-template.js +42 -0
  107. package/dist/modules/access-model/access-template.js.map +1 -0
  108. package/dist/modules/access-model/change-gate.d.ts +65 -0
  109. package/dist/modules/access-model/change-gate.d.ts.map +1 -0
  110. package/dist/modules/access-model/change-gate.js +25 -0
  111. package/dist/modules/access-model/change-gate.js.map +1 -0
  112. package/dist/modules/access-model/creator.d.ts +13 -16
  113. package/dist/modules/access-model/creator.d.ts.map +1 -1
  114. package/dist/modules/access-model/creator.js.map +1 -1
  115. package/dist/modules/access-model/frontmatter-lines.d.ts +27 -0
  116. package/dist/modules/access-model/frontmatter-lines.d.ts.map +1 -0
  117. package/dist/modules/access-model/frontmatter-lines.js +74 -0
  118. package/dist/modules/access-model/frontmatter-lines.js.map +1 -0
  119. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  120. package/dist/modules/access-model/group-files.js +4 -1
  121. package/dist/modules/access-model/group-files.js.map +1 -1
  122. package/dist/modules/access-model/plugin-principals.d.ts +51 -0
  123. package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
  124. package/dist/modules/access-model/plugin-principals.js +119 -0
  125. package/dist/modules/access-model/plugin-principals.js.map +1 -0
  126. package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
  127. package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
  128. package/dist/modules/access-model/roles-yaml-guard.js +153 -3
  129. package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
  130. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  131. package/dist/modules/admin/admin-access.service.js +5 -2
  132. package/dist/modules/admin/admin-access.service.js.map +1 -1
  133. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
  134. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
  135. package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
  136. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
  137. package/dist/modules/agent-instructions/compose.d.ts +71 -0
  138. package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
  139. package/dist/modules/agent-instructions/compose.js +234 -0
  140. package/dist/modules/agent-instructions/compose.js.map +1 -0
  141. package/dist/modules/agent-instructions/index.d.ts +4 -0
  142. package/dist/modules/agent-instructions/index.d.ts.map +1 -0
  143. package/dist/modules/agent-instructions/index.js +4 -0
  144. package/dist/modules/agent-instructions/index.js.map +1 -0
  145. package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
  146. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
  147. package/dist/modules/agent-instructions/read-preamble.js +81 -0
  148. package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
  149. package/dist/modules/auth/account-erasure.service.d.ts +30 -4
  150. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
  151. package/dist/modules/auth/account-erasure.service.js +96 -10
  152. package/dist/modules/auth/account-erasure.service.js.map +1 -1
  153. package/dist/modules/auth/account.routes.d.ts +6 -3
  154. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  155. package/dist/modules/auth/account.routes.js +109 -10
  156. package/dist/modules/auth/account.routes.js.map +1 -1
  157. package/dist/modules/auth/auth.routes.d.ts +10 -3
  158. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  159. package/dist/modules/auth/auth.routes.js +9 -4
  160. package/dist/modules/auth/auth.routes.js.map +1 -1
  161. package/dist/modules/auth/auth.service.d.ts +89 -5
  162. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  163. package/dist/modules/auth/auth.service.js +160 -25
  164. package/dist/modules/auth/auth.service.js.map +1 -1
  165. package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
  166. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  167. package/dist/modules/auth/oidc-auth-provider.js +99 -21
  168. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  169. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  170. package/dist/modules/code-mode/code-mode.tool.js +14 -2
  171. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  172. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  173. package/dist/modules/connection-probe/connection-probe.service.js +56 -7
  174. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  175. package/dist/modules/database/advisory-lock.d.ts +103 -0
  176. package/dist/modules/database/advisory-lock.d.ts.map +1 -0
  177. package/dist/modules/database/advisory-lock.js +320 -0
  178. package/dist/modules/database/advisory-lock.js.map +1 -0
  179. package/dist/modules/database/connection.d.ts.map +1 -1
  180. package/dist/modules/database/connection.js +9 -1
  181. package/dist/modules/database/connection.js.map +1 -1
  182. package/dist/modules/database/core-schema.d.ts +672 -0
  183. package/dist/modules/database/core-schema.d.ts.map +1 -1
  184. package/dist/modules/database/core-schema.js +171 -0
  185. package/dist/modules/database/core-schema.js.map +1 -1
  186. package/dist/modules/database/migrate.d.ts.map +1 -1
  187. package/dist/modules/database/migrate.js +26 -6
  188. package/dist/modules/database/migrate.js.map +1 -1
  189. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  190. package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
  191. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  192. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  193. package/dist/modules/diff/diff.routes.js +2 -4
  194. package/dist/modules/diff/diff.routes.js.map +1 -1
  195. package/dist/modules/diff/diff.service.d.ts +4 -1
  196. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  197. package/dist/modules/diff/diff.service.js +79 -81
  198. package/dist/modules/diff/diff.service.js.map +1 -1
  199. package/dist/modules/kb-fs/bevel-ignore.d.ts +35 -0
  200. package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
  201. package/dist/modules/kb-fs/bevel-ignore.js +71 -0
  202. package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
  203. package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
  204. package/dist/modules/kb-fs/file-change-notifier.js +3 -1
  205. package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
  206. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +37 -0
  207. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
  208. package/dist/modules/kb-fs/git-guarded-filesystem.js +104 -0
  209. package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
  210. package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
  211. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  212. package/dist/modules/kb-fs/locking-filesystem.js +353 -76
  213. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  214. package/dist/modules/kb-fs/node-fs.d.ts +23 -0
  215. package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
  216. package/dist/modules/kb-fs/node-fs.js +158 -0
  217. package/dist/modules/kb-fs/node-fs.js.map +1 -0
  218. package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
  219. package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
  220. package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
  221. package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
  222. package/dist/modules/kb-fs/repo-path.d.ts +100 -3
  223. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  224. package/dist/modules/kb-fs/repo-path.js +195 -3
  225. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  226. package/dist/modules/kb-sync/index.d.ts +6 -0
  227. package/dist/modules/kb-sync/index.d.ts.map +1 -0
  228. package/dist/modules/kb-sync/index.js +5 -0
  229. package/dist/modules/kb-sync/index.js.map +1 -0
  230. package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
  231. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
  232. package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
  233. package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
  234. package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
  235. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
  236. package/dist/modules/kb-sync/kb-sync.routes.js +137 -0
  237. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
  238. package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
  239. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
  240. package/dist/modules/kb-sync/kb-sync.service.js +185 -0
  241. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
  242. package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
  243. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
  244. package/dist/modules/kb-sync/sync-auth.js +62 -0
  245. package/dist/modules/kb-sync/sync-auth.js.map +1 -0
  246. package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
  247. package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
  248. package/dist/modules/kb-sync/sync-payload.js +105 -0
  249. package/dist/modules/kb-sync/sync-payload.js.map +1 -0
  250. package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
  251. package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
  252. package/dist/modules/marketplace/git-http.routes.js +192 -0
  253. package/dist/modules/marketplace/git-http.routes.js.map +1 -0
  254. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +33 -0
  255. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
  256. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +88 -0
  257. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
  258. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
  259. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
  260. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
  261. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
  262. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +126 -0
  263. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
  264. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +192 -0
  265. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
  266. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
  267. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
  268. package/dist/modules/marketplace/github-facade/github-facade.routes.js +252 -0
  269. package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
  270. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +121 -0
  271. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
  272. package/dist/modules/marketplace/github-facade/github-facade.service.js +213 -0
  273. package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
  274. package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
  275. package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
  276. package/dist/modules/marketplace/github-facade/index.js +6 -0
  277. package/dist/modules/marketplace/github-facade/index.js.map +1 -0
  278. package/dist/modules/marketplace/index.d.ts +4 -0
  279. package/dist/modules/marketplace/index.d.ts.map +1 -0
  280. package/dist/modules/marketplace/index.js +4 -0
  281. package/dist/modules/marketplace/index.js.map +1 -0
  282. package/dist/modules/marketplace/marketplace-repo.service.d.ts +135 -0
  283. package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
  284. package/dist/modules/marketplace/marketplace-repo.service.js +318 -0
  285. package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
  286. package/dist/modules/mcp/downstream-pool.d.ts +107 -0
  287. package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
  288. package/dist/modules/mcp/downstream-pool.js +221 -0
  289. package/dist/modules/mcp/downstream-pool.js.map +1 -0
  290. package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
  291. package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
  292. package/dist/modules/mcp/downstream-token-refresh.js +166 -0
  293. package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
  294. package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
  295. package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
  296. package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
  297. package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
  298. package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
  299. package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
  300. package/dist/modules/mcp/first-call-probe.cli.js +62 -0
  301. package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
  302. package/dist/modules/mcp/first-call-probe.d.ts +165 -0
  303. package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
  304. package/dist/modules/mcp/first-call-probe.js +298 -0
  305. package/dist/modules/mcp/first-call-probe.js.map +1 -0
  306. package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
  307. package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
  308. package/dist/modules/mcp/manual-failure-memo.js +79 -23
  309. package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
  310. package/dist/modules/mcp/mcp-auth.middleware.d.ts +9 -5
  311. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  312. package/dist/modules/mcp/mcp-auth.middleware.js +24 -16
  313. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  314. package/dist/modules/mcp/mcp.routes.d.ts +4 -4
  315. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  316. package/dist/modules/mcp/mcp.routes.js +83 -179
  317. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  318. package/dist/modules/mcp/mcp.service.d.ts +218 -61
  319. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  320. package/dist/modules/mcp/mcp.service.js +713 -159
  321. package/dist/modules/mcp/mcp.service.js.map +1 -1
  322. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  323. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +8 -2
  324. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  325. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
  326. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  327. package/dist/modules/mcp/oauth/oauth-consent.routes.js +16 -2
  328. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  329. package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
  330. package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
  331. package/dist/modules/mcp/oauth/oauth-state.js +3 -1
  332. package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
  333. package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
  334. package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
  335. package/dist/modules/mcp/surface-log-throttle.js +94 -0
  336. package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
  337. package/dist/modules/plugins/compile/compile-marketplace.d.ts +104 -0
  338. package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
  339. package/dist/modules/plugins/compile/compile-marketplace.js +323 -0
  340. package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
  341. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +73 -0
  342. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
  343. package/dist/modules/plugins/compile/marketplace-compiler.service.js +135 -0
  344. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
  345. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
  346. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
  347. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +158 -0
  348. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
  349. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +65 -0
  350. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
  351. package/dist/modules/plugins/discovery/bundle-dialect/registry.js +204 -0
  352. package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
  353. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +39 -0
  354. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
  355. package/dist/modules/plugins/discovery/kb-plugin-source.js +110 -0
  356. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
  357. package/dist/modules/plugins/discovery/native.source.d.ts +16 -0
  358. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
  359. package/dist/modules/plugins/discovery/native.source.js +120 -0
  360. package/dist/modules/plugins/discovery/native.source.js.map +1 -0
  361. package/dist/modules/plugins/discovery/plugin-source.d.ts +110 -0
  362. package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
  363. package/dist/modules/plugins/discovery/plugin-source.js +2 -0
  364. package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
  365. package/dist/modules/plugins/index.d.ts +15 -3
  366. package/dist/modules/plugins/index.d.ts.map +1 -1
  367. package/dist/modules/plugins/index.js +13 -2
  368. package/dist/modules/plugins/index.js.map +1 -1
  369. package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
  370. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
  371. package/dist/modules/plugins/join-request-jobs.service.js +602 -0
  372. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
  373. package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
  374. package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
  375. package/dist/modules/plugins/join-request-records.store.js +179 -0
  376. package/dist/modules/plugins/join-request-records.store.js.map +1 -0
  377. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  378. package/dist/modules/plugins/join-requests.service.js +4 -2
  379. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  380. package/dist/modules/plugins/plugin-links.d.ts +69 -0
  381. package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
  382. package/dist/modules/plugins/plugin-links.js +163 -0
  383. package/dist/modules/plugins/plugin-links.js.map +1 -0
  384. package/dist/modules/plugins/plugin-links.service.d.ts +111 -0
  385. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
  386. package/dist/modules/plugins/plugin-links.service.js +295 -0
  387. package/dist/modules/plugins/plugin-links.service.js.map +1 -0
  388. package/dist/modules/plugins/plugin-provision.service.d.ts +129 -21
  389. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  390. package/dist/modules/plugins/plugin-provision.service.js +320 -75
  391. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  392. package/dist/modules/plugins/plugin-rename.service.d.ts +71 -0
  393. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
  394. package/dist/modules/plugins/plugin-rename.service.js +254 -0
  395. package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
  396. package/dist/modules/plugins/plugins.contract.d.ts +78 -4
  397. package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
  398. package/dist/modules/plugins/plugins.routes.d.ts +44 -5
  399. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  400. package/dist/modules/plugins/plugins.routes.js +297 -115
  401. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  402. package/dist/modules/plugins/plugins.service.d.ts +82 -3
  403. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  404. package/dist/modules/plugins/plugins.service.js +178 -48
  405. package/dist/modules/plugins/plugins.service.js.map +1 -1
  406. package/dist/modules/plugins/plugins.tools.d.ts +25 -0
  407. package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
  408. package/dist/modules/plugins/plugins.tools.js +82 -0
  409. package/dist/modules/plugins/plugins.tools.js.map +1 -0
  410. package/dist/modules/plugins/teams.routes.d.ts +42 -0
  411. package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
  412. package/dist/modules/plugins/teams.routes.js +79 -0
  413. package/dist/modules/plugins/teams.routes.js.map +1 -0
  414. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
  415. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  416. package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
  417. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  418. package/dist/modules/secrets-vault/index.d.ts +1 -1
  419. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  420. package/dist/modules/secrets-vault/index.js.map +1 -1
  421. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
  422. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
  423. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
  424. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  425. package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
  426. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  427. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
  428. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  429. package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
  430. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  431. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  432. package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
  433. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  434. package/dist/modules/settings/connection-check.d.ts +118 -0
  435. package/dist/modules/settings/connection-check.d.ts.map +1 -0
  436. package/dist/modules/settings/connection-check.js +287 -0
  437. package/dist/modules/settings/connection-check.js.map +1 -0
  438. package/dist/modules/settings/deployment-settings.service.d.ts +157 -2
  439. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  440. package/dist/modules/settings/deployment-settings.service.js +412 -35
  441. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  442. package/dist/modules/settings/git-root-folders.d.ts +54 -0
  443. package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
  444. package/dist/modules/settings/git-root-folders.js +130 -0
  445. package/dist/modules/settings/git-root-folders.js.map +1 -0
  446. package/dist/modules/settings/oidc-check.d.ts +85 -0
  447. package/dist/modules/settings/oidc-check.d.ts.map +1 -0
  448. package/dist/modules/settings/oidc-check.js +172 -0
  449. package/dist/modules/settings/oidc-check.js.map +1 -0
  450. package/dist/modules/settings/setup.routes.d.ts +57 -1
  451. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  452. package/dist/modules/settings/setup.routes.js +408 -108
  453. package/dist/modules/settings/setup.routes.js.map +1 -1
  454. package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
  455. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
  456. package/dist/modules/skills/allowed-tools-check.js +260 -0
  457. package/dist/modules/skills/allowed-tools-check.js.map +1 -0
  458. package/dist/modules/skills/index.d.ts +4 -1
  459. package/dist/modules/skills/index.d.ts.map +1 -1
  460. package/dist/modules/skills/index.js +2 -0
  461. package/dist/modules/skills/index.js.map +1 -1
  462. package/dist/modules/skills/pending-skills.service.d.ts +6 -11
  463. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  464. package/dist/modules/skills/pending-skills.service.js +24 -71
  465. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  466. package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
  467. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
  468. package/dist/modules/skills/skill-access-requests.routes.js +171 -0
  469. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
  470. package/dist/modules/skills/skills.contract.d.ts +67 -2
  471. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  472. package/dist/modules/skills/skills.routes.d.ts +35 -3
  473. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  474. package/dist/modules/skills/skills.routes.js +57 -4
  475. package/dist/modules/skills/skills.routes.js.map +1 -1
  476. package/dist/modules/skills/skills.service.d.ts +31 -3
  477. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  478. package/dist/modules/skills/skills.service.js +236 -65
  479. package/dist/modules/skills/skills.service.js.map +1 -1
  480. package/dist/modules/skills/skills.tools.d.ts +4 -1
  481. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  482. package/dist/modules/skills/skills.tools.js +41 -6
  483. package/dist/modules/skills/skills.tools.js.map +1 -1
  484. package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
  485. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
  486. package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
  487. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
  488. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
  489. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
  490. package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
  491. package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
  492. package/dist/modules/tool-auth/external-api-key.interface.d.ts +66 -1
  493. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  494. package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
  495. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  496. package/dist/modules/tool-auth/external-api-key.service.d.ts +23 -4
  497. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  498. package/dist/modules/tool-auth/external-api-key.service.js +79 -19
  499. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  500. package/dist/modules/tool-auth/internal-token.service.d.ts +5 -2
  501. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  502. package/dist/modules/tool-auth/internal-token.service.js +5 -2
  503. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  504. package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
  505. package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
  506. package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
  507. package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
  508. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +7 -1
  509. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  510. package/dist/modules/tool-auth/tool-auth.middleware.js +8 -5
  511. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  512. package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
  513. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  514. package/dist/modules/tool-helpers/tool-context.js +28 -6
  515. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  516. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  517. package/dist/modules/tool-helpers/tool-handler.js +22 -5
  518. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  519. package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
  520. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  521. package/dist/modules/tool-helpers/tool.contract.js +7 -1
  522. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  523. package/dist/modules/tool-manuals/index.d.ts +2 -1
  524. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  525. package/dist/modules/tool-manuals/index.js +1 -0
  526. package/dist/modules/tool-manuals/index.js.map +1 -1
  527. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
  528. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  529. package/dist/modules/tool-manuals/mcp-json-discovery.js +136 -96
  530. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  531. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
  532. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  533. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
  534. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  535. package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
  536. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
  537. package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
  538. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
  539. package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
  540. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
  541. package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
  542. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
  543. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
  544. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  545. package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
  546. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
  547. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
  548. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  549. package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
  550. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  551. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +121 -2
  552. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  553. package/dist/modules/tool-manuals/tool-manuals.service.js +415 -92
  554. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  555. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  556. package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
  557. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  558. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  559. package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
  560. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  561. package/dist/modules/workflow/event-bus.d.ts +10 -4
  562. package/dist/modules/workflow/event-bus.d.ts.map +1 -1
  563. package/dist/modules/workflow/event-bus.js +14 -11
  564. package/dist/modules/workflow/event-bus.js.map +1 -1
  565. package/dist/modules/workflow/events.routes.d.ts +17 -2
  566. package/dist/modules/workflow/events.routes.d.ts.map +1 -1
  567. package/dist/modules/workflow/events.routes.js +31 -12
  568. package/dist/modules/workflow/events.routes.js.map +1 -1
  569. package/dist/modules/workflow/file-lock.service.d.ts +22 -4
  570. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  571. package/dist/modules/workflow/file-lock.service.js +27 -4
  572. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  573. package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
  574. package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
  575. package/dist/modules/workflow/git/change-request-link.js +34 -0
  576. package/dist/modules/workflow/git/change-request-link.js.map +1 -0
  577. package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
  578. package/dist/modules/workflow/git/git-version.js +3 -1
  579. package/dist/modules/workflow/git/git-version.js.map +1 -1
  580. package/dist/modules/workflow/git/git.service.d.ts +303 -6
  581. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  582. package/dist/modules/workflow/git/git.service.js +753 -170
  583. package/dist/modules/workflow/git/git.service.js.map +1 -1
  584. package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
  585. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
  586. package/dist/modules/workflow/git/node-git-runner.js +273 -0
  587. package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
  588. package/dist/modules/workflow/git/pull-request.service.d.ts +57 -2
  589. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  590. package/dist/modules/workflow/git/pull-request.service.js +165 -25
  591. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  592. package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
  593. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  594. package/dist/modules/workflow/pending-commits.service.js +24 -4
  595. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  596. package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
  597. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  598. package/dist/modules/workflow/pending-commits.worker.js +107 -58
  599. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  600. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
  601. package/dist/modules/workflow/recovery-bot.js +3 -1
  602. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  603. package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
  604. package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
  605. package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
  606. package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
  607. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
  608. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
  609. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -0
  610. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  611. package/dist/modules/workflow/review-workflow/review-workflow.service.js +269 -90
  612. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  613. package/dist/modules/workflow/sanitize-error.d.ts +5 -2
  614. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  615. package/dist/modules/workflow/sanitize-error.js +8 -4
  616. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  617. package/dist/modules/workflow/workflow.routes.d.ts +2 -0
  618. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  619. package/dist/modules/workflow/workflow.routes.js +259 -29
  620. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  621. package/dist/modules/workflow/workflow.service.d.ts +217 -14
  622. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  623. package/dist/modules/workflow/workflow.service.js +1142 -109
  624. package/dist/modules/workflow/workflow.service.js.map +1 -1
  625. package/dist/modules/workspace/empty-dirs.d.ts +15 -0
  626. package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
  627. package/dist/modules/workspace/empty-dirs.js +90 -0
  628. package/dist/modules/workspace/empty-dirs.js.map +1 -0
  629. package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
  630. package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
  631. package/dist/modules/workspace/file-readers/content-mode.js +55 -0
  632. package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
  633. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
  634. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  635. package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
  636. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  637. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
  638. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
  639. package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
  640. package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
  641. package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
  642. package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
  643. package/dist/modules/workspace/file-readers/document-reader.js +5 -0
  644. package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
  645. package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
  646. package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
  647. package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
  648. package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
  649. package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
  650. package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
  651. package/dist/modules/workspace/file-readers/file-reader.js +11 -6
  652. package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
  653. package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
  654. package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
  655. package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
  656. package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
  657. package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
  658. package/dist/modules/workspace/file-readers/image-reader.js +9 -5
  659. package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
  660. package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
  661. package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
  662. package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
  663. package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
  664. package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
  665. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  666. package/dist/modules/workspace/file-readers/text-reader.js +57 -3
  667. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  668. package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
  669. package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
  670. package/dist/modules/workspace/git-internals.middleware.js +93 -0
  671. package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
  672. package/dist/modules/workspace/not-found.d.ts +80 -0
  673. package/dist/modules/workspace/not-found.d.ts.map +1 -0
  674. package/dist/modules/workspace/not-found.js +112 -0
  675. package/dist/modules/workspace/not-found.js.map +1 -0
  676. package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
  677. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  678. package/dist/modules/workspace/session-ontology.gate.js +1 -1
  679. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  680. package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
  681. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
  682. package/dist/modules/workspace/startup/beside-checkout.js +110 -0
  683. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
  684. package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
  685. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  686. package/dist/modules/workspace/startup/kb-git.js +34 -35
  687. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  688. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
  689. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  690. package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
  691. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  692. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
  693. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  694. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
  695. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  696. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
  697. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
  698. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
  699. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
  700. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
  701. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
  702. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
  703. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
  704. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
  705. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
  706. package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
  707. package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
  708. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +41 -0
  709. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
  710. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +85 -0
  711. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
  712. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
  713. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
  714. package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
  715. package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
  716. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
  717. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  718. package/dist/modules/workspace/startup/steps/seed-tree.js +174 -54
  719. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  720. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
  721. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  722. package/dist/modules/workspace/startup/steps/template-files.step.js +521 -126
  723. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  724. package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
  725. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
  726. package/dist/modules/workspace/startup/steps/template-source.js +160 -0
  727. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
  728. package/dist/modules/workspace/workspace.routes.d.ts +14 -2
  729. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  730. package/dist/modules/workspace/workspace.routes.js +657 -246
  731. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  732. package/dist/modules/workspace/workspace.service.d.ts +323 -12
  733. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  734. package/dist/modules/workspace/workspace.service.js +1006 -257
  735. package/dist/modules/workspace/workspace.service.js.map +1 -1
  736. package/dist/modules/workspace/workspace.tools.d.ts +42 -1
  737. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  738. package/dist/modules/workspace/workspace.tools.js +1590 -146
  739. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  740. package/dist/modules/workspace/write-denial.d.ts +76 -0
  741. package/dist/modules/workspace/write-denial.d.ts.map +1 -0
  742. package/dist/modules/workspace/write-denial.js +138 -0
  743. package/dist/modules/workspace/write-denial.js.map +1 -0
  744. package/dist/shared/canonical-file-identity.d.ts +64 -0
  745. package/dist/shared/canonical-file-identity.d.ts.map +1 -0
  746. package/dist/shared/canonical-file-identity.js +92 -0
  747. package/dist/shared/canonical-file-identity.js.map +1 -0
  748. package/dist/shared/domain-errors.d.ts +128 -0
  749. package/dist/shared/domain-errors.d.ts.map +1 -1
  750. package/dist/shared/domain-errors.js +166 -0
  751. package/dist/shared/domain-errors.js.map +1 -1
  752. package/dist/shared/email-identity.d.ts +32 -0
  753. package/dist/shared/email-identity.d.ts.map +1 -0
  754. package/dist/shared/email-identity.js +37 -0
  755. package/dist/shared/email-identity.js.map +1 -0
  756. package/dist/shared/fs-errors.d.ts +8 -0
  757. package/dist/shared/fs-errors.d.ts.map +1 -0
  758. package/dist/shared/fs-errors.js +11 -0
  759. package/dist/shared/fs-errors.js.map +1 -0
  760. package/dist/shared/fs-walk.d.ts +20 -6
  761. package/dist/shared/fs-walk.d.ts.map +1 -1
  762. package/dist/shared/fs-walk.js +35 -27
  763. package/dist/shared/fs-walk.js.map +1 -1
  764. package/dist/shared/fs.contract.d.ts +206 -0
  765. package/dist/shared/fs.contract.d.ts.map +1 -0
  766. package/dist/shared/fs.contract.js +79 -0
  767. package/dist/shared/fs.contract.js.map +1 -0
  768. package/dist/shared/git-failure.d.ts +80 -0
  769. package/dist/shared/git-failure.d.ts.map +1 -0
  770. package/dist/shared/git-failure.js +139 -0
  771. package/dist/shared/git-failure.js.map +1 -0
  772. package/dist/shared/git-internals.d.ts +17 -0
  773. package/dist/shared/git-internals.d.ts.map +1 -0
  774. package/dist/shared/git-internals.js +123 -0
  775. package/dist/shared/git-internals.js.map +1 -0
  776. package/dist/shared/git.contract.d.ts +157 -0
  777. package/dist/shared/git.contract.d.ts.map +1 -0
  778. package/dist/shared/git.contract.js +103 -0
  779. package/dist/shared/git.contract.js.map +1 -0
  780. package/dist/shared/http-errors.d.ts +33 -0
  781. package/dist/shared/http-errors.d.ts.map +1 -0
  782. package/dist/shared/http-errors.js +35 -0
  783. package/dist/shared/http-errors.js.map +1 -0
  784. package/dist/shared/kb-walk.d.ts +52 -0
  785. package/dist/shared/kb-walk.d.ts.map +1 -0
  786. package/dist/shared/kb-walk.js +76 -0
  787. package/dist/shared/kb-walk.js.map +1 -0
  788. package/dist/shared/logger.contract.d.ts +63 -0
  789. package/dist/shared/logger.contract.d.ts.map +1 -0
  790. package/dist/shared/logger.contract.js +126 -0
  791. package/dist/shared/logger.contract.js.map +1 -0
  792. package/dist/shared/logging.d.ts +31 -0
  793. package/dist/shared/logging.d.ts.map +1 -0
  794. package/dist/shared/logging.js +92 -0
  795. package/dist/shared/logging.js.map +1 -0
  796. package/dist/shared/path-containment.d.ts +21 -0
  797. package/dist/shared/path-containment.d.ts.map +1 -0
  798. package/dist/shared/path-containment.js +34 -0
  799. package/dist/shared/path-containment.js.map +1 -0
  800. package/dist/shared/path-order.d.ts +18 -0
  801. package/dist/shared/path-order.d.ts.map +1 -0
  802. package/dist/shared/path-order.js +35 -0
  803. package/dist/shared/path-order.js.map +1 -0
  804. package/dist/shared/pending-proposals.d.ts +71 -0
  805. package/dist/shared/pending-proposals.d.ts.map +1 -0
  806. package/dist/shared/pending-proposals.js +98 -0
  807. package/dist/shared/pending-proposals.js.map +1 -0
  808. package/dist/shared/printable.d.ts +22 -0
  809. package/dist/shared/printable.d.ts.map +1 -0
  810. package/dist/shared/printable.js +65 -0
  811. package/dist/shared/printable.js.map +1 -0
  812. package/dist/shared/redact-secret.d.ts +30 -0
  813. package/dist/shared/redact-secret.d.ts.map +1 -0
  814. package/dist/shared/redact-secret.js +114 -0
  815. package/dist/shared/redact-secret.js.map +1 -0
  816. package/dist/shared/rename-no-replace.d.ts +119 -0
  817. package/dist/shared/rename-no-replace.d.ts.map +1 -0
  818. package/dist/shared/rename-no-replace.js +323 -0
  819. package/dist/shared/rename-no-replace.js.map +1 -0
  820. package/dist/shared/ssrf.d.ts.map +1 -1
  821. package/dist/shared/ssrf.js +49 -18
  822. package/dist/shared/ssrf.js.map +1 -1
  823. package/dist/shared/ttl-cache.d.ts +28 -1
  824. package/dist/shared/ttl-cache.d.ts.map +1 -1
  825. package/dist/shared/ttl-cache.js +33 -1
  826. package/dist/shared/ttl-cache.js.map +1 -1
  827. package/dist/version.d.ts +10 -6
  828. package/dist/version.d.ts.map +1 -1
  829. package/dist/version.js +10 -6
  830. package/dist/version.js.map +1 -1
  831. package/kb-template/.bevelignore +4 -3
  832. package/kb-template/AGENTS.md +364 -54
  833. package/kb-template/KnowledgeBase/How to get started.md +1 -1
  834. package/kb-template/Skills/.gitkeep +0 -0
  835. package/kb-template/access.md +5 -3
  836. package/kb-template/mcp-description.md +27 -0
  837. package/migrations/0005_github_facade.sql +25 -0
  838. package/migrations/0006_api_tokens_revoked_by.sql +1 -0
  839. package/migrations/0007_github_facade_registered.sql +1 -0
  840. package/migrations/0008_change_request_apply_failure.sql +4 -0
  841. package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
  842. package/migrations/0010_plugin_join_requests.sql +15 -0
  843. package/migrations/0011_plugin_join_request_claim.sql +11 -0
  844. package/migrations/meta/0005_snapshot.json +1717 -0
  845. package/migrations/meta/0006_snapshot.json +1723 -0
  846. package/migrations/meta/0007_snapshot.json +1729 -0
  847. package/migrations/meta/0008_snapshot.json +1753 -0
  848. package/migrations/meta/0009_snapshot.json +1759 -0
  849. package/migrations/meta/0010_snapshot.json +1872 -0
  850. package/migrations/meta/0011_snapshot.json +1884 -0
  851. package/migrations/meta/_journal.json +49 -0
  852. package/package.json +8 -7
  853. package/src/__tests__/core-config.domain.test.ts +67 -2
  854. package/src/__tests__/kb-layout-config.test.ts +295 -0
  855. package/src/__tests__/open-change-gate.ts +15 -0
  856. package/src/core/__tests__/catalog-cache-invalidation.test.ts +226 -0
  857. package/src/core/__tests__/catalog-revision.test.ts +190 -0
  858. package/src/core/__tests__/lifecycle.test.ts +369 -0
  859. package/src/core/__tests__/public-config.test.ts +30 -0
  860. package/src/core/__tests__/readiness.test.ts +109 -0
  861. package/src/core/catalog-cache-invalidation.ts +111 -0
  862. package/src/core/catalog-revision.ts +110 -0
  863. package/src/core/create-core-server.ts +358 -63
  864. package/src/core/create-core-services.ts +449 -107
  865. package/src/core/lifecycle.ts +312 -0
  866. package/src/core/public-config.ts +86 -0
  867. package/src/core/readiness.ts +123 -0
  868. package/src/core-config.ts +70 -4
  869. package/src/index.ts +15 -0
  870. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
  871. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
  872. package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
  873. package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
  874. package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
  875. package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
  876. package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
  877. package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +88 -0
  878. package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
  879. package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
  880. package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
  881. package/src/modules/access/__tests__/access-control.service.test.ts +142 -81
  882. package/src/modules/access/__tests__/access-groups.test.ts +47 -14
  883. package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
  884. package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
  885. package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
  886. package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
  887. package/src/modules/access/__tests__/access-plugin-principals.test.ts +385 -0
  888. package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
  889. package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
  890. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
  891. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
  892. package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
  893. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
  894. package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
  895. package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
  896. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
  897. package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
  898. package/src/modules/access/__tests__/creator-access.test.ts +81 -83
  899. package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
  900. package/src/modules/access/__tests__/groups-admin.service.test.ts +14 -5
  901. package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
  902. package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
  903. package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
  904. package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
  905. package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
  906. package/src/modules/access/__tests__/users-db-double.ts +63 -0
  907. package/src/modules/access/access-control.interface.ts +269 -18
  908. package/src/modules/access/access-control.service.ts +943 -226
  909. package/src/modules/access/access-mutation.service.ts +182 -13
  910. package/src/modules/access/access-view.ts +316 -0
  911. package/src/modules/access/access.routes.ts +385 -105
  912. package/src/modules/access/admin-locked-commit.ts +12 -14
  913. package/src/modules/access/admin-route-helpers.ts +4 -4
  914. package/src/modules/access/change-read-gate.ts +148 -0
  915. package/src/modules/access/creator-access.ts +89 -124
  916. package/src/modules/access/directory-sync-bot.ts +4 -1
  917. package/src/modules/access/groups-admin.service.ts +3 -3
  918. package/src/modules/access/reference-scan.ts +5 -5
  919. package/src/modules/access/roles-admin.service.ts +94 -15
  920. package/src/modules/access/synced-groups-committer.ts +10 -15
  921. package/src/modules/access/synced-groups-writer.ts +3 -12
  922. package/src/modules/access/user-access-removal.service.ts +711 -0
  923. package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
  924. package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
  925. package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
  926. package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
  927. package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
  928. package/src/modules/access-model/access-errors.ts +56 -15
  929. package/src/modules/access-model/access-grammar.ts +198 -49
  930. package/src/modules/access-model/access-splice.ts +18 -18
  931. package/src/modules/access-model/access-template.ts +44 -0
  932. package/src/modules/access-model/change-gate.ts +80 -0
  933. package/src/modules/access-model/creator.ts +18 -21
  934. package/src/modules/access-model/frontmatter-lines.ts +98 -0
  935. package/src/modules/access-model/group-files.ts +4 -0
  936. package/src/modules/access-model/plugin-principals.ts +130 -0
  937. package/src/modules/access-model/roles-yaml-guard.ts +182 -4
  938. package/src/modules/admin/admin-access.service.ts +6 -2
  939. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
  940. package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
  941. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
  942. package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
  943. package/src/modules/agent-instructions/compose.ts +271 -0
  944. package/src/modules/agent-instructions/index.ts +14 -0
  945. package/src/modules/agent-instructions/read-preamble.ts +88 -0
  946. package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
  947. package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
  948. package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
  949. package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
  950. package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
  951. package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
  952. package/src/modules/auth/account-erasure.service.ts +117 -10
  953. package/src/modules/auth/account.routes.ts +115 -14
  954. package/src/modules/auth/auth.routes.ts +20 -7
  955. package/src/modules/auth/auth.service.ts +187 -28
  956. package/src/modules/auth/oidc-auth-provider.ts +120 -22
  957. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
  958. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  959. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
  960. package/src/modules/connection-probe/connection-probe.service.ts +59 -7
  961. package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
  962. package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
  963. package/src/modules/database/advisory-lock.ts +346 -0
  964. package/src/modules/database/connection.ts +9 -1
  965. package/src/modules/database/core-schema.ts +177 -0
  966. package/src/modules/database/migrate.ts +27 -6
  967. package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
  968. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
  969. package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
  970. package/src/modules/diff/diff.routes.ts +2 -4
  971. package/src/modules/diff/diff.service.ts +75 -79
  972. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
  973. package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
  974. package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
  975. package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
  976. package/src/modules/kb-fs/file-change-notifier.ts +4 -1
  977. package/src/modules/kb-fs/git-guarded-filesystem.ts +123 -0
  978. package/src/modules/kb-fs/locking-filesystem.ts +399 -109
  979. package/src/modules/kb-fs/node-fs.ts +165 -0
  980. package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
  981. package/src/modules/kb-fs/repo-path.ts +213 -3
  982. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
  983. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
  984. package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
  985. package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
  986. package/src/modules/kb-sync/index.ts +19 -0
  987. package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
  988. package/src/modules/kb-sync/kb-sync.routes.ts +200 -0
  989. package/src/modules/kb-sync/kb-sync.service.ts +204 -0
  990. package/src/modules/kb-sync/sync-auth.ts +118 -0
  991. package/src/modules/kb-sync/sync-payload.ts +143 -0
  992. package/src/modules/marketplace/__tests__/github-facade.test.ts +575 -0
  993. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +180 -0
  994. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
  995. package/src/modules/marketplace/git-http.routes.ts +196 -0
  996. package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +110 -0
  997. package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
  998. package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +263 -0
  999. package/src/modules/marketplace/github-facade/github-facade.routes.ts +294 -0
  1000. package/src/modules/marketplace/github-facade/github-facade.service.ts +282 -0
  1001. package/src/modules/marketplace/github-facade/index.ts +26 -0
  1002. package/src/modules/marketplace/index.ts +3 -0
  1003. package/src/modules/marketplace/marketplace-repo.service.ts +352 -0
  1004. package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
  1005. package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
  1006. package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
  1007. package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
  1008. package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
  1009. package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
  1010. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +8 -5
  1011. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
  1012. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
  1013. package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
  1014. package/src/modules/mcp/__tests__/mcp.service.test.ts +274 -6
  1015. package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
  1016. package/src/modules/mcp/downstream-pool.ts +256 -0
  1017. package/src/modules/mcp/downstream-token-refresh.ts +170 -0
  1018. package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
  1019. package/src/modules/mcp/first-call-probe.cli.ts +60 -0
  1020. package/src/modules/mcp/first-call-probe.ts +418 -0
  1021. package/src/modules/mcp/manual-failure-memo.ts +81 -23
  1022. package/src/modules/mcp/mcp-auth.middleware.ts +25 -16
  1023. package/src/modules/mcp/mcp.routes.ts +376 -482
  1024. package/src/modules/mcp/mcp.service.ts +846 -176
  1025. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +8 -2
  1026. package/src/modules/mcp/oauth/oauth-consent.routes.ts +28 -6
  1027. package/src/modules/mcp/oauth/oauth-state.ts +15 -3
  1028. package/src/modules/mcp/surface-log-throttle.ts +120 -0
  1029. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +166 -0
  1030. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
  1031. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +423 -0
  1032. package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
  1033. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
  1034. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
  1035. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +201 -16
  1036. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +284 -0
  1037. package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
  1038. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +330 -20
  1039. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +456 -0
  1040. package/src/modules/plugins/__tests__/plugins.routes.test.ts +441 -43
  1041. package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
  1042. package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
  1043. package/src/modules/plugins/compile/compile-marketplace.ts +500 -0
  1044. package/src/modules/plugins/compile/marketplace-compiler.service.ts +151 -0
  1045. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +365 -0
  1046. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +161 -0
  1047. package/src/modules/plugins/discovery/bundle-dialect/registry.ts +250 -0
  1048. package/src/modules/plugins/discovery/kb-plugin-source.ts +117 -0
  1049. package/src/modules/plugins/discovery/native.source.ts +140 -0
  1050. package/src/modules/plugins/discovery/plugin-source.ts +113 -0
  1051. package/src/modules/plugins/index.ts +25 -2
  1052. package/src/modules/plugins/join-request-jobs.service.ts +675 -0
  1053. package/src/modules/plugins/join-request-records.store.ts +353 -0
  1054. package/src/modules/plugins/join-requests.service.ts +7 -4
  1055. package/src/modules/plugins/plugin-links.service.ts +350 -0
  1056. package/src/modules/plugins/plugin-links.ts +187 -0
  1057. package/src/modules/plugins/plugin-provision.service.ts +356 -78
  1058. package/src/modules/plugins/plugin-rename.service.ts +310 -0
  1059. package/src/modules/plugins/plugins.contract.ts +79 -4
  1060. package/src/modules/plugins/plugins.routes.ts +320 -123
  1061. package/src/modules/plugins/plugins.service.ts +184 -57
  1062. package/src/modules/plugins/plugins.tools.ts +88 -0
  1063. package/src/modules/plugins/teams.routes.ts +129 -0
  1064. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
  1065. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
  1066. package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
  1067. package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
  1068. package/src/modules/secrets-vault/index.ts +1 -0
  1069. package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
  1070. package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
  1071. package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
  1072. package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
  1073. package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
  1074. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +240 -1
  1075. package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
  1076. package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
  1077. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
  1078. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
  1079. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
  1080. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
  1081. package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
  1082. package/src/modules/settings/connection-check.ts +351 -0
  1083. package/src/modules/settings/deployment-settings.service.ts +478 -39
  1084. package/src/modules/settings/git-root-folders.ts +171 -0
  1085. package/src/modules/settings/oidc-check.ts +229 -0
  1086. package/src/modules/settings/setup.routes.ts +480 -110
  1087. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
  1088. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
  1089. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
  1090. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
  1091. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
  1092. package/src/modules/skills/__tests__/skills.service.test.ts +308 -2
  1093. package/src/modules/skills/allowed-tools-check.ts +352 -0
  1094. package/src/modules/skills/index.ts +5 -0
  1095. package/src/modules/skills/pending-skills.service.ts +26 -85
  1096. package/src/modules/skills/skill-access-requests.routes.ts +203 -0
  1097. package/src/modules/skills/skills.contract.ts +67 -3
  1098. package/src/modules/skills/skills.routes.ts +75 -4
  1099. package/src/modules/skills/skills.service.ts +258 -66
  1100. package/src/modules/skills/skills.tools.ts +43 -5
  1101. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
  1102. package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
  1103. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +124 -0
  1104. package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
  1105. package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
  1106. package/src/modules/tool-auth/external-api-key.interface.ts +73 -1
  1107. package/src/modules/tool-auth/external-api-key.service.ts +95 -29
  1108. package/src/modules/tool-auth/internal-token.service.ts +5 -2
  1109. package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
  1110. package/src/modules/tool-auth/tool-auth.middleware.ts +16 -7
  1111. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
  1112. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
  1113. package/src/modules/tool-helpers/tool-context.ts +46 -7
  1114. package/src/modules/tool-helpers/tool-handler.ts +23 -5
  1115. package/src/modules/tool-helpers/tool.contract.ts +5 -0
  1116. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
  1117. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  1118. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
  1119. package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
  1120. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
  1121. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
  1122. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +11 -4
  1123. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
  1124. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
  1125. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
  1126. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
  1127. package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
  1128. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
  1129. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
  1130. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
  1131. package/src/modules/tool-manuals/index.ts +3 -0
  1132. package/src/modules/tool-manuals/mcp-json-discovery.ts +164 -95
  1133. package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
  1134. package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
  1135. package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
  1136. package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
  1137. package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
  1138. package/src/modules/tool-manuals/tool-manuals.service.ts +440 -104
  1139. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
  1140. package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
  1141. package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
  1142. package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
  1143. package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
  1144. package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
  1145. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
  1146. package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
  1147. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +3 -1
  1148. package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
  1149. package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
  1150. package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
  1151. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
  1152. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
  1153. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +3 -0
  1154. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
  1155. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +296 -27
  1156. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
  1157. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
  1158. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
  1159. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +133 -2
  1160. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +442 -0
  1161. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
  1162. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
  1163. package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
  1164. package/src/modules/workflow/event-bus.ts +26 -28
  1165. package/src/modules/workflow/events.routes.ts +56 -21
  1166. package/src/modules/workflow/file-lock.service.ts +27 -4
  1167. package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
  1168. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
  1169. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
  1170. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
  1171. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
  1172. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
  1173. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
  1174. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
  1175. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
  1176. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
  1177. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
  1178. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
  1179. package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
  1180. package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
  1181. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
  1182. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
  1183. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
  1184. package/src/modules/workflow/git/change-request-link.ts +35 -0
  1185. package/src/modules/workflow/git/git-version.ts +5 -2
  1186. package/src/modules/workflow/git/git.service.ts +922 -199
  1187. package/src/modules/workflow/git/node-git-runner.ts +315 -0
  1188. package/src/modules/workflow/git/pull-request.service.ts +188 -27
  1189. package/src/modules/workflow/pending-commits.service.ts +26 -7
  1190. package/src/modules/workflow/pending-commits.worker.ts +114 -69
  1191. package/src/modules/workflow/recovery-bot.ts +4 -1
  1192. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
  1193. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
  1194. package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
  1195. package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
  1196. package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
  1197. package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
  1198. package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
  1199. package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
  1200. package/src/modules/workflow/sanitize-error.ts +9 -4
  1201. package/src/modules/workflow/workflow.routes.ts +273 -29
  1202. package/src/modules/workflow/workflow.service.ts +1341 -174
  1203. package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
  1204. package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
  1205. package/src/modules/workspace/__tests__/git-internals.security.test.ts +792 -0
  1206. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
  1207. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
  1208. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
  1209. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
  1210. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
  1211. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
  1212. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
  1213. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
  1214. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
  1215. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +31 -13
  1216. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
  1217. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +94 -0
  1218. package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
  1219. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
  1220. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +98 -13
  1221. package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
  1222. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
  1223. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
  1224. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
  1225. package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
  1226. package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
  1227. package/src/modules/workspace/empty-dirs.ts +79 -0
  1228. package/src/modules/workspace/file-readers/README.md +32 -0
  1229. package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
  1230. package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
  1231. package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
  1232. package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
  1233. package/src/modules/workspace/file-readers/content-mode.ts +87 -0
  1234. package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
  1235. package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
  1236. package/src/modules/workspace/file-readers/document-reader.ts +6 -0
  1237. package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
  1238. package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
  1239. package/src/modules/workspace/file-readers/file-reader.ts +27 -6
  1240. package/src/modules/workspace/file-readers/image-reader.ts +10 -5
  1241. package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
  1242. package/src/modules/workspace/file-readers/text-reader.ts +61 -4
  1243. package/src/modules/workspace/git-internals.middleware.ts +86 -0
  1244. package/src/modules/workspace/not-found.ts +119 -0
  1245. package/src/modules/workspace/session-ontology.gate.ts +1 -1
  1246. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
  1247. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
  1248. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
  1249. package/src/modules/workspace/startup/beside-checkout.ts +112 -0
  1250. package/src/modules/workspace/startup/kb-git.ts +43 -36
  1251. package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
  1252. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1405 -48
  1253. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
  1254. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
  1255. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
  1256. package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
  1257. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +90 -0
  1258. package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
  1259. package/src/modules/workspace/startup/steps/seed-tree.ts +177 -54
  1260. package/src/modules/workspace/startup/steps/template-files.step.ts +581 -126
  1261. package/src/modules/workspace/startup/steps/template-source.ts +168 -0
  1262. package/src/modules/workspace/workspace.routes.ts +691 -269
  1263. package/src/modules/workspace/workspace.service.ts +1071 -292
  1264. package/src/modules/workspace/workspace.tools.ts +1787 -180
  1265. package/src/modules/workspace/write-denial.ts +189 -0
  1266. package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
  1267. package/src/shared/__tests__/email-identity.test.ts +35 -0
  1268. package/src/shared/__tests__/filename.test.ts +0 -0
  1269. package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
  1270. package/src/shared/__tests__/git-failure.test.ts +198 -0
  1271. package/src/shared/__tests__/git-internals.test.ts +115 -0
  1272. package/src/shared/__tests__/http-errors.test.ts +42 -0
  1273. package/src/shared/__tests__/logging.test.ts +185 -0
  1274. package/src/shared/__tests__/path-containment.test.ts +60 -0
  1275. package/src/shared/__tests__/path-order.test.ts +24 -0
  1276. package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
  1277. package/src/shared/__tests__/printable.test.ts +64 -0
  1278. package/src/shared/__tests__/redact-secret.test.ts +122 -0
  1279. package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
  1280. package/src/shared/__tests__/ssrf.test.ts +20 -0
  1281. package/src/shared/__tests__/ttl-cache.test.ts +55 -0
  1282. package/src/shared/canonical-file-identity.ts +92 -0
  1283. package/src/shared/domain-errors.ts +178 -0
  1284. package/src/shared/email-identity.ts +38 -0
  1285. package/src/shared/fs.contract.ts +225 -0
  1286. package/src/shared/git-failure.ts +196 -0
  1287. package/src/shared/git-internals.ts +124 -0
  1288. package/src/shared/git.contract.ts +170 -0
  1289. package/src/shared/http-errors.ts +36 -0
  1290. package/src/shared/logger.contract.ts +141 -0
  1291. package/src/shared/logging.ts +96 -0
  1292. package/src/shared/path-containment.ts +34 -0
  1293. package/src/shared/path-order.ts +33 -0
  1294. package/src/shared/pending-proposals.ts +161 -0
  1295. package/src/shared/printable.ts +68 -0
  1296. package/src/shared/redact-secret.ts +118 -0
  1297. package/src/shared/rename-no-replace.ts +386 -0
  1298. package/src/shared/ssrf.ts +44 -16
  1299. package/src/shared/ttl-cache.ts +33 -1
  1300. package/src/version.ts +10 -6
  1301. package/src/modules/diff/diff-paths.ts +0 -9
  1302. package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
  1303. package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
  1304. package/src/modules/mcp/mcp-session-store.ts +0 -162
  1305. package/src/shared/__tests__/fs-walk.test.ts +0 -42
  1306. package/src/shared/fs-walk.ts +0 -30
  1307. package/src/shared/hash-email.ts +0 -14
@@ -1,18 +1,20 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { execFile } from 'node:child_process';
4
- import { promisify } from 'node:util';
3
+ import { logger } from '../../../shared/logging.js';
4
+ import { isFolderPlaceholder } from '@bevel-software/platform-shared';
5
5
  import { AccessDeniedError } from '../../access-model/access-errors.js';
6
6
  import { WorkspaceMutex } from '../../kb-fs/mutex.js';
7
+ import { isAbsence } from '../../../shared/fs.contract.js';
7
8
  import { assertInsideRepo } from '../../kb-fs/repo-path.js';
8
9
  import { cloneTrackingConfigArgs, SAFE_IMPLICIT_FETCH_ARGS } from '../../kb-fs/clone-config.js';
9
10
  import { assertValidBranchName, assertValidRelativePath, isBranchAuthoredBy, isOwnSuggestionsBranch, isProtectedBranch, PROTECTED_BRANCHES, } from '../../kb-fs/branch-name.js';
10
- import { BranchAuthorshipError, WorkflowValidationError, ProtectedBranchError, PullRebaseConflictError, } from '../../../shared/domain-errors.js';
11
- const execFileAsync = promisify(execFile);
12
- function redact(msg) {
13
- const token = process.env.GITHUB_TOKEN;
14
- return token ? msg.replaceAll(token, '***') : msg;
15
- }
11
+ import { BranchAuthorshipError, WorkflowDomainError, WorkflowValidationError, ProtectedBranchError, PullRebaseConflictError, RemoteBranchGoneError, isMissingRemoteBranchFailure, } from '../../../shared/domain-errors.js';
12
+ import { GitRunError, isGitTimeout, redactGitToken, } from '../../../shared/git.contract.js';
13
+ import { NodeGitRunner } from './node-git-runner.js';
14
+ import { printable } from '../../../shared/printable.js';
15
+ import { sanitizeError } from '../sanitize-error.js';
16
+ const log = logger('git');
17
+ const crLog = logger('cr');
16
18
  /**
17
19
  * Fallback committer identity. Every workflow commit overrides the author via
18
20
  * `--author=…` so the real human shows up in `git log`; this is just the
@@ -196,16 +198,15 @@ async function synthesizeUntrackedSideDiff(cwd, relativePath, direction) {
196
198
  : synthesizeDeletedFileDiff(relativePath, text);
197
199
  }
198
200
  catch (err) {
199
- if (err.code === 'ENOENT')
201
+ if (isAbsence(err))
200
202
  return '';
201
203
  throw err;
202
204
  }
203
205
  }
204
206
  /**
205
- * `stat` for the stray check: false only when nothing can be there (ENOENT,
206
- * or ENOTDIR when a parent segment is a file). Permission and I/O failures
207
- * propagate: treating them as "absent" would let an unreadable stray pass as
208
- * committed.
207
+ * `stat` for the stray check: false only when nothing can be there — the
208
+ * disk's own definition of absence. Permission and I/O failures propagate:
209
+ * treating them as "absent" would let an unreadable stray pass as committed.
209
210
  */
210
211
  async function existsForCommitCheck(absolutePath) {
211
212
  try {
@@ -213,8 +214,7 @@ async function existsForCommitCheck(absolutePath) {
213
214
  return true;
214
215
  }
215
216
  catch (err) {
216
- const code = err.code;
217
- if (code === 'ENOENT' || code === 'ENOTDIR')
217
+ if (isAbsence(err))
218
218
  return false;
219
219
  throw err;
220
220
  }
@@ -225,6 +225,7 @@ export class GitService {
225
225
  kbDirName;
226
226
  mutex;
227
227
  accessControl;
228
+ gitRunner;
228
229
  // Per-workspace fetch state, kept OUTSIDE the workspace mutex on purpose.
229
230
  // `git fetch` only writes `refs/remotes/origin/*` and packs new objects —
230
231
  // it doesn't touch HEAD, the index, or the working tree, so it's safe to
@@ -249,12 +250,21 @@ export class GitService {
249
250
  * replaced). No hooks registered = no commit-time validation, which is
250
251
  * safe because validation is advisory and never blocks a commit.
251
252
  */
252
- hooks, kbDirName, mutex = new WorkspaceMutex(), accessControl = null) {
253
+ hooks, kbDirName, mutex = new WorkspaceMutex(), accessControl = null,
254
+ /**
255
+ * How git is actually run — see `shared/git.contract.ts`. The composition
256
+ * root passes the one runner, carrying the deployment's configured
257
+ * deadline; the default here is the same runner on its default deadline,
258
+ * so a directly constructed service (every suite in this module) still has
259
+ * one rather than none.
260
+ */
261
+ gitRunner = new NodeGitRunner()) {
253
262
  this.workspaceService = workspaceService;
254
263
  this.hooks = hooks;
255
264
  this.kbDirName = kbDirName;
256
265
  this.mutex = mutex;
257
266
  this.accessControl = accessControl;
267
+ this.gitRunner = gitRunner;
258
268
  }
259
269
  /**
260
270
  * "Does the pending-commits queue hold rows for this workspace?" — used by
@@ -268,6 +278,17 @@ export class GitService {
268
278
  setPendingCommitsProbe(probe) {
269
279
  this.pendingCommitsProbe = probe;
270
280
  }
281
+ /**
282
+ * Late-bound by the composition root: notified with the branch names every
283
+ * `listBranches` returns. A listing is the one moment the platform sees
284
+ * origin's set of branches, and the workspace layer needs that memory to
285
+ * tell a branch that was deleted from a name that never existed — see
286
+ * `WorkspaceService.noteBranchesListed`.
287
+ */
288
+ onBranchesListed = null;
289
+ setBranchesListedListener(listener) {
290
+ this.onBranchesListed = listener;
291
+ }
271
292
  /**
272
293
  * Mark a workspace as freshly fetched. Called by `WorkspaceService` right
273
294
  * after a branch's clone is created — `git clone` already downloaded every
@@ -282,6 +303,21 @@ export class GitService {
282
303
  // refuses a list that is in fact provably fresh.
283
304
  this.lastImplicitFetchOk.set(workspaceId, true);
284
305
  }
306
+ /**
307
+ * The most recent attempt any workspace made to reach the remote, and
308
+ * whether it succeeded — for the readiness answer. Null before the first
309
+ * attempt of this process's life. Read off the per-workspace fetch record
310
+ * above rather than kept separately, so it cannot disagree with it.
311
+ */
312
+ lastRemoteContact() {
313
+ let latest = null;
314
+ for (const [workspaceId, at] of this.lastImplicitFetchAt) {
315
+ if (latest === null || at > latest.at) {
316
+ latest = { at, ok: this.lastImplicitFetchOk.get(workspaceId) ?? false };
317
+ }
318
+ }
319
+ return latest;
320
+ }
285
321
  /**
286
322
  * Run the registered ADVISORY commit-validation hooks at a commit site.
287
323
  * Preserves the semantics of the injected validator this replaced: a
@@ -295,12 +331,12 @@ export class GitService {
295
331
  try {
296
332
  const report = await hook(ctx);
297
333
  if (report && report.mustFix.length > 0) {
298
- console.warn(formatWarning(report));
334
+ log.warn(formatWarning(report));
299
335
  }
300
336
  }
301
337
  catch (validatorErr) {
302
338
  // Validator failure is non-fatal — advisory only.
303
- console.warn('[git] validator crashed (advisory only, ignoring):', validatorErr instanceof Error ? validatorErr.message : validatorErr);
339
+ log.warn('validator crashed (advisory only, ignoring):', { err: validatorErr });
304
340
  }
305
341
  }
306
342
  }
@@ -453,6 +489,18 @@ export class GitService {
453
489
  };
454
490
  }
455
491
  }));
492
+ // Every name this listing showed — origin's refs and the local heads
493
+ // alike, since a branch checked out here is one the platform has
494
+ // plainly heard of. A listener that misbehaves must not turn a good
495
+ // listing into a failed one.
496
+ try {
497
+ this.onBranchesListed?.(infos.map((info) => info.name));
498
+ }
499
+ catch (err) {
500
+ log.error(`branches-listed listener failed for workspace ${workspaceId}:`, {
501
+ detail: err instanceof Error ? err.message : String(err),
502
+ });
503
+ }
456
504
  return infos;
457
505
  });
458
506
  }
@@ -502,7 +550,7 @@ export class GitService {
502
550
  }
503
551
  catch (err) {
504
552
  const msg = err instanceof Error ? err.message : String(err);
505
- console.warn(`[git] implicit fetch failed for workspace ${workspaceId}: ${msg}`);
553
+ log.warn(`implicit fetch failed for workspace ${workspaceId}: ${msg}`);
506
554
  // Stamp the TTL on failure too so a hard-down origin doesn't make
507
555
  // every subsequent listBranches retry the (still-failing) fetch and
508
556
  // pile up 10s timeouts. Stale local refs are better than spinning.
@@ -823,14 +871,14 @@ export class GitService {
823
871
  }
824
872
  if (trackedAtHead) {
825
873
  await this.git(cwd, ['checkout', 'HEAD', '--', repoRelativePath]).catch((err) => {
826
- console.warn(`[git] discardPath checkout failed for "${repoRelativePath}":`, err instanceof Error ? err.message : err);
874
+ log.warn(`discardPath checkout failed for "${repoRelativePath}":`, { err });
827
875
  });
828
876
  }
829
877
  else {
830
878
  // Untracked new file — remove from working tree.
831
879
  const fileAbs = path.join(cwd, repoRelativePath);
832
880
  await fs.rm(fileAbs, { force: true }).catch((err) => {
833
- console.warn(`[git] discardPath rm failed for "${repoRelativePath}":`, err instanceof Error ? err.message : err);
881
+ log.warn(`discardPath rm failed for "${repoRelativePath}":`, { err });
834
882
  });
835
883
  }
836
884
  });
@@ -839,8 +887,11 @@ export class GitService {
839
887
  assertValidRelativePath(relativePath);
840
888
  assertValidAuthor(user);
841
889
  const repoRelativePath = this.stripRepoPrefix(relativePath);
842
- return this.mutex.run(workspaceId, async () => {
843
- const cwd = await this.repoDir(workspaceId);
890
+ return this.mutex.run(workspaceId, async () => this.commitFileUnlocked(workspaceId, await this.repoDir(workspaceId), user, relativePath, repoRelativePath, summary, skipValidator));
891
+ }
892
+ /** `commitFile`'s body, for a caller that already holds the workspace's reservation. */
893
+ async commitFileUnlocked(workspaceId, cwd, user, relativePath, repoRelativePath, summary, skipValidator) {
894
+ {
844
895
  const branch = await this.currentBranch(cwd);
845
896
  // Path-scoped status — is this specific file dirty? `--porcelain` on
846
897
  // a single path returns empty when there's nothing to commit.
@@ -900,7 +951,7 @@ export class GitService {
900
951
  subject: subj ?? subject,
901
952
  committedAt: committedAt?.trim() ?? new Date().toISOString(),
902
953
  };
903
- });
954
+ }
904
955
  }
905
956
  /**
906
957
  * Atomic multi-file commit — commits the caller's batch as ONE commit
@@ -1049,8 +1100,11 @@ export class GitService {
1049
1100
  });
1050
1101
  }
1051
1102
  async push(workspaceId, user, opts) {
1052
- return this.mutex.run(workspaceId, async () => {
1053
- const cwd = await this.repoDir(workspaceId);
1103
+ return this.mutex.run(workspaceId, async () => this.pushUnlocked(workspaceId, await this.repoDir(workspaceId), user, opts));
1104
+ }
1105
+ /** `push`'s body, for a caller that already holds the workspace's reservation. */
1106
+ async pushUnlocked(workspaceId, cwd, user, opts) {
1107
+ {
1054
1108
  const branch = await this.currentBranch(cwd);
1055
1109
  // Access gate fires only when pushing to a protected branch. Pushing
1056
1110
  // a feature/draft branch to its same-named remote can't change
@@ -1072,7 +1126,7 @@ export class GitService {
1072
1126
  await this.assertCanWriteAtRef(workspaceId, `origin/${branch}`, user.email, touched);
1073
1127
  }
1074
1128
  await this.git(cwd, ['push', '-u', 'origin', branch]);
1075
- });
1129
+ }
1076
1130
  }
1077
1131
  // `forcePush` method removed. The workflow layer no longer auto-force-pushes
1078
1132
  // as a recovery — when the cooperative `pull --rebase` + retry can't
@@ -1188,7 +1242,7 @@ export class GitService {
1188
1242
  *
1189
1243
  * Returns the merge commit SHA, or the conflicting paths.
1190
1244
  */
1191
- async mergeChangeRequest(baseWorkspaceId, sourceBranch, targetBranch, commit, user) {
1245
+ async mergeChangeRequest(baseWorkspaceId, sourceBranch, targetBranch, commit, user, opts = {}) {
1192
1246
  assertValidBranchName(sourceBranch);
1193
1247
  assertValidBranchName(targetBranch);
1194
1248
  assertValidAuthor(user);
@@ -1201,6 +1255,13 @@ export class GitService {
1201
1255
  // unknown" on a clone that lacks it).
1202
1256
  await this.git(cwd, ['config', 'user.name', BOT_NAME]);
1203
1257
  await this.git(cwd, ['config', 'user.email', BOT_EMAIL]);
1258
+ if (opts.requireCleanTarget) {
1259
+ const { stdout: dirty } = await this.git(cwd, ['status', '--porcelain=v1', '-z']);
1260
+ const unpushed = await this.hasUnpushedCommitsAt(cwd);
1261
+ if (parsePorcelainZ(dirty).length > 0 || unpushed) {
1262
+ throw new WorkflowDomainError(`"${targetBranch}" has edits that are not shared yet. Try the merge again once they are saved.`, 409, { kind: 'merge-target-busy', targetBranch });
1263
+ }
1264
+ }
1204
1265
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
1205
1266
  // Explicit destination refspecs, because the two `origin/<branch>`
1206
1267
  // reads below MUST see the tips this fetch just retrieved. A bare
@@ -1227,6 +1288,14 @@ export class GitService {
1227
1288
  // discards a prior attempt's state, never real work.
1228
1289
  await this.git(cwd, ['checkout', targetBranch]);
1229
1290
  await this.git(cwd, ['reset', '--hard', `origin/${targetBranch}`]);
1291
+ if (opts.authorize) {
1292
+ // The tip this attempt merges into, and the paths it writes against
1293
+ // that tip. Re-read every attempt: a retry happens BECAUSE the
1294
+ // target moved, which can move its roles and its file set too.
1295
+ const { stdout: targetSha } = await this.git(cwd, ['rev-parse', `origin/${targetBranch}`]);
1296
+ const changedPaths = await this.prChangedPathsAt(cwd, `origin/${targetBranch}`, `origin/${sourceBranch}`, { forAccessCheck: true });
1297
+ await opts.authorize({ sha: targetSha.trim(), changedPaths });
1298
+ }
1230
1299
  // `--no-commit --no-ff` so we author the merge commit as the human and
1231
1300
  // never fast-forward past the merge record.
1232
1301
  try {
@@ -1239,7 +1308,7 @@ export class GitService {
1239
1308
  return { kind: 'conflicts', paths };
1240
1309
  // Not a conflict — surface the underlying git error so the real cause
1241
1310
  // (e.g. missing identity, unrelated histories) is diagnosable.
1242
- throw new Error(`git merge failed without detectable conflicts: ${redact(err instanceof Error ? err.message : String(err))}`);
1311
+ throw new Error(`git merge failed without detectable conflicts: ${redactGitToken(err instanceof Error ? err.message : String(err))}`);
1243
1312
  }
1244
1313
  // Nothing staged ⇒ base already contains source (empty CR). The base tip
1245
1314
  // is the "merged" state; report it without an empty commit.
@@ -1369,68 +1438,244 @@ export class GitService {
1369
1438
  ]);
1370
1439
  return `refs/remotes/origin/${branch}`;
1371
1440
  }
1372
- async pull(workspaceId) {
1441
+ /** A commit-free tree, for diffing against a HEAD that did not exist yet. */
1442
+ static EMPTY_TREE_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
1443
+ /**
1444
+ * The checked-out branch by its symbolic ref, which an UNBORN HEAD (a clone
1445
+ * of an empty upstream) still has — `rev-parse --abbrev-ref HEAD` does not
1446
+ * resolve there, so the tolerant tree reads below would never be reached.
1447
+ */
1448
+ async checkedOutBranch(cwd) {
1449
+ const { stdout } = await this.git(cwd, ['symbolic-ref', '--short', 'HEAD']);
1450
+ return stdout.trim();
1451
+ }
1452
+ /**
1453
+ * Whether origin still has `branch`. `ls-remote --exit-code` answers with
1454
+ * exit status 2 for "no such ref" — the one failure that is a fact about the
1455
+ * branch rather than about reaching the host, which propagates.
1456
+ */
1457
+ async remoteBranchExists(workspaceId, branch) {
1458
+ assertValidBranchName(branch);
1459
+ const cwd = await this.repoDir(workspaceId);
1460
+ try {
1461
+ await this.git(cwd, ['ls-remote', '--exit-code', '--heads', 'origin', `refs/heads/${branch}`]);
1462
+ return true;
1463
+ }
1464
+ catch (err) {
1465
+ if (err.exitCode === 2)
1466
+ return false;
1467
+ throw err;
1468
+ }
1469
+ }
1470
+ /** Whether origin has ANY branch — an empty repository has none. */
1471
+ async remoteHasAnyBranch(cwd) {
1472
+ const { stdout } = await this.git(cwd, ['ls-remote', '--heads', 'origin']);
1473
+ return stdout.trim().length > 0;
1474
+ }
1475
+ /** `git rev-parse --verify` that answers null for a rev that does not resolve (an unborn HEAD). */
1476
+ async revParseOrNull(cwd, rev) {
1477
+ try {
1478
+ const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', rev]);
1479
+ return stdout.trim() || null;
1480
+ }
1481
+ catch (err) {
1482
+ // Null is "no such rev"; a deadline is not that answer (see GitRunError).
1483
+ if (isGitTimeout(err))
1484
+ throw err;
1485
+ return null;
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Refresh `refs/remotes/origin/<branch>` for a sync, naming the one failure
1490
+ * that is not "origin unreachable": the branch no longer exists there.
1491
+ */
1492
+ async refreshRemoteBranchRefForSync(cwd, branch) {
1493
+ try {
1494
+ return await this.refreshRemoteBranchRef(cwd, branch);
1495
+ }
1496
+ catch (err) {
1497
+ const msg = err instanceof Error ? err.message : String(err);
1498
+ if (isMissingRemoteBranchFailure(msg))
1499
+ throw new RemoteBranchGoneError(branch);
1500
+ throw err;
1501
+ }
1502
+ }
1503
+ /**
1504
+ * Rebase the checked-out branch onto `remoteRef`, turning a content
1505
+ * conflict into the typed error the workflow layer queues recovery for.
1506
+ * Shared by `pull` and `syncFromRemote` so the two cannot drift.
1507
+ */
1508
+ async rebaseOntoRemote(cwd, branch, remoteRef) {
1509
+ // An UNBORN HEAD (a clone of an upstream that was empty when cloned) has
1510
+ // no commit to replay and `git rebase` refuses to start from it, so the
1511
+ // branch is moved to the remote commit instead. That is only safe while
1512
+ // the index is EMPTY: files staged in an unborn clone are tracked, and a
1513
+ // hard reset would destroy them where the normal path autostashes — and
1514
+ // `git stash` cannot run without an initial commit. So a non-empty index
1515
+ // is refused as a conflict, naming the staged paths, and takes the same
1516
+ // recovery path a rebase conflict does; nothing on disk is touched.
1517
+ if ((await this.revParseOrNull(cwd, 'HEAD')) === null) {
1518
+ // `-z`: NUL-delimited and unquoted. Line output quotes a path with a
1519
+ // non-ASCII or control character ("Entw\303\274rfe.md"), and that
1520
+ // spelling would name a file that does not exist to whoever recovers.
1521
+ const { stdout: stagedOut } = await this.git(cwd, ['ls-files', '--cached', '-z']);
1522
+ const staged = stagedOut.split('\0').filter(Boolean);
1523
+ if (staged.length > 0) {
1524
+ throw new PullRebaseConflictError(branch, staged, 'The clone has files staged before its first commit; they cannot be replayed onto the remote branch automatically.');
1525
+ }
1526
+ await this.git(cwd, ['reset', '--hard', remoteRef]);
1527
+ return;
1528
+ }
1529
+ try {
1530
+ // `--autostash` is load-bearing for the cooperative push recovery.
1531
+ // Under save=share the working tree is *supposed* to stay clean, but
1532
+ // in practice it can carry modified tracked artifacts (e.g. the
1533
+ // validator regenerates a dashboard HTML on every commit) that the
1534
+ // single-file commit didn't pick up. A rebase without it aborts
1535
+ // outright on those — "cannot rebase: You have unstaged
1536
+ // changes" — which is exactly the failure that strands a diverged
1537
+ // branch in an unrecoverable loop (the retry push stays non-fast-
1538
+ // forward forever). Autostash stashes those modifications, rebases
1539
+ // the local commits onto origin, then reapplies them, so the retry
1540
+ // push can fast-forward. Untracked files don't block rebase and are
1541
+ // left untouched.
1542
+ await this.git(cwd, ['rebase', '--autostash', remoteRef]);
1543
+ }
1544
+ catch (err) {
1545
+ // Capture the contested paths BEFORE the abort wipes the rebase
1546
+ // state — `--diff-filter=U` lists exactly the files whose replay
1547
+ // conflicted. Best-effort: an empty list just means this wasn't a
1548
+ // content conflict (or the probe itself failed) and the raw error
1549
+ // is surfaced unchanged.
1550
+ let conflictedPaths = [];
1551
+ try {
1552
+ const { stdout } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
1553
+ conflictedPaths = stdout.split('\n').map((s) => s.trim()).filter(Boolean);
1554
+ }
1555
+ catch {
1556
+ // fall through with an empty list
1557
+ }
1558
+ // A failed rebase leaves the repo in a "REBASE_HEAD" / detached-apply state,
1559
+ // which makes every subsequent git command behave unexpectedly. Return the
1560
+ // working tree to a clean state before surfacing the original error.
1561
+ let abortSucceeded = true;
1562
+ await this.git(cwd, ['rebase', '--abort']).catch(() => {
1563
+ abortSucceeded = false;
1564
+ });
1565
+ if (abortSucceeded && conflictedPaths.length > 0) {
1566
+ // Typed so the workflow layer can queue background recovery — this
1567
+ // divergence never resolves on its own (every retry pull hits the
1568
+ // same conflict) and the unpushed local commits are someone's saved
1569
+ // content stuck violating save=share.
1570
+ //
1571
+ // Gated on the abort succeeding: a failed abort means either no
1572
+ // rebase was ever active (the unmerged paths pre-date this pull —
1573
+ // some earlier operation left the index broken) or the clone is in
1574
+ // a state git itself can't unwind. Queueing recovery there would
1575
+ // point the worker's commitFile at a repo mid-conflict, risking a
1576
+ // commit full of conflict markers — surface the raw error instead
1577
+ // and leave diagnosis to a human.
1578
+ throw new PullRebaseConflictError(branch, conflictedPaths, err instanceof Error ? err.message : String(err));
1579
+ }
1580
+ throw err;
1581
+ }
1582
+ // `--autostash` reapplies the stashed edits AFTER the rebase, and when that
1583
+ // reapply conflicts git exits 0: the clone is left with an unmerged index,
1584
+ // the edits kept in `stash@{0}`, and nothing above has thrown. Read the
1585
+ // index rather than the exit status. The rebase itself landed, so the
1586
+ // branch is at the remote commit; resetting to it drops only the
1587
+ // half-applied stash (still in the stash list) and lets the conflict
1588
+ // travel the same typed path a rebase conflict does.
1589
+ const { stdout: unmergedOut } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
1590
+ const unmerged = unmergedOut.split('\n').map((l) => l.trim()).filter(Boolean);
1591
+ if (unmerged.length > 0) {
1592
+ await this.git(cwd, ['reset', '--hard', 'HEAD']);
1593
+ throw new PullRebaseConflictError(branch, unmerged, 'Reapplying the autostashed working-tree edits conflicted; the edits are kept in git stash.');
1594
+ }
1595
+ }
1596
+ /**
1597
+ * Repo-relative paths whose content differs between two commits. Rename-
1598
+ * aware: a renamed file reports both its old and new path, so a viewer
1599
+ * holding the OLD path learns it is gone. Caller holds the mutex.
1600
+ */
1601
+ async changedPathsBetween(cwd, fromSha, toSha) {
1602
+ const { stdout } = await this.git(cwd, [
1603
+ 'diff', '--name-status', '-M', '--no-color', '-z', fromSha, toSha,
1604
+ ]);
1605
+ const fields = stdout.split('\0');
1606
+ const paths = new Set();
1607
+ for (let i = 0; i < fields.length;) {
1608
+ const status = fields[i];
1609
+ if (!status)
1610
+ break;
1611
+ if (status.startsWith('R') || status.startsWith('C')) {
1612
+ if (fields[i + 1])
1613
+ paths.add(fields[i + 1]);
1614
+ if (fields[i + 2])
1615
+ paths.add(fields[i + 2]);
1616
+ i += 3;
1617
+ }
1618
+ else {
1619
+ if (fields[i + 1])
1620
+ paths.add(fields[i + 1]);
1621
+ i += 2;
1622
+ }
1623
+ }
1624
+ return [...paths];
1625
+ }
1626
+ async syncFromRemote(workspaceId) {
1627
+ // ONE mutex section for everything the sync reports about this clone.
1628
+ // Bracketing `pull` with separate HEAD reads and a diff took the hold
1629
+ // four times, and a save landing between any two of them was announced
1630
+ // back to its author as a "Git sync" change with the wrong sha.
1373
1631
  return this.mutex.run(workspaceId, async () => {
1374
1632
  const cwd = await this.repoDir(workspaceId);
1375
- const branch = await this.currentBranch(cwd);
1633
+ const branch = await this.checkedOutBranch(cwd);
1634
+ const before = await this.revParseOrNull(cwd, 'HEAD');
1635
+ const treeBefore = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
1636
+ let remoteRef;
1376
1637
  try {
1377
- // Refresh WITHOUT `git pull`, and without touching `FETCH_HEAD` — see
1378
- // `refreshRemoteBranchRef` for why both halves are load-bearing.
1379
- //
1380
- // `--autostash` is load-bearing for the cooperative push recovery.
1381
- // Under save=share the working tree is *supposed* to stay clean, but
1382
- // in practice it can carry modified tracked artifacts (e.g. the
1383
- // validator regenerates a dashboard HTML on every commit) that the
1384
- // single-file commit didn't pick up. A rebase without it aborts
1385
- // outright on those — "cannot rebase: You have unstaged
1386
- // changes" — which is exactly the failure that strands a diverged
1387
- // branch in an unrecoverable loop (the retry push stays non-fast-
1388
- // forward forever). Autostash stashes those modifications, rebases
1389
- // the local commits onto origin, then reapplies them, so the retry
1390
- // push can fast-forward. Untracked files don't block rebase and are
1391
- // left untouched.
1392
- const remoteRef = await this.refreshRemoteBranchRef(cwd, branch);
1393
- await this.git(cwd, ['rebase', '--autostash', remoteRef]);
1638
+ remoteRef = await this.refreshRemoteBranchRefForSync(cwd, branch);
1394
1639
  }
1395
1640
  catch (err) {
1396
- // Capture the contested paths BEFORE the abort wipes the rebase
1397
- // state — `--diff-filter=U` lists exactly the files whose replay
1398
- // conflicted. Best-effort: an empty list just means this wasn't a
1399
- // content conflict (or the probe itself failed) and the raw error
1400
- // is surfaced unchanged.
1401
- let conflictedPaths = [];
1402
- try {
1403
- const { stdout } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
1404
- conflictedPaths = stdout.split('\n').map((s) => s.trim()).filter(Boolean);
1405
- }
1406
- catch {
1407
- // fall through with an empty list
1408
- }
1409
- // A failed rebase leaves the repo in a "REBASE_HEAD" / detached-apply state,
1410
- // which makes every subsequent git command behave unexpectedly. Return the
1411
- // working tree to a clean state before surfacing the original error.
1412
- let abortSucceeded = true;
1413
- await this.git(cwd, ['rebase', '--abort']).catch(() => {
1414
- abortSucceeded = false;
1415
- });
1416
- if (abortSucceeded && conflictedPaths.length > 0) {
1417
- // Typed so the workflow layer can queue background recovery — this
1418
- // divergence never resolves on its own (every retry pull hits the
1419
- // same conflict) and the unpushed local commits are someone's saved
1420
- // content stuck violating save=share.
1421
- //
1422
- // Gated on the abort succeeding: a failed abort means either no
1423
- // rebase was ever active (the unmerged paths pre-date this pull —
1424
- // some earlier operation left the index broken) or the clone is in
1425
- // a state git itself can't unwind. Queueing recovery there would
1426
- // point the worker's commitFile at a repo mid-conflict, risking a
1427
- // commit full of conflict markers — surface the raw error instead
1428
- // and leave diagnosis to a human.
1429
- throw new PullRebaseConflictError(branch, conflictedPaths, err instanceof Error ? err.message : String(err));
1641
+ // An unborn clone whose branch origin does not have either: on a FRESH
1642
+ // deployment (origin has no branch at all) nobody has pushed yet —
1643
+ // nothing to sync, nothing to retire. But an origin that does have
1644
+ // branches and lacks this one has had this branch created and deleted
1645
+ // since the clone was made, and the clone is as stale as any other.
1646
+ if (err instanceof RemoteBranchGoneError && before === null && !(await this.remoteHasAnyBranch(cwd))) {
1647
+ return { before: null, after: null, treeChanged: false, changedPaths: [] };
1430
1648
  }
1431
1649
  throw err;
1432
1650
  }
1651
+ await this.rebaseOntoRemote(cwd, branch, remoteRef);
1433
1652
  this.accessControl?.invalidate(workspaceId);
1653
+ const after = await this.revParseOrNull(cwd, 'HEAD');
1654
+ const treeAfter = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
1655
+ const treeChanged = treeAfter !== treeBefore;
1656
+ const changedPaths = treeChanged && after
1657
+ ? await this.changedPathsBetween(cwd, before ?? GitService.EMPTY_TREE_SHA, after)
1658
+ : [];
1659
+ return { before, after, treeChanged, changedPaths };
1660
+ });
1661
+ }
1662
+ async pull(workspaceId) {
1663
+ return this.mutex.run(workspaceId, async () => {
1664
+ const cwd = await this.repoDir(workspaceId);
1665
+ const branch = await this.checkedOutBranch(cwd);
1666
+ // For `treeChanged` (see IGitService.pull): TREE ids, not commit ids —
1667
+ // a pull that lands only content-identical commits (an empty commit)
1668
+ // must not read as a content change. Tolerant of an unborn HEAD (a
1669
+ // clone of an empty upstream): "" before + a tree after correctly
1670
+ // reads as changed, "" both sides as not.
1671
+ const treeBefore = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
1672
+ // Refresh WITHOUT `git pull`, and without touching `FETCH_HEAD` — see
1673
+ // `refreshRemoteBranchRef` for why both halves are load-bearing.
1674
+ const remoteRef = await this.refreshRemoteBranchRef(cwd, branch);
1675
+ await this.rebaseOntoRemote(cwd, branch, remoteRef);
1676
+ this.accessControl?.invalidate(workspaceId);
1677
+ const treeAfter = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
1678
+ return { treeChanged: treeAfter !== treeBefore };
1434
1679
  });
1435
1680
  }
1436
1681
  /**
@@ -1453,18 +1698,97 @@ export class GitService {
1453
1698
  * only ever see settled states.
1454
1699
  */
1455
1700
  async hasUnpushedCommits(workspaceId) {
1456
- return this.mutex.run(workspaceId, async () => {
1457
- const cwd = await this.repoDir(workspaceId);
1458
- const branch = await this.currentBranch(cwd);
1701
+ return this.mutex.run(workspaceId, async () => this.hasUnpushedCommitsAt(await this.repoDir(workspaceId)));
1702
+ }
1703
+ /**
1704
+ * `hasUnpushedCommits` without the workspace reservation — for a caller that
1705
+ * already holds one (`mergeChangeRequest`, which has to ask inside the same
1706
+ * reservation as the reset that would discard the answer). Fails closed: a
1707
+ * question that cannot be answered counts as "yes, unpushed".
1708
+ */
1709
+ async hasUnpushedCommitsAt(cwd) {
1710
+ const branch = await this.currentBranch(cwd);
1711
+ try {
1712
+ const { stdout } = await this.git(cwd, [
1713
+ 'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
1714
+ ]);
1715
+ return Number(stdout.trim()) > 0;
1716
+ }
1717
+ catch {
1718
+ return true;
1719
+ }
1720
+ }
1721
+ /**
1722
+ * Revert paths in several workspaces and publish the result as ONE git
1723
+ * operation. Every workspace involved is reserved together (`runAll`), and
1724
+ * reading each HEAD, restoring and committing every path, pushing each
1725
+ * branch and — when a step fails — undoing, all happen inside that single
1726
+ * reservation: no other git operation on those workspaces can land between
1727
+ * the HEAD an undo returns to and the commits it undoes.
1728
+ *
1729
+ * - Each workspace's HEAD is read first; one that can't be read throws
1730
+ * before anything changes.
1731
+ * - Every path in every workspace is restored from its `ref` and committed
1732
+ * (`subject`) before anything is pushed. A failed restore or commit
1733
+ * undoes all of them and rethrows: nothing is pushed.
1734
+ * - Branches are then pushed in order. A failed push undoes that workspace
1735
+ * and every later one and is reported in `failed`; the earlier pushes
1736
+ * stand, since a remote can't be un-pushed.
1737
+ *
1738
+ * An undo never rewrites history: each touched path is restored from the
1739
+ * HEAD recorded at the start and committed (`undoSubject`), so unpushed work
1740
+ * the checkout already had stays exactly as it was. Each path is undone on
1741
+ * its own, so one that fails doesn't leave the rest reverted.
1742
+ */
1743
+ async revertPathsAndPush(user, plans) {
1744
+ assertValidAuthor(user);
1745
+ for (const plan of plans)
1746
+ for (const entry of plan.paths)
1747
+ assertValidRelativePath(entry.path);
1748
+ return this.mutex.runAll(plans.map((plan) => plan.workspaceId), async () => {
1749
+ const steps = [];
1750
+ for (const plan of plans) {
1751
+ const cwd = await this.repoDir(plan.workspaceId);
1752
+ const { stdout } = await this.git(cwd, ['rev-parse', '--verify', 'HEAD^{commit}']);
1753
+ steps.push({ workspaceId: plan.workspaceId, cwd, head: stdout.trim(), paths: plan.paths });
1754
+ }
1755
+ const undo = async (unpushed) => {
1756
+ for (const step of unpushed) {
1757
+ for (const entry of step.paths) {
1758
+ try {
1759
+ await this.restorePathUnlocked(step.cwd, step.head, entry.path);
1760
+ await this.commitFileUnlocked(step.workspaceId, step.cwd, user, entry.path, entry.path, entry.undoSubject, true);
1761
+ }
1762
+ catch (err) {
1763
+ log.warn(`undo of ${printable(entry.path)} failed in workspace ${printable(step.workspaceId)}: ${printable(sanitizeError(err))}`);
1764
+ }
1765
+ }
1766
+ }
1767
+ };
1459
1768
  try {
1460
- const { stdout } = await this.git(cwd, [
1461
- 'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
1462
- ]);
1463
- return Number(stdout.trim()) > 0;
1769
+ for (const step of steps) {
1770
+ for (const entry of step.paths) {
1771
+ await this.restorePathUnlocked(step.cwd, entry.ref, entry.path);
1772
+ await this.commitFileUnlocked(step.workspaceId, step.cwd, user, entry.path, entry.path, entry.subject, true);
1773
+ }
1774
+ }
1464
1775
  }
1465
- catch {
1466
- return true;
1776
+ catch (err) {
1777
+ await undo(steps);
1778
+ throw err;
1467
1779
  }
1780
+ const pushed = [];
1781
+ for (const [i, step] of steps.entries()) {
1782
+ try {
1783
+ await this.pushUnlocked(step.workspaceId, step.cwd, user);
1784
+ }
1785
+ catch (error) {
1786
+ await undo(steps.slice(i));
1787
+ return { pushed, failed: { workspaceId: step.workspaceId, error } };
1788
+ }
1789
+ pushed.push(step.workspaceId);
1790
+ }
1791
+ return { pushed, failed: null };
1468
1792
  });
1469
1793
  }
1470
1794
  /**
@@ -1814,8 +2138,8 @@ export class GitService {
1814
2138
  const cwd = await this.repoDir(workspaceId);
1815
2139
  await this.fetchPrRefs(cwd, baseBranch, headBranch);
1816
2140
  return this.mutex.run(workspaceId, async () => {
1817
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
1818
- const headRef = await this.resolveBranchRef(cwd, headBranch);
2141
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2142
+ const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
1819
2143
  const [{ stdout: baseSha }, { stdout: headSha }] = await Promise.all([
1820
2144
  this.git(cwd, ['rev-parse', baseRef]),
1821
2145
  this.git(cwd, ['rev-parse', headRef]),
@@ -1859,8 +2183,8 @@ export class GitService {
1859
2183
  if (!opts.at)
1860
2184
  await this.fetchPrRefs(cwd, baseBranch, headBranch);
1861
2185
  return this.mutex.run(workspaceId, async () => {
1862
- const baseRef = opts.at ? opts.at.baseSha : await this.resolveBranchRef(cwd, baseBranch);
1863
- const headRef = opts.at ? opts.at.headSha : await this.resolveBranchRef(cwd, headBranch);
2186
+ const baseRef = opts.at ? opts.at.baseSha : await this.resolvePublishedBranchRef(cwd, baseBranch);
2187
+ const headRef = opts.at ? opts.at.headSha : await this.resolvePublishedBranchRef(cwd, headBranch);
1864
2188
  const range = `${baseRef}...${headRef}`; // three-dot = changes on head since merge-base
1865
2189
  const [{ stdout: nameStatusOut }, { stdout: numstatOut }] = await Promise.all([
1866
2190
  this.git(cwd, ['diff', '-M', '-z', '--name-status', range]),
@@ -1875,8 +2199,17 @@ export class GitService {
1875
2199
  // land. Filter it from the review surface entirely; the neutralisation
1876
2200
  // reads the raw refs itself and is unaffected. Both lists are filtered
1877
2201
  // IN STEP so the index-zip below stays aligned.
1878
- if (statuses.some((s) => s.path === 'roles.yaml')) {
1879
- const keep = statuses.map((s) => s.path !== 'roles.yaml');
2202
+ // The empty-folder placeholder is filtered the same way: it is never
2203
+ // content, so it is not a file to review or approve. It still merges
2204
+ // with the rest, and `changedPathsForPr` keeps it, so a request that
2205
+ // only creates a folder is not mistaken for an empty one and closed.
2206
+ // An empty file deleted as its folder gets the (equally empty)
2207
+ // placeholder reads to `-M` as a RENAME onto it; the real side of such
2208
+ // a pair is first made the plain removal (or addition) it is, so
2209
+ // dropping the placeholder never drops the file with it.
2210
+ statuses = statuses.map(withoutPlaceholderRename);
2211
+ if (statuses.some((s) => s.path === 'roles.yaml' || isFolderPlaceholder(s.path))) {
2212
+ const keep = statuses.map((s) => s.path !== 'roles.yaml' && !isFolderPlaceholder(s.path));
1880
2213
  statuses = statuses.filter((_, i) => keep[i]);
1881
2214
  if (counts.length === keep.length)
1882
2215
  counts = counts.filter((_, i) => keep[i]);
@@ -1888,7 +2221,7 @@ export class GitService {
1888
2221
  // +/- to a file.
1889
2222
  const aligned = counts.length === statuses.length;
1890
2223
  if (!aligned) {
1891
- console.warn(`[cr] diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
2224
+ crLog.warn(`diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
1892
2225
  `for ${range} — reporting file list without +/- counts`);
1893
2226
  }
1894
2227
  const files = statuses.map((s, i) => {
@@ -1922,29 +2255,77 @@ export class GitService {
1922
2255
  * Just the repo-relative paths changed by a change request (three-dot), no
1923
2256
  * patches — the cheap version used to build CR-list summaries and owner
1924
2257
  * routing. `changedFilesForPr` is the full version with statuses + diffs.
2258
+ *
2259
+ * `fetch: false` skips the per-request ref fetch, for a caller that has
2260
+ * ALREADY refreshed the clone's remote-tracking refs — which is the whole
2261
+ * clone in one round trip, against one per request here. A CR list is
2262
+ * exactly that caller: at five open requests the per-request fetch was
2263
+ * measured at ~0.55s each, and the list is re-read on every proposal and
2264
+ * every 60s poll. Only pass it when a fetch of the clone really has just
2265
+ * happened; the refs are otherwise as old as the last one, and the diff
2266
+ * would describe a stale head.
2267
+ *
2268
+ * It is a skip, not a promise never to fetch: a branch this clone has
2269
+ * never heard of is fetched anyway. That case is not a stale diff, it is
2270
+ * NO diff — an empty touched-path set, which is a change request missing
2271
+ * from its own author's tree. Two local `rev-parse`s are worth not being
2272
+ * that.
1925
2273
  */
1926
- async changedPathsForPr(workspaceId, baseBranch, headBranch) {
2274
+ async changedPathsForPr(workspaceId, baseBranch, headBranch, opts = {}) {
1927
2275
  assertValidBranchName(baseBranch);
1928
2276
  assertValidBranchName(headBranch);
1929
2277
  // Fetch outside the mutex (network round-trip) so origin latency can't hold
1930
2278
  // the workspace lock; the lock guards only the local diff below.
1931
2279
  const cwd = await this.repoDir(workspaceId);
1932
- await this.fetchPrRefs(cwd, baseBranch, headBranch);
2280
+ // The skip decision and the diff are ONE reading of the refs, not two.
2281
+ // Both are resolved here, to COMMITS, and the commits are what the diff
2282
+ // below runs on — the same pinning `changedFilesForPr`'s `at` does. A
2283
+ // concurrent `fetch --prune origin` (`ensureRemotesFetched` and
2284
+ // `fetchPrRefs` both run outside this mutex, deliberately) can drop a
2285
+ // remote-tracking ref between the two, and re-resolving inside the mutex
2286
+ // would then throw `WorkflowValidationError` — which `touchedPathsFor`
2287
+ // swallows into an empty touched-path set, i.e. exactly the change
2288
+ // request missing from its own author's tree that the skip is guarded
2289
+ // against. The commits stay readable whatever happens to the ref names.
2290
+ const pinned = opts.fetch === false ? await this.publishedPrCommits(cwd, baseBranch, headBranch) : null;
2291
+ if (!pinned) {
2292
+ await this.fetchPrRefs(cwd, baseBranch, headBranch);
2293
+ }
1933
2294
  return this.mutex.run(workspaceId, async () => {
1934
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
1935
- const headRef = await this.resolveBranchRef(cwd, headBranch);
1936
- const { stdout } = await this.git(cwd, [
1937
- 'diff', '-M', '--name-only', `${baseRef}...${headRef}`,
1938
- ]);
1939
- return (stdout
1940
- .split('\n')
1941
- .map((s) => s.trim())
1942
- .filter(Boolean)
2295
+ const baseRef = pinned ? pinned.base : await this.resolvePublishedBranchRef(cwd, baseBranch);
2296
+ const headRef = pinned ? pinned.head : await this.resolvePublishedBranchRef(cwd, headBranch);
2297
+ return this.prChangedPathsAt(cwd, baseRef, headRef, opts);
2298
+ });
2299
+ }
2300
+ /**
2301
+ * The shaping behind `changedPathsForPr`, without its workspace
2302
+ * reservation: the caller must already hold one. `mergeChangeRequest` reads
2303
+ * the same set inside its OWN reservation, where re-entering this one would
2304
+ * deadlock — and where the refs are the ones the merge is actually built on.
2305
+ */
2306
+ async prChangedPathsAt(cwd, baseRef, headRef, opts = {}) {
2307
+ const { stdout } = await this.git(cwd, [
2308
+ 'diff', '-M', '-z', '--name-status', `${baseRef}...${headRef}`,
2309
+ ]);
2310
+ // Deduped: both sides of a rename can collide with another entry's path.
2311
+ return [
2312
+ ...new Set(parseNameStatusZ(stdout)
2313
+ // A rename onto or off the placeholder is a real file removed or
2314
+ // added (see `withoutPlaceholderRename`): both of its paths are
2315
+ // touched, or filtering the placeholder would lose the file.
2316
+ //
2317
+ // Authorizing the change needs both sides of EVERY rename, not just
2318
+ // the placeholder's: git reports a rename under its new name alone,
2319
+ // and the old name is a path the change deletes.
2320
+ .flatMap((s) => s.previousPath &&
2321
+ (opts.forAccessCheck || isFolderPlaceholder(s.path) || isFolderPlaceholder(s.previousPath))
2322
+ ? [s.previousPath, s.path]
2323
+ : [s.path])
1943
2324
  // Same rule as `changedFilesForPr`: a roles.yaml change never
1944
2325
  // survives a merge, so it is not a touched path for routing or
1945
2326
  // summaries either.
1946
- .filter((p) => p !== 'roles.yaml'));
1947
- });
2327
+ .filter((p) => opts.forAccessCheck || p !== 'roles.yaml')),
2328
+ ];
1948
2329
  }
1949
2330
  /**
1950
2331
  * The merge-base commit of a change request's two branches (their origin
@@ -1958,8 +2339,8 @@ export class GitService {
1958
2339
  const cwd = await this.repoDir(workspaceId);
1959
2340
  await this.fetchPrRefs(cwd, baseBranch, headBranch);
1960
2341
  return this.mutex.run(workspaceId, async () => {
1961
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
1962
- const headRef = await this.resolveBranchRef(cwd, headBranch);
2342
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2343
+ const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
1963
2344
  try {
1964
2345
  const { stdout } = await this.git(cwd, ['merge-base', baseRef, headRef]);
1965
2346
  return stdout.trim() || null;
@@ -1973,6 +2354,128 @@ export class GitService {
1973
2354
  }
1974
2355
  });
1975
2356
  }
2357
+ /**
2358
+ * Where a change request forked from its target, and whether the target has
2359
+ * moved on since — for the two commits a detail read has ALREADY resolved
2360
+ * (no fetch, no ref resolution, same contract as `changedFilesForPr`'s `at`).
2361
+ *
2362
+ * `mergeBaseSha` is the "before" every file in the request is read against,
2363
+ * so an edit made on the target after the proposal never shows as something
2364
+ * the proposal deletes. `behind` is true when the target holds commits the
2365
+ * proposal does not (`head..base` is non-empty) — the proposal needs
2366
+ * updating before its diff and its eventual merge describe the same text.
2367
+ * No shared history: `mergeBaseSha: null`, and `behind` stays honest (the
2368
+ * target certainly has commits the proposal lacks).
2369
+ */
2370
+ async forkPointForPr(workspaceId, at) {
2371
+ for (const sha of [at.baseSha, at.headSha]) {
2372
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2373
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2374
+ }
2375
+ }
2376
+ const cwd = await this.repoDir(workspaceId);
2377
+ return this.mutex.run(workspaceId, async () => {
2378
+ let mergeBaseSha = null;
2379
+ try {
2380
+ const { stdout } = await this.git(cwd, ['merge-base', at.baseSha, at.headSha]);
2381
+ mergeBaseSha = stdout.trim() || null;
2382
+ }
2383
+ catch (err) {
2384
+ // Exit 1 is git's "no common ancestor"; anything else is infra.
2385
+ if (err.exitCode !== 1)
2386
+ throw err;
2387
+ }
2388
+ const { stdout: count } = await this.git(cwd, [
2389
+ 'rev-list', '--count', `${at.headSha}..${at.baseSha}`,
2390
+ ]);
2391
+ return { mergeBaseSha, behind: Number.parseInt(count.trim(), 10) > 0 };
2392
+ });
2393
+ }
2394
+ /**
2395
+ * Every repo-relative path whose content differs between two commits on a
2396
+ * change request's own branch — what an update from the target actually
2397
+ * CHANGED about the proposal.
2398
+ *
2399
+ * Two-dot, because that is the question: not "what does this request
2400
+ * propose" (three-dot, against the target) but "what did the merge move
2401
+ * under the reviewers who already approved". `--no-renames` is what makes
2402
+ * the answer the conservative one: git detects renames BY DEFAULT
2403
+ * (`diff.renames` has been on since 2.9) and then reports only the new
2404
+ * path, which would leave an approval on the path a merge renamed AWAY
2405
+ * reading as untouched. Switched off, a rename reports as the old path
2406
+ * removed and the new one added, so an approval on either side is correctly
2407
+ * read as touched — a path that appears here loses its approval, and only a
2408
+ * path that does not appear keeps it.
2409
+ */
2410
+ async pathsChangedBetween(workspaceId, fromSha, toSha) {
2411
+ for (const sha of [fromSha, toSha]) {
2412
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2413
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2414
+ }
2415
+ }
2416
+ const cwd = await this.repoDir(workspaceId);
2417
+ return this.mutex.run(workspaceId, async () => {
2418
+ const { stdout } = await this.git(cwd, [
2419
+ 'diff', '--no-renames', '-z', '--name-only', fromSha, toSha,
2420
+ ]);
2421
+ // `-z` terminates each name with NUL, so the trailing field is empty —
2422
+ // and a tracked name may legally carry leading or trailing spaces, so
2423
+ // nothing is trimmed here.
2424
+ return [...new Set(stdout.split('\0').filter((name) => name !== ''))];
2425
+ });
2426
+ }
2427
+ /**
2428
+ * One file as it stood at a change request's fork point — the "before" side
2429
+ * of the request dialog's diff. `sha` must be a commit on the target
2430
+ * branch's published history (`origin/<baseBranch>`); anything else is
2431
+ * refused, so this can never become a read of an arbitrary commit. `null`
2432
+ * means the path did not exist at the fork point (the request adds it).
2433
+ */
2434
+ async readFileAtForkPoint(workspaceId, baseBranch, sha, relativePath) {
2435
+ assertValidBranchName(baseBranch);
2436
+ assertValidRelativePath(relativePath);
2437
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2438
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2439
+ }
2440
+ const repoRelativePath = this.stripRepoPrefix(relativePath);
2441
+ return this.mutex.run(workspaceId, async () => {
2442
+ const cwd = await this.repoDir(workspaceId);
2443
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2444
+ try {
2445
+ await this.git(cwd, ['merge-base', '--is-ancestor', sha, baseRef]);
2446
+ }
2447
+ catch (err) {
2448
+ if (err.exitCode === 1) {
2449
+ throw new WorkflowDomainError(`That commit is not part of "${baseBranch}" — it is not this request's fork point.`, 404);
2450
+ }
2451
+ throw err;
2452
+ }
2453
+ await this.assertNotTreeAtRef(cwd, sha, repoRelativePath, relativePath);
2454
+ return this.readFileAtRef(workspaceId, sha, repoRelativePath);
2455
+ });
2456
+ }
2457
+ /**
2458
+ * Whether `repoRelativePath` exists (as a file or tree) at `ref`. ONLY
2459
+ * git's own "that path is not at this ref" answer is false; an unresolvable
2460
+ * ref, a timeout, a locked or corrupt repository PROPAGATE — a revert that
2461
+ * read one of those as "absent" would decide wrongly what to restore.
2462
+ */
2463
+ async pathExistsAtRef(workspaceId, ref, repoRelativePath) {
2464
+ assertValidRelativePath(repoRelativePath);
2465
+ return this.mutex.run(workspaceId, async () => {
2466
+ const cwd = await this.repoDir(workspaceId);
2467
+ try {
2468
+ await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
2469
+ return true;
2470
+ }
2471
+ catch (err) {
2472
+ const stderr = err.stderr ?? (err instanceof Error ? err.message : String(err));
2473
+ if (/does not exist in|exists on disk, but not in/.test(stderr))
2474
+ return false;
2475
+ throw err;
2476
+ }
2477
+ });
2478
+ }
1976
2479
  /**
1977
2480
  * Restore one path in the working tree (and index) to its content at `ref`;
1978
2481
  * a path ABSENT at `ref` is deleted — the revert of an added file is its
@@ -1982,8 +2485,11 @@ export class GitService {
1982
2485
  */
1983
2486
  async restorePathFromRef(workspaceId, ref, repoRelativePath) {
1984
2487
  assertValidRelativePath(repoRelativePath);
1985
- return this.mutex.run(workspaceId, async () => {
1986
- const cwd = await this.repoDir(workspaceId);
2488
+ return this.mutex.run(workspaceId, async () => this.restorePathUnlocked(await this.repoDir(workspaceId), ref, repoRelativePath));
2489
+ }
2490
+ /** `restorePathFromRef`'s body, for a caller that already holds the workspace's reservation. */
2491
+ async restorePathUnlocked(cwd, ref, repoRelativePath) {
2492
+ {
1987
2493
  let existsAtRef = true;
1988
2494
  try {
1989
2495
  await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
@@ -2007,7 +2513,7 @@ export class GitService {
2007
2513
  // missing from disk is exactly what `git add` records as deleted.
2008
2514
  await fs.rm(path.join(cwd, repoRelativePath), { force: true });
2009
2515
  }
2010
- });
2516
+ }
2011
2517
  }
2012
2518
  /** Fetch the two branches a CR spans so origin refs reflect the latest push. */
2013
2519
  async fetchPrRefs(cwd, baseBranch, headBranch) {
@@ -2069,6 +2575,63 @@ export class GitService {
2069
2575
  ? relativePath.slice(this.kbDirName.length + 1)
2070
2576
  : relativePath;
2071
2577
  }
2578
+ /**
2579
+ * The COMMITS both of a change request's branches point at in this clone
2580
+ * already, or null when either is unknown to it — the question behind
2581
+ * `changedPathsForPr`'s `fetch: false`, answered with the resolution itself
2582
+ * so the caller diffs precisely what it decided on. Only git's own "no such
2583
+ * ref" answers null; every other failure is the caller's to see, and
2584
+ * treating it as "not here" would spend a network fetch on a clone that is
2585
+ * broken for some other reason.
2586
+ */
2587
+ async publishedPrCommits(cwd, baseBranch, headBranch) {
2588
+ const shas = [];
2589
+ for (const branch of [baseBranch, headBranch]) {
2590
+ try {
2591
+ shas.push((await this.resolvePublishedBranch(cwd, branch)).sha);
2592
+ }
2593
+ catch (err) {
2594
+ if (err instanceof WorkflowValidationError)
2595
+ return null;
2596
+ throw err;
2597
+ }
2598
+ }
2599
+ return { base: shas[0], head: shas[1] };
2600
+ }
2601
+ /**
2602
+ * `resolveBranchRef` with the preference reversed: the PUBLISHED ref
2603
+ * (`origin/<branch>`, just fetched by `fetchPrRefs`) before the local head.
2604
+ * A change request is a pair of published branches, and a clone's local
2605
+ * copy of the OTHER branch is whatever it was when the clone last touched
2606
+ * it — in a proposal's own workspace the local target is typically the
2607
+ * target as of the fork. Reading that made every request look up to date
2608
+ * and diffed it against a stale target. A local-only branch still resolves.
2609
+ */
2610
+ async resolvePublishedBranchRef(cwd, branch) {
2611
+ return (await this.resolvePublishedBranch(cwd, branch)).ref;
2612
+ }
2613
+ /**
2614
+ * {@link resolvePublishedBranchRef} with the commit it resolved to — the
2615
+ * `rev-parse` that verifies the ref already prints it, so pinning a diff to
2616
+ * the commit costs nothing over naming the ref.
2617
+ */
2618
+ async resolvePublishedBranch(cwd, branch) {
2619
+ for (const ref of [`refs/remotes/origin/${branch}`, `refs/heads/${branch}`]) {
2620
+ try {
2621
+ const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', ref]);
2622
+ return { ref, sha: stdout.trim() };
2623
+ }
2624
+ catch (err) {
2625
+ // Only git's own "no such ref" (exit 1 under --quiet) moves on to the
2626
+ // next candidate. A deadline or any other failure is not that answer:
2627
+ // skipping past it would pick the stale local copy, or call a real
2628
+ // branch unknown.
2629
+ if (!(err instanceof GitRunError) || err.timedOut || err.exitCode !== 1)
2630
+ throw err;
2631
+ }
2632
+ }
2633
+ throw new WorkflowValidationError(`unknown branch: ${branch}`);
2634
+ }
2072
2635
  /**
2073
2636
  * Resolve a branch name to a concrete ref the local clone knows about.
2074
2637
  * Tries the local head first, falls back to the matching remote-tracking
@@ -2118,12 +2681,12 @@ export class GitService {
2118
2681
  }
2119
2682
  const dirtyList = porcelain.stdout.trim().replace(/\s+/g, ' ');
2120
2683
  if (queueExplainsIt) {
2121
- console.log(`[git] workspace=${workspaceId} branch=${branch} working tree is dirty while ` +
2684
+ log.info(`workspace=${workspaceId} branch=${branch} working tree is dirty while ` +
2122
2685
  `pending commits drain (expected — the background worker is catching up). ` +
2123
2686
  `Files: ${dirtyList}`);
2124
2687
  }
2125
2688
  else {
2126
- console.warn(`[git] workspace=${workspaceId} branch=${branch} has a non-clean working ` +
2689
+ log.warn(`workspace=${workspaceId} branch=${branch} has a non-clean working ` +
2127
2690
  `tree under save=share with NO queued pending commits — this should never ` +
2128
2691
  `happen and likely indicates a missed lock-release commit. Files: ${dirtyList}`);
2129
2692
  }
@@ -2246,6 +2809,34 @@ export class GitService {
2246
2809
  throw err;
2247
2810
  }
2248
2811
  }
2812
+ /**
2813
+ * The commits on `ref` that touched `repoRelativePath`, newest first, at
2814
+ * most `limit` of them. What a skill's `version` is looked up through: each
2815
+ * commit is a copy of the file that may have declared a version. Renames are
2816
+ * not followed — a moved skill's earlier history is the old path's.
2817
+ */
2818
+ async pathHistory(workspaceId, ref, repoRelativePath, limit) {
2819
+ const cwd = await this.repoDir(workspaceId);
2820
+ const { stdout } = await this.git(cwd, [
2821
+ 'rev-list',
2822
+ `--max-count=${Math.max(1, Math.trunc(limit))}`,
2823
+ ref,
2824
+ '--',
2825
+ repoRelativePath,
2826
+ ]);
2827
+ return stdout.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
2828
+ }
2829
+ /**
2830
+ * Every file under `folder` at `ref`, repo-root-relative and in tree order.
2831
+ * A folder that does not exist at that ref lists nothing. NUL-delimited, so
2832
+ * a non-ASCII name comes back as itself rather than C-quoted, and one with
2833
+ * a leading or trailing space keeps it.
2834
+ */
2835
+ async listFilesAtRef(workspaceId, ref, folder) {
2836
+ const cwd = await this.repoDir(workspaceId);
2837
+ const { stdout } = await this.git(cwd, ['ls-tree', '-r', '-z', '--name-only', ref, '--', folder]);
2838
+ return stdout.split('\0').filter((line) => line.length > 0);
2839
+ }
2249
2840
  /**
2250
2841
  * A clean `git status` for a path has two honest readings (already
2251
2842
  * committed; a queued re-apply of something that landed) and one dishonest
@@ -2287,58 +2878,32 @@ export class GitService {
2287
2878
  ]);
2288
2879
  return stdout.trim().length > 0;
2289
2880
  }
2290
- async git(cwd, args, opts) {
2291
- try {
2292
- const pending = execFileAsync('git', args, {
2293
- cwd,
2294
- // `GIT_LITERAL_PATHSPECS=1` makes git treat every pathspec literally
2295
- // instead of interpreting `[`, `]`, `*`, `?`, `!`, or `:(magic)` as
2296
- // glob / magic syntax. KB files routinely arrive with bracketed
2297
- // prefixes like `[Approved] foo.docx` or `[Updated 2025] bar.md`; the
2298
- // upload pipeline (`writeFileBinary` + `releaseLock` + `commitFile`)
2299
- // passes the relative path straight through to `git add` / `git
2300
- // checkout -- <path>`, which would otherwise glob and either match the
2301
- // wrong file or no file at all. Setting it once here covers every git
2302
- // subprocess this service spawns.
2303
- //
2304
- // `LC_ALL=C` / `LANG=C` force git's human-readable output (including
2305
- // stderr) to stable, English, locale-independent text. Callers that
2306
- // classify errors by message — e.g. `readFileAtRef` distinguishing a
2307
- // "path does not exist in <ref>" absence from a hard failure — would
2308
- // otherwise misread a translated message on a non-English host and, for
2309
- // the fail-closed roles.yaml preservation path, fail OPEN.
2310
- env: { ...process.env, GIT_LITERAL_PATHSPECS: '1', LC_ALL: 'C', LANG: 'C' },
2311
- maxBuffer: 32 * 1024 * 1024,
2312
- });
2313
- if (opts?.input !== undefined && pending.child.stdin) {
2314
- // A dying git can close stdin mid-write; the promise below still
2315
- // rejects with the exit code, which is the error we want to surface.
2316
- pending.child.stdin.on('error', () => undefined);
2317
- pending.child.stdin.write(opts.input);
2318
- pending.child.stdin.end();
2319
- }
2320
- const { stdout, stderr } = await pending;
2321
- return { stdout: stdout.toString(), stderr: stderr.toString() };
2322
- }
2323
- catch (err) {
2324
- const msg = err instanceof Error ? err.message : String(err);
2325
- // Skip past any leading `-c key=val` pairs so the error names the actual
2326
- // git subcommand that failed (e.g. "git fetch failed:" not "git -c failed:").
2327
- let i = 0;
2328
- while (i < args.length && args[i] === '-c')
2329
- i += 2;
2330
- const subcommand = args[i] ?? args[0];
2331
- const wrapped = new Error(`git ${subcommand} failed: ${redact(msg)}`);
2332
- // Preserve the underlying exit code / stderr so callers can distinguish
2333
- // expected non-zero exits (e.g. merge-base exit 1 = no common ancestor)
2334
- // from infra failures (ENOENT, exit 128) without parsing message strings.
2335
- const original = err;
2336
- if (typeof original.code === 'number')
2337
- wrapped.exitCode = original.code;
2338
- if (typeof original.stderr === 'string')
2339
- wrapped.stderr = redact(original.stderr);
2340
- throw wrapped;
2341
- }
2881
+ /**
2882
+ * Every git invocation this service makes goes through here, and from here
2883
+ * through the injected runner — which is what puts a deadline on it. The
2884
+ * environment, the buffer ceiling, the stdin handling and the error shape all
2885
+ * live in `NodeGitRunner` now; see `shared/git.contract.ts` for why.
2886
+ *
2887
+ * Errors arrive as `GitRunError`, which still carries `exitCode` and
2888
+ * `stderr`, so the callers that read those to tell an expected non-zero exit
2889
+ * from a real failure are unaffected.
2890
+ */
2891
+ git(cwd, args, opts) {
2892
+ return this.gitRunner.run(cwd, args, {
2893
+ ...opts,
2894
+ // `GIT_LITERAL_PATHSPECS=1` makes git treat every pathspec literally
2895
+ // instead of interpreting `[`, `]`, `*`, `?`, `!`, or `:(magic)` as glob
2896
+ // / magic syntax. KB files routinely arrive with bracketed prefixes like
2897
+ // `[Approved] foo.docx` or `[Updated 2025] bar.md`; the upload pipeline
2898
+ // (`writeFileBinary` + `releaseLock` + `commitFile`) passes the relative
2899
+ // path straight through to `git add` / `git checkout -- <path>`, which
2900
+ // would otherwise glob and either match the wrong file or no file at all.
2901
+ //
2902
+ // THIS service's setting, not the runner's: the startup runner spells its
2903
+ // literal paths as `:(literal)<path>`, which this variable would turn into
2904
+ // a search for a file literally named that.
2905
+ env: { GIT_LITERAL_PATHSPECS: '1', ...opts?.env },
2906
+ });
2342
2907
  }
2343
2908
  }
2344
2909
  /** Map a git status letter (`git diff --name-status`) to the DTO's `PrFileStatus`. */
@@ -2386,6 +2951,24 @@ export function parseNameStatusZ(out) {
2386
2951
  }
2387
2952
  return entries;
2388
2953
  }
2954
+ /**
2955
+ * Undo a rename git detected between a real file and the empty-folder
2956
+ * placeholder. Deleting a folder's last file writes the placeholder, and
2957
+ * when that file was empty too, `-M` pairs the two as a 100% rename. The
2958
+ * pair is really a removal (or, the other way round, an addition) plus the
2959
+ * placeholder, which is never reviewed — so the entry becomes the real side
2960
+ * alone, and the caller's placeholder filter has nothing of it to drop.
2961
+ */
2962
+ export function withoutPlaceholderRename(entry) {
2963
+ const { previousPath } = entry;
2964
+ if (!previousPath || entry.status !== 'renamed')
2965
+ return entry;
2966
+ const toPlaceholder = isFolderPlaceholder(entry.path);
2967
+ const fromPlaceholder = isFolderPlaceholder(previousPath);
2968
+ if (toPlaceholder === fromPlaceholder)
2969
+ return entry;
2970
+ return toPlaceholder ? { status: 'removed', path: previousPath } : { status: 'added', path: entry.path };
2971
+ }
2389
2972
  /**
2390
2973
  * Parse `git diff -M -z --numstat` output, in the SAME file order as
2391
2974
  * `--name-status` (both driven by `-M` over the same range). Each record is