@bevel-software/platform-core-backend 0.15.1 → 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 (1220) hide show
  1. package/THIRD-PARTY-NOTICES.md +4 -4
  2. package/dist/core/catalog-cache-invalidation.d.ts +16 -0
  3. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  4. package/dist/core/catalog-cache-invalidation.js +6 -1
  5. package/dist/core/catalog-cache-invalidation.js.map +1 -1
  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 +199 -75
  13. package/dist/core/create-core-server.js.map +1 -1
  14. package/dist/core/create-core-services.d.ts +49 -2
  15. package/dist/core/create-core-services.d.ts.map +1 -1
  16. package/dist/core/create-core-services.js +212 -86
  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 +215 -11
  39. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.d.ts +57 -11
  41. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  42. package/dist/modules/access/access-control.service.js +616 -155
  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 +255 -79
  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 +49 -24
  98. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  99. package/dist/modules/access-model/access-grammar.js +68 -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/plugin-principals.d.ts +5 -3
  120. package/dist/modules/access-model/plugin-principals.d.ts.map +1 -1
  121. package/dist/modules/access-model/plugin-principals.js +11 -17
  122. package/dist/modules/access-model/plugin-principals.js.map +1 -1
  123. package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
  124. package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
  125. package/dist/modules/access-model/roles-yaml-guard.js +153 -3
  126. package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
  127. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  128. package/dist/modules/admin/admin-access.service.js +5 -2
  129. package/dist/modules/admin/admin-access.service.js.map +1 -1
  130. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
  131. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
  132. package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
  133. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
  134. package/dist/modules/agent-instructions/compose.d.ts +71 -0
  135. package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
  136. package/dist/modules/agent-instructions/compose.js +234 -0
  137. package/dist/modules/agent-instructions/compose.js.map +1 -0
  138. package/dist/modules/agent-instructions/index.d.ts +4 -0
  139. package/dist/modules/agent-instructions/index.d.ts.map +1 -0
  140. package/dist/modules/agent-instructions/index.js +4 -0
  141. package/dist/modules/agent-instructions/index.js.map +1 -0
  142. package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
  143. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
  144. package/dist/modules/agent-instructions/read-preamble.js +81 -0
  145. package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
  146. package/dist/modules/auth/account-erasure.service.d.ts +30 -4
  147. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
  148. package/dist/modules/auth/account-erasure.service.js +96 -10
  149. package/dist/modules/auth/account-erasure.service.js.map +1 -1
  150. package/dist/modules/auth/account.routes.d.ts +6 -3
  151. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  152. package/dist/modules/auth/account.routes.js +109 -10
  153. package/dist/modules/auth/account.routes.js.map +1 -1
  154. package/dist/modules/auth/auth.routes.d.ts +10 -3
  155. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  156. package/dist/modules/auth/auth.routes.js +9 -4
  157. package/dist/modules/auth/auth.routes.js.map +1 -1
  158. package/dist/modules/auth/auth.service.d.ts +89 -5
  159. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  160. package/dist/modules/auth/auth.service.js +160 -25
  161. package/dist/modules/auth/auth.service.js.map +1 -1
  162. package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
  163. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  164. package/dist/modules/auth/oidc-auth-provider.js +99 -21
  165. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  166. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  167. package/dist/modules/code-mode/code-mode.tool.js +14 -2
  168. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  169. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  170. package/dist/modules/connection-probe/connection-probe.service.js +56 -7
  171. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  172. package/dist/modules/database/advisory-lock.d.ts +103 -0
  173. package/dist/modules/database/advisory-lock.d.ts.map +1 -0
  174. package/dist/modules/database/advisory-lock.js +320 -0
  175. package/dist/modules/database/advisory-lock.js.map +1 -0
  176. package/dist/modules/database/connection.d.ts.map +1 -1
  177. package/dist/modules/database/connection.js +9 -1
  178. package/dist/modules/database/connection.js.map +1 -1
  179. package/dist/modules/database/core-schema.d.ts +346 -0
  180. package/dist/modules/database/core-schema.d.ts.map +1 -1
  181. package/dist/modules/database/core-schema.js +119 -0
  182. package/dist/modules/database/core-schema.js.map +1 -1
  183. package/dist/modules/database/migrate.d.ts.map +1 -1
  184. package/dist/modules/database/migrate.js +26 -6
  185. package/dist/modules/database/migrate.js.map +1 -1
  186. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  187. package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
  188. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  189. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  190. package/dist/modules/diff/diff.routes.js +2 -4
  191. package/dist/modules/diff/diff.routes.js.map +1 -1
  192. package/dist/modules/diff/diff.service.d.ts +4 -1
  193. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  194. package/dist/modules/diff/diff.service.js +79 -81
  195. package/dist/modules/diff/diff.service.js.map +1 -1
  196. package/dist/modules/kb-fs/bevel-ignore.d.ts +35 -0
  197. package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
  198. package/dist/modules/kb-fs/bevel-ignore.js +71 -0
  199. package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
  200. package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
  201. package/dist/modules/kb-fs/file-change-notifier.js +3 -1
  202. package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
  203. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +37 -0
  204. package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
  205. package/dist/modules/kb-fs/git-guarded-filesystem.js +104 -0
  206. package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
  207. package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
  208. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  209. package/dist/modules/kb-fs/locking-filesystem.js +353 -76
  210. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  211. package/dist/modules/kb-fs/node-fs.d.ts +23 -0
  212. package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
  213. package/dist/modules/kb-fs/node-fs.js +158 -0
  214. package/dist/modules/kb-fs/node-fs.js.map +1 -0
  215. package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
  216. package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
  217. package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
  218. package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
  219. package/dist/modules/kb-fs/repo-path.d.ts +100 -3
  220. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  221. package/dist/modules/kb-fs/repo-path.js +195 -3
  222. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  223. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  224. package/dist/modules/kb-sync/kb-sync.routes.js +4 -2
  225. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  226. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  227. package/dist/modules/kb-sync/kb-sync.service.js +7 -5
  228. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  229. package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -1
  230. package/dist/modules/marketplace/git-http.routes.js +6 -4
  231. package/dist/modules/marketplace/git-http.routes.js.map +1 -1
  232. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +12 -2
  233. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -1
  234. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +32 -3
  235. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -1
  236. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +27 -0
  237. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -1
  238. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +45 -0
  239. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -1
  240. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -1
  241. package/dist/modules/marketplace/github-facade/github-facade.routes.js +30 -6
  242. package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -1
  243. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +21 -9
  244. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -1
  245. package/dist/modules/marketplace/github-facade/github-facade.service.js +60 -19
  246. package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -1
  247. package/dist/modules/marketplace/github-facade/index.d.ts +1 -1
  248. package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -1
  249. package/dist/modules/marketplace/github-facade/index.js +1 -1
  250. package/dist/modules/marketplace/github-facade/index.js.map +1 -1
  251. package/dist/modules/marketplace/marketplace-repo.service.d.ts +14 -1
  252. package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -1
  253. package/dist/modules/marketplace/marketplace-repo.service.js +26 -8
  254. package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -1
  255. package/dist/modules/mcp/downstream-pool.d.ts +107 -0
  256. package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
  257. package/dist/modules/mcp/downstream-pool.js +221 -0
  258. package/dist/modules/mcp/downstream-pool.js.map +1 -0
  259. package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
  260. package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
  261. package/dist/modules/mcp/downstream-token-refresh.js +166 -0
  262. package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
  263. package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
  264. package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
  265. package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
  266. package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
  267. package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
  268. package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
  269. package/dist/modules/mcp/first-call-probe.cli.js +62 -0
  270. package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
  271. package/dist/modules/mcp/first-call-probe.d.ts +165 -0
  272. package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
  273. package/dist/modules/mcp/first-call-probe.js +298 -0
  274. package/dist/modules/mcp/first-call-probe.js.map +1 -0
  275. package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
  276. package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
  277. package/dist/modules/mcp/manual-failure-memo.js +79 -23
  278. package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
  279. package/dist/modules/mcp/mcp-auth.middleware.d.ts +9 -5
  280. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  281. package/dist/modules/mcp/mcp-auth.middleware.js +24 -16
  282. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  283. package/dist/modules/mcp/mcp.routes.d.ts +4 -4
  284. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  285. package/dist/modules/mcp/mcp.routes.js +83 -179
  286. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  287. package/dist/modules/mcp/mcp.service.d.ts +218 -61
  288. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  289. package/dist/modules/mcp/mcp.service.js +713 -159
  290. package/dist/modules/mcp/mcp.service.js.map +1 -1
  291. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  292. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +4 -2
  293. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  294. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  295. package/dist/modules/mcp/oauth/oauth-consent.routes.js +3 -1
  296. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  297. package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
  298. package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
  299. package/dist/modules/mcp/surface-log-throttle.js +94 -0
  300. package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
  301. package/dist/modules/plugins/compile/compile-marketplace.d.ts +13 -6
  302. package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -1
  303. package/dist/modules/plugins/compile/compile-marketplace.js +119 -36
  304. package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -1
  305. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +8 -1
  306. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  307. package/dist/modules/plugins/compile/marketplace-compiler.service.js +9 -6
  308. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  309. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  310. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +32 -9
  311. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  312. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +26 -6
  313. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -1
  314. package/dist/modules/plugins/discovery/bundle-dialect/registry.js +71 -14
  315. package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -1
  316. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +4 -2
  317. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  318. package/dist/modules/plugins/discovery/kb-plugin-source.js +9 -6
  319. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  320. package/dist/modules/plugins/discovery/native.source.d.ts +2 -1
  321. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  322. package/dist/modules/plugins/discovery/native.source.js +22 -4
  323. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  324. package/dist/modules/plugins/discovery/plugin-source.d.ts +17 -3
  325. package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -1
  326. package/dist/modules/plugins/index.d.ts +6 -3
  327. package/dist/modules/plugins/index.d.ts.map +1 -1
  328. package/dist/modules/plugins/index.js +6 -3
  329. package/dist/modules/plugins/index.js.map +1 -1
  330. package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
  331. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
  332. package/dist/modules/plugins/join-request-jobs.service.js +602 -0
  333. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
  334. package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
  335. package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
  336. package/dist/modules/plugins/join-request-records.store.js +179 -0
  337. package/dist/modules/plugins/join-request-records.store.js.map +1 -0
  338. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  339. package/dist/modules/plugins/join-requests.service.js +4 -2
  340. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  341. package/dist/modules/plugins/plugin-links.d.ts +4 -1
  342. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  343. package/dist/modules/plugins/plugin-links.js +6 -3
  344. package/dist/modules/plugins/plugin-links.js.map +1 -1
  345. package/dist/modules/plugins/plugin-links.service.d.ts +2 -5
  346. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  347. package/dist/modules/plugins/plugin-links.service.js +4 -13
  348. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  349. package/dist/modules/plugins/plugin-provision.service.d.ts +91 -19
  350. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  351. package/dist/modules/plugins/plugin-provision.service.js +183 -38
  352. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  353. package/dist/modules/plugins/plugin-rename.service.d.ts +3 -1
  354. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  355. package/dist/modules/plugins/plugin-rename.service.js +20 -11
  356. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  357. package/dist/modules/plugins/plugins.contract.d.ts +52 -4
  358. package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
  359. package/dist/modules/plugins/plugins.routes.d.ts +38 -5
  360. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  361. package/dist/modules/plugins/plugins.routes.js +190 -116
  362. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  363. package/dist/modules/plugins/plugins.service.d.ts +38 -6
  364. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  365. package/dist/modules/plugins/plugins.service.js +86 -17
  366. package/dist/modules/plugins/plugins.service.js.map +1 -1
  367. package/dist/modules/plugins/plugins.tools.d.ts +25 -0
  368. package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
  369. package/dist/modules/plugins/plugins.tools.js +82 -0
  370. package/dist/modules/plugins/plugins.tools.js.map +1 -0
  371. package/dist/modules/plugins/teams.routes.d.ts +42 -0
  372. package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
  373. package/dist/modules/plugins/teams.routes.js +79 -0
  374. package/dist/modules/plugins/teams.routes.js.map +1 -0
  375. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
  376. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  377. package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
  378. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  379. package/dist/modules/secrets-vault/index.d.ts +1 -1
  380. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  381. package/dist/modules/secrets-vault/index.js.map +1 -1
  382. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
  383. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
  384. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
  385. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  386. package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
  387. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  388. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
  389. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  390. package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
  391. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  392. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  393. package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
  394. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  395. package/dist/modules/settings/connection-check.d.ts +118 -0
  396. package/dist/modules/settings/connection-check.d.ts.map +1 -0
  397. package/dist/modules/settings/connection-check.js +287 -0
  398. package/dist/modules/settings/connection-check.js.map +1 -0
  399. package/dist/modules/settings/deployment-settings.service.d.ts +153 -7
  400. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  401. package/dist/modules/settings/deployment-settings.service.js +348 -51
  402. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  403. package/dist/modules/settings/git-root-folders.d.ts +54 -0
  404. package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
  405. package/dist/modules/settings/git-root-folders.js +130 -0
  406. package/dist/modules/settings/git-root-folders.js.map +1 -0
  407. package/dist/modules/settings/oidc-check.d.ts +85 -0
  408. package/dist/modules/settings/oidc-check.d.ts.map +1 -0
  409. package/dist/modules/settings/oidc-check.js +172 -0
  410. package/dist/modules/settings/oidc-check.js.map +1 -0
  411. package/dist/modules/settings/setup.routes.d.ts +31 -1
  412. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  413. package/dist/modules/settings/setup.routes.js +400 -108
  414. package/dist/modules/settings/setup.routes.js.map +1 -1
  415. package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
  416. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
  417. package/dist/modules/skills/allowed-tools-check.js +260 -0
  418. package/dist/modules/skills/allowed-tools-check.js.map +1 -0
  419. package/dist/modules/skills/index.d.ts +3 -1
  420. package/dist/modules/skills/index.d.ts.map +1 -1
  421. package/dist/modules/skills/index.js +1 -0
  422. package/dist/modules/skills/index.js.map +1 -1
  423. package/dist/modules/skills/pending-skills.service.d.ts +6 -11
  424. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  425. package/dist/modules/skills/pending-skills.service.js +18 -70
  426. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  427. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  428. package/dist/modules/skills/skill-access-requests.routes.js +9 -6
  429. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  430. package/dist/modules/skills/skills.contract.d.ts +44 -10
  431. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  432. package/dist/modules/skills/skills.routes.d.ts +8 -1
  433. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  434. package/dist/modules/skills/skills.routes.js +16 -2
  435. package/dist/modules/skills/skills.routes.js.map +1 -1
  436. package/dist/modules/skills/skills.service.d.ts +31 -7
  437. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  438. package/dist/modules/skills/skills.service.js +191 -81
  439. package/dist/modules/skills/skills.service.js.map +1 -1
  440. package/dist/modules/skills/skills.tools.d.ts +4 -1
  441. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  442. package/dist/modules/skills/skills.tools.js +41 -6
  443. package/dist/modules/skills/skills.tools.js.map +1 -1
  444. package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
  445. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
  446. package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
  447. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
  448. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
  449. package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
  450. package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
  451. package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
  452. package/dist/modules/tool-auth/external-api-key.interface.d.ts +51 -3
  453. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  454. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  455. package/dist/modules/tool-auth/external-api-key.service.d.ts +16 -6
  456. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  457. package/dist/modules/tool-auth/external-api-key.service.js +61 -18
  458. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  459. package/dist/modules/tool-auth/internal-token.service.d.ts +5 -2
  460. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  461. package/dist/modules/tool-auth/internal-token.service.js +5 -2
  462. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  463. package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
  464. package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
  465. package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
  466. package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
  467. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +7 -1
  468. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  469. package/dist/modules/tool-auth/tool-auth.middleware.js +8 -5
  470. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  471. package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
  472. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  473. package/dist/modules/tool-helpers/tool-context.js +28 -6
  474. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  475. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  476. package/dist/modules/tool-helpers/tool-handler.js +22 -5
  477. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  478. package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
  479. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  480. package/dist/modules/tool-helpers/tool.contract.js +7 -1
  481. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  482. package/dist/modules/tool-manuals/index.d.ts +2 -1
  483. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  484. package/dist/modules/tool-manuals/index.js +1 -0
  485. package/dist/modules/tool-manuals/index.js.map +1 -1
  486. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  487. package/dist/modules/tool-manuals/mcp-json-discovery.js +8 -6
  488. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  489. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
  490. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  491. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
  492. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  493. package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
  494. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
  495. package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
  496. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
  497. package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
  498. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
  499. package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
  500. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
  501. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
  502. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  503. package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
  504. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
  505. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
  506. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  507. package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
  508. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  509. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +119 -5
  510. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  511. package/dist/modules/tool-manuals/tool-manuals.service.js +404 -74
  512. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  513. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  514. package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
  515. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  516. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  517. package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
  518. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  519. package/dist/modules/workflow/event-bus.d.ts +10 -4
  520. package/dist/modules/workflow/event-bus.d.ts.map +1 -1
  521. package/dist/modules/workflow/event-bus.js +14 -11
  522. package/dist/modules/workflow/event-bus.js.map +1 -1
  523. package/dist/modules/workflow/events.routes.d.ts +17 -2
  524. package/dist/modules/workflow/events.routes.d.ts.map +1 -1
  525. package/dist/modules/workflow/events.routes.js +31 -12
  526. package/dist/modules/workflow/events.routes.js.map +1 -1
  527. package/dist/modules/workflow/file-lock.service.d.ts +22 -4
  528. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  529. package/dist/modules/workflow/file-lock.service.js +27 -4
  530. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  531. package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
  532. package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
  533. package/dist/modules/workflow/git/change-request-link.js +34 -0
  534. package/dist/modules/workflow/git/change-request-link.js.map +1 -0
  535. package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
  536. package/dist/modules/workflow/git/git-version.js +3 -1
  537. package/dist/modules/workflow/git/git-version.js.map +1 -1
  538. package/dist/modules/workflow/git/git.service.d.ts +263 -4
  539. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  540. package/dist/modules/workflow/git/git.service.js +528 -118
  541. package/dist/modules/workflow/git/git.service.js.map +1 -1
  542. package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
  543. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
  544. package/dist/modules/workflow/git/node-git-runner.js +273 -0
  545. package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
  546. package/dist/modules/workflow/git/pull-request.service.d.ts +51 -2
  547. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  548. package/dist/modules/workflow/git/pull-request.service.js +157 -25
  549. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  550. package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
  551. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  552. package/dist/modules/workflow/pending-commits.service.js +24 -4
  553. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  554. package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
  555. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  556. package/dist/modules/workflow/pending-commits.worker.js +107 -58
  557. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  558. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
  559. package/dist/modules/workflow/recovery-bot.js +3 -1
  560. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  561. package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
  562. package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
  563. package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
  564. package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
  565. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
  566. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
  567. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -0
  568. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  569. package/dist/modules/workflow/review-workflow/review-workflow.service.js +269 -90
  570. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  571. package/dist/modules/workflow/sanitize-error.d.ts +5 -2
  572. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  573. package/dist/modules/workflow/sanitize-error.js +8 -4
  574. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  575. package/dist/modules/workflow/workflow.routes.d.ts +2 -0
  576. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  577. package/dist/modules/workflow/workflow.routes.js +259 -29
  578. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  579. package/dist/modules/workflow/workflow.service.d.ts +166 -14
  580. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  581. package/dist/modules/workflow/workflow.service.js +906 -106
  582. package/dist/modules/workflow/workflow.service.js.map +1 -1
  583. package/dist/modules/workspace/empty-dirs.d.ts +15 -0
  584. package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
  585. package/dist/modules/workspace/empty-dirs.js +90 -0
  586. package/dist/modules/workspace/empty-dirs.js.map +1 -0
  587. package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
  588. package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
  589. package/dist/modules/workspace/file-readers/content-mode.js +55 -0
  590. package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
  591. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
  592. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  593. package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
  594. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  595. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
  596. package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
  597. package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
  598. package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
  599. package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
  600. package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
  601. package/dist/modules/workspace/file-readers/document-reader.js +5 -0
  602. package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
  603. package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
  604. package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
  605. package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
  606. package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
  607. package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
  608. package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
  609. package/dist/modules/workspace/file-readers/file-reader.js +11 -6
  610. package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
  611. package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
  612. package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
  613. package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
  614. package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
  615. package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
  616. package/dist/modules/workspace/file-readers/image-reader.js +9 -5
  617. package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
  618. package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
  619. package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
  620. package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
  621. package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
  622. package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
  623. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  624. package/dist/modules/workspace/file-readers/text-reader.js +57 -3
  625. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  626. package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
  627. package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
  628. package/dist/modules/workspace/git-internals.middleware.js +93 -0
  629. package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
  630. package/dist/modules/workspace/not-found.d.ts +80 -0
  631. package/dist/modules/workspace/not-found.d.ts.map +1 -0
  632. package/dist/modules/workspace/not-found.js +112 -0
  633. package/dist/modules/workspace/not-found.js.map +1 -0
  634. package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
  635. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  636. package/dist/modules/workspace/session-ontology.gate.js +1 -1
  637. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  638. package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
  639. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
  640. package/dist/modules/workspace/startup/beside-checkout.js +110 -0
  641. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
  642. package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
  643. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  644. package/dist/modules/workspace/startup/kb-git.js +34 -35
  645. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  646. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
  647. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  648. package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
  649. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  650. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
  651. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  652. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
  653. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  654. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
  655. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
  656. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
  657. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
  658. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
  659. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
  660. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
  661. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
  662. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
  663. package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
  664. package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
  665. package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
  666. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  667. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  668. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +45 -81
  669. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  670. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
  671. package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
  672. package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
  673. package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
  674. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
  675. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  676. package/dist/modules/workspace/startup/steps/seed-tree.js +154 -78
  677. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  678. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
  679. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  680. package/dist/modules/workspace/startup/steps/template-files.step.js +446 -138
  681. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  682. package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
  683. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
  684. package/dist/modules/workspace/startup/steps/template-source.js +160 -0
  685. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
  686. package/dist/modules/workspace/workspace.routes.d.ts +14 -2
  687. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  688. package/dist/modules/workspace/workspace.routes.js +649 -238
  689. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  690. package/dist/modules/workspace/workspace.service.d.ts +290 -12
  691. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  692. package/dist/modules/workspace/workspace.service.js +924 -269
  693. package/dist/modules/workspace/workspace.service.js.map +1 -1
  694. package/dist/modules/workspace/workspace.tools.d.ts +42 -1
  695. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  696. package/dist/modules/workspace/workspace.tools.js +1590 -146
  697. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  698. package/dist/modules/workspace/write-denial.d.ts +76 -0
  699. package/dist/modules/workspace/write-denial.d.ts.map +1 -0
  700. package/dist/modules/workspace/write-denial.js +138 -0
  701. package/dist/modules/workspace/write-denial.js.map +1 -0
  702. package/dist/shared/canonical-file-identity.d.ts +64 -0
  703. package/dist/shared/canonical-file-identity.d.ts.map +1 -0
  704. package/dist/shared/canonical-file-identity.js +92 -0
  705. package/dist/shared/canonical-file-identity.js.map +1 -0
  706. package/dist/shared/domain-errors.d.ts +108 -0
  707. package/dist/shared/domain-errors.d.ts.map +1 -1
  708. package/dist/shared/domain-errors.js +137 -0
  709. package/dist/shared/domain-errors.js.map +1 -1
  710. package/dist/shared/email-identity.d.ts +32 -0
  711. package/dist/shared/email-identity.d.ts.map +1 -0
  712. package/dist/shared/email-identity.js +37 -0
  713. package/dist/shared/email-identity.js.map +1 -0
  714. package/dist/shared/fs.contract.d.ts +206 -0
  715. package/dist/shared/fs.contract.d.ts.map +1 -0
  716. package/dist/shared/fs.contract.js +79 -0
  717. package/dist/shared/fs.contract.js.map +1 -0
  718. package/dist/shared/git-failure.d.ts +80 -0
  719. package/dist/shared/git-failure.d.ts.map +1 -0
  720. package/dist/shared/git-failure.js +139 -0
  721. package/dist/shared/git-failure.js.map +1 -0
  722. package/dist/shared/git-internals.d.ts +17 -0
  723. package/dist/shared/git-internals.d.ts.map +1 -0
  724. package/dist/shared/git-internals.js +123 -0
  725. package/dist/shared/git-internals.js.map +1 -0
  726. package/dist/shared/git.contract.d.ts +157 -0
  727. package/dist/shared/git.contract.d.ts.map +1 -0
  728. package/dist/shared/git.contract.js +103 -0
  729. package/dist/shared/git.contract.js.map +1 -0
  730. package/dist/shared/http-errors.d.ts +33 -0
  731. package/dist/shared/http-errors.d.ts.map +1 -0
  732. package/dist/shared/http-errors.js +35 -0
  733. package/dist/shared/http-errors.js.map +1 -0
  734. package/dist/shared/logger.contract.d.ts +63 -0
  735. package/dist/shared/logger.contract.d.ts.map +1 -0
  736. package/dist/shared/logger.contract.js +126 -0
  737. package/dist/shared/logger.contract.js.map +1 -0
  738. package/dist/shared/logging.d.ts +31 -0
  739. package/dist/shared/logging.d.ts.map +1 -0
  740. package/dist/shared/logging.js +92 -0
  741. package/dist/shared/logging.js.map +1 -0
  742. package/dist/shared/path-containment.d.ts +21 -0
  743. package/dist/shared/path-containment.d.ts.map +1 -0
  744. package/dist/shared/path-containment.js +34 -0
  745. package/dist/shared/path-containment.js.map +1 -0
  746. package/dist/shared/pending-proposals.d.ts +71 -0
  747. package/dist/shared/pending-proposals.d.ts.map +1 -0
  748. package/dist/shared/pending-proposals.js +98 -0
  749. package/dist/shared/pending-proposals.js.map +1 -0
  750. package/dist/shared/printable.d.ts +22 -0
  751. package/dist/shared/printable.d.ts.map +1 -0
  752. package/dist/shared/printable.js +65 -0
  753. package/dist/shared/printable.js.map +1 -0
  754. package/dist/shared/redact-secret.d.ts +30 -0
  755. package/dist/shared/redact-secret.d.ts.map +1 -0
  756. package/dist/shared/redact-secret.js +114 -0
  757. package/dist/shared/redact-secret.js.map +1 -0
  758. package/dist/shared/rename-no-replace.d.ts +119 -0
  759. package/dist/shared/rename-no-replace.d.ts.map +1 -0
  760. package/dist/shared/rename-no-replace.js +323 -0
  761. package/dist/shared/rename-no-replace.js.map +1 -0
  762. package/dist/shared/ssrf.d.ts.map +1 -1
  763. package/dist/shared/ssrf.js +49 -18
  764. package/dist/shared/ssrf.js.map +1 -1
  765. package/dist/version.d.ts +10 -6
  766. package/dist/version.d.ts.map +1 -1
  767. package/dist/version.js +10 -6
  768. package/dist/version.js.map +1 -1
  769. package/kb-template/.bevelignore +4 -3
  770. package/kb-template/AGENTS.md +281 -36
  771. package/kb-template/KnowledgeBase/How to get started.md +1 -1
  772. package/kb-template/access.md +5 -3
  773. package/kb-template/mcp-description.md +27 -0
  774. package/migrations/0006_api_tokens_revoked_by.sql +1 -0
  775. package/migrations/0007_github_facade_registered.sql +1 -0
  776. package/migrations/0008_change_request_apply_failure.sql +4 -0
  777. package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
  778. package/migrations/0010_plugin_join_requests.sql +15 -0
  779. package/migrations/0011_plugin_join_request_claim.sql +11 -0
  780. package/migrations/meta/0006_snapshot.json +1723 -0
  781. package/migrations/meta/0007_snapshot.json +1729 -0
  782. package/migrations/meta/0008_snapshot.json +1753 -0
  783. package/migrations/meta/0009_snapshot.json +1759 -0
  784. package/migrations/meta/0010_snapshot.json +1872 -0
  785. package/migrations/meta/0011_snapshot.json +1884 -0
  786. package/migrations/meta/_journal.json +42 -0
  787. package/package.json +6 -5
  788. package/src/__tests__/core-config.domain.test.ts +67 -2
  789. package/src/__tests__/kb-layout-config.test.ts +186 -1
  790. package/src/__tests__/open-change-gate.ts +15 -0
  791. package/src/core/__tests__/catalog-cache-invalidation.test.ts +83 -3
  792. package/src/core/__tests__/catalog-revision.test.ts +190 -0
  793. package/src/core/__tests__/lifecycle.test.ts +369 -0
  794. package/src/core/__tests__/public-config.test.ts +30 -0
  795. package/src/core/__tests__/readiness.test.ts +109 -0
  796. package/src/core/catalog-cache-invalidation.ts +20 -1
  797. package/src/core/catalog-revision.ts +110 -0
  798. package/src/core/create-core-server.ts +258 -79
  799. package/src/core/create-core-services.ts +302 -81
  800. package/src/core/lifecycle.ts +312 -0
  801. package/src/core/public-config.ts +86 -0
  802. package/src/core/readiness.ts +123 -0
  803. package/src/core-config.ts +70 -4
  804. package/src/index.ts +15 -0
  805. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
  806. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
  807. package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
  808. package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
  809. package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
  810. package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
  811. package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
  812. package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +2 -0
  813. package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
  814. package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
  815. package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
  816. package/src/modules/access/__tests__/access-control.service.test.ts +123 -78
  817. package/src/modules/access/__tests__/access-groups.test.ts +47 -14
  818. package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
  819. package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
  820. package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
  821. package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
  822. package/src/modules/access/__tests__/access-plugin-principals.test.ts +18 -1
  823. package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
  824. package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
  825. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
  826. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
  827. package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
  828. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
  829. package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
  830. package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
  831. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
  832. package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
  833. package/src/modules/access/__tests__/creator-access.test.ts +81 -83
  834. package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
  835. package/src/modules/access/__tests__/groups-admin.service.test.ts +6 -5
  836. package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
  837. package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
  838. package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
  839. package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
  840. package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
  841. package/src/modules/access/__tests__/users-db-double.ts +63 -0
  842. package/src/modules/access/access-control.interface.ts +251 -11
  843. package/src/modules/access/access-control.service.ts +719 -164
  844. package/src/modules/access/access-mutation.service.ts +182 -13
  845. package/src/modules/access/access-view.ts +316 -0
  846. package/src/modules/access/access.routes.ts +293 -95
  847. package/src/modules/access/admin-locked-commit.ts +12 -14
  848. package/src/modules/access/admin-route-helpers.ts +4 -4
  849. package/src/modules/access/change-read-gate.ts +148 -0
  850. package/src/modules/access/creator-access.ts +89 -124
  851. package/src/modules/access/directory-sync-bot.ts +4 -1
  852. package/src/modules/access/groups-admin.service.ts +3 -3
  853. package/src/modules/access/reference-scan.ts +5 -5
  854. package/src/modules/access/roles-admin.service.ts +94 -15
  855. package/src/modules/access/synced-groups-committer.ts +10 -15
  856. package/src/modules/access/synced-groups-writer.ts +3 -12
  857. package/src/modules/access/user-access-removal.service.ts +711 -0
  858. package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
  859. package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
  860. package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
  861. package/src/modules/access-model/access-errors.ts +56 -15
  862. package/src/modules/access-model/access-grammar.ts +85 -48
  863. package/src/modules/access-model/access-splice.ts +18 -18
  864. package/src/modules/access-model/access-template.ts +44 -0
  865. package/src/modules/access-model/change-gate.ts +80 -0
  866. package/src/modules/access-model/creator.ts +18 -21
  867. package/src/modules/access-model/frontmatter-lines.ts +98 -0
  868. package/src/modules/access-model/plugin-principals.ts +12 -19
  869. package/src/modules/access-model/roles-yaml-guard.ts +182 -4
  870. package/src/modules/admin/admin-access.service.ts +6 -2
  871. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
  872. package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
  873. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
  874. package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
  875. package/src/modules/agent-instructions/compose.ts +271 -0
  876. package/src/modules/agent-instructions/index.ts +14 -0
  877. package/src/modules/agent-instructions/read-preamble.ts +88 -0
  878. package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
  879. package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
  880. package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
  881. package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
  882. package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
  883. package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
  884. package/src/modules/auth/account-erasure.service.ts +117 -10
  885. package/src/modules/auth/account.routes.ts +115 -14
  886. package/src/modules/auth/auth.routes.ts +20 -7
  887. package/src/modules/auth/auth.service.ts +187 -28
  888. package/src/modules/auth/oidc-auth-provider.ts +120 -22
  889. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
  890. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  891. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
  892. package/src/modules/connection-probe/connection-probe.service.ts +59 -7
  893. package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
  894. package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
  895. package/src/modules/database/advisory-lock.ts +346 -0
  896. package/src/modules/database/connection.ts +9 -1
  897. package/src/modules/database/core-schema.ts +123 -0
  898. package/src/modules/database/migrate.ts +27 -6
  899. package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
  900. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
  901. package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
  902. package/src/modules/diff/diff.routes.ts +2 -4
  903. package/src/modules/diff/diff.service.ts +75 -79
  904. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
  905. package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
  906. package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
  907. package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
  908. package/src/modules/kb-fs/file-change-notifier.ts +4 -1
  909. package/src/modules/kb-fs/git-guarded-filesystem.ts +123 -0
  910. package/src/modules/kb-fs/locking-filesystem.ts +399 -109
  911. package/src/modules/kb-fs/node-fs.ts +165 -0
  912. package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
  913. package/src/modules/kb-fs/repo-path.ts +213 -3
  914. package/src/modules/kb-sync/kb-sync.routes.ts +5 -2
  915. package/src/modules/kb-sync/kb-sync.service.ts +8 -5
  916. package/src/modules/marketplace/__tests__/github-facade.test.ts +134 -2
  917. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +2 -1
  918. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
  919. package/src/modules/marketplace/git-http.routes.ts +7 -4
  920. package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +35 -3
  921. package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +55 -0
  922. package/src/modules/marketplace/github-facade/github-facade.routes.ts +32 -6
  923. package/src/modules/marketplace/github-facade/github-facade.service.ts +80 -19
  924. package/src/modules/marketplace/github-facade/index.ts +1 -0
  925. package/src/modules/marketplace/marketplace-repo.service.ts +23 -8
  926. package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
  927. package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
  928. package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
  929. package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
  930. package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
  931. package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
  932. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +8 -5
  933. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
  934. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
  935. package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
  936. package/src/modules/mcp/__tests__/mcp.service.test.ts +274 -6
  937. package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
  938. package/src/modules/mcp/downstream-pool.ts +256 -0
  939. package/src/modules/mcp/downstream-token-refresh.ts +170 -0
  940. package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
  941. package/src/modules/mcp/first-call-probe.cli.ts +60 -0
  942. package/src/modules/mcp/first-call-probe.ts +418 -0
  943. package/src/modules/mcp/manual-failure-memo.ts +81 -23
  944. package/src/modules/mcp/mcp-auth.middleware.ts +25 -16
  945. package/src/modules/mcp/mcp.routes.ts +376 -482
  946. package/src/modules/mcp/mcp.service.ts +846 -176
  947. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +5 -2
  948. package/src/modules/mcp/oauth/oauth-consent.routes.ts +4 -4
  949. package/src/modules/mcp/surface-log-throttle.ts +120 -0
  950. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +10 -7
  951. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
  952. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +139 -10
  953. package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
  954. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
  955. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
  956. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +148 -7
  957. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +10 -13
  958. package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
  959. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +129 -19
  960. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +91 -29
  961. package/src/modules/plugins/__tests__/plugins.routes.test.ts +370 -19
  962. package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
  963. package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
  964. package/src/modules/plugins/compile/compile-marketplace.ts +143 -46
  965. package/src/modules/plugins/compile/marketplace-compiler.service.ts +8 -7
  966. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +56 -20
  967. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +30 -9
  968. package/src/modules/plugins/discovery/bundle-dialect/registry.ts +93 -18
  969. package/src/modules/plugins/discovery/kb-plugin-source.ts +10 -7
  970. package/src/modules/plugins/discovery/native.source.ts +21 -3
  971. package/src/modules/plugins/discovery/plugin-source.ts +17 -3
  972. package/src/modules/plugins/index.ts +16 -3
  973. package/src/modules/plugins/join-request-jobs.service.ts +675 -0
  974. package/src/modules/plugins/join-request-records.store.ts +353 -0
  975. package/src/modules/plugins/join-requests.service.ts +7 -4
  976. package/src/modules/plugins/plugin-links.service.ts +4 -17
  977. package/src/modules/plugins/plugin-links.ts +6 -3
  978. package/src/modules/plugins/plugin-provision.service.ts +209 -37
  979. package/src/modules/plugins/plugin-rename.service.ts +21 -9
  980. package/src/modules/plugins/plugins.contract.ts +52 -4
  981. package/src/modules/plugins/plugins.routes.ts +204 -124
  982. package/src/modules/plugins/plugins.service.ts +92 -18
  983. package/src/modules/plugins/plugins.tools.ts +88 -0
  984. package/src/modules/plugins/teams.routes.ts +129 -0
  985. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
  986. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
  987. package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
  988. package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
  989. package/src/modules/secrets-vault/index.ts +1 -0
  990. package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
  991. package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
  992. package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
  993. package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
  994. package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
  995. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +188 -6
  996. package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
  997. package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
  998. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
  999. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
  1000. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
  1001. package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
  1002. package/src/modules/settings/connection-check.ts +351 -0
  1003. package/src/modules/settings/deployment-settings.service.ts +403 -57
  1004. package/src/modules/settings/git-root-folders.ts +171 -0
  1005. package/src/modules/settings/oidc-check.ts +229 -0
  1006. package/src/modules/settings/setup.routes.ts +456 -110
  1007. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
  1008. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
  1009. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
  1010. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
  1011. package/src/modules/skills/__tests__/skills.service.test.ts +223 -2
  1012. package/src/modules/skills/allowed-tools-check.ts +352 -0
  1013. package/src/modules/skills/index.ts +4 -0
  1014. package/src/modules/skills/pending-skills.service.ts +21 -82
  1015. package/src/modules/skills/skill-access-requests.routes.ts +10 -6
  1016. package/src/modules/skills/skills.contract.ts +43 -11
  1017. package/src/modules/skills/skills.routes.ts +16 -1
  1018. package/src/modules/skills/skills.service.ts +214 -84
  1019. package/src/modules/skills/skills.tools.ts +43 -5
  1020. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
  1021. package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
  1022. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +124 -0
  1023. package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
  1024. package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
  1025. package/src/modules/tool-auth/external-api-key.interface.ts +56 -3
  1026. package/src/modules/tool-auth/external-api-key.service.ts +73 -25
  1027. package/src/modules/tool-auth/internal-token.service.ts +5 -2
  1028. package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
  1029. package/src/modules/tool-auth/tool-auth.middleware.ts +16 -7
  1030. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
  1031. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
  1032. package/src/modules/tool-helpers/tool-context.ts +46 -7
  1033. package/src/modules/tool-helpers/tool-handler.ts +23 -5
  1034. package/src/modules/tool-helpers/tool.contract.ts +5 -0
  1035. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  1036. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
  1037. package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
  1038. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
  1039. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
  1040. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +9 -4
  1041. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
  1042. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
  1043. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
  1044. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
  1045. package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
  1046. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
  1047. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
  1048. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
  1049. package/src/modules/tool-manuals/index.ts +3 -0
  1050. package/src/modules/tool-manuals/mcp-json-discovery.ts +13 -10
  1051. package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
  1052. package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
  1053. package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
  1054. package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
  1055. package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
  1056. package/src/modules/tool-manuals/tool-manuals.service.ts +432 -87
  1057. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
  1058. package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
  1059. package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
  1060. package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
  1061. package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
  1062. package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
  1063. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
  1064. package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
  1065. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -0
  1066. package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
  1067. package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
  1068. package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
  1069. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
  1070. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
  1071. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -0
  1072. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
  1073. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +294 -25
  1074. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
  1075. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
  1076. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
  1077. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +14 -0
  1078. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -0
  1079. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
  1080. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
  1081. package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
  1082. package/src/modules/workflow/event-bus.ts +26 -28
  1083. package/src/modules/workflow/events.routes.ts +56 -21
  1084. package/src/modules/workflow/file-lock.service.ts +27 -4
  1085. package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
  1086. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
  1087. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
  1088. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
  1089. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
  1090. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
  1091. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
  1092. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
  1093. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
  1094. package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
  1095. package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
  1096. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
  1097. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
  1098. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
  1099. package/src/modules/workflow/git/change-request-link.ts +35 -0
  1100. package/src/modules/workflow/git/git-version.ts +5 -2
  1101. package/src/modules/workflow/git/git.service.ts +684 -144
  1102. package/src/modules/workflow/git/node-git-runner.ts +315 -0
  1103. package/src/modules/workflow/git/pull-request.service.ts +179 -27
  1104. package/src/modules/workflow/pending-commits.service.ts +26 -7
  1105. package/src/modules/workflow/pending-commits.worker.ts +114 -69
  1106. package/src/modules/workflow/recovery-bot.ts +4 -1
  1107. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
  1108. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
  1109. package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
  1110. package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
  1111. package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
  1112. package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
  1113. package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
  1114. package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
  1115. package/src/modules/workflow/sanitize-error.ts +9 -4
  1116. package/src/modules/workflow/workflow.routes.ts +273 -29
  1117. package/src/modules/workflow/workflow.service.ts +1087 -171
  1118. package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
  1119. package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
  1120. package/src/modules/workspace/__tests__/git-internals.security.test.ts +792 -0
  1121. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
  1122. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
  1123. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
  1124. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
  1125. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
  1126. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
  1127. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
  1128. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
  1129. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
  1130. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +25 -6
  1131. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
  1132. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  1133. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
  1134. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +56 -1
  1135. package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
  1136. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
  1137. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
  1138. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
  1139. package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
  1140. package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
  1141. package/src/modules/workspace/empty-dirs.ts +79 -0
  1142. package/src/modules/workspace/file-readers/README.md +32 -0
  1143. package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
  1144. package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
  1145. package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
  1146. package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
  1147. package/src/modules/workspace/file-readers/content-mode.ts +87 -0
  1148. package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
  1149. package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
  1150. package/src/modules/workspace/file-readers/document-reader.ts +6 -0
  1151. package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
  1152. package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
  1153. package/src/modules/workspace/file-readers/file-reader.ts +27 -6
  1154. package/src/modules/workspace/file-readers/image-reader.ts +10 -5
  1155. package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
  1156. package/src/modules/workspace/file-readers/text-reader.ts +61 -4
  1157. package/src/modules/workspace/git-internals.middleware.ts +86 -0
  1158. package/src/modules/workspace/not-found.ts +119 -0
  1159. package/src/modules/workspace/session-ontology.gate.ts +1 -1
  1160. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
  1161. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
  1162. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
  1163. package/src/modules/workspace/startup/beside-checkout.ts +112 -0
  1164. package/src/modules/workspace/startup/kb-git.ts +43 -36
  1165. package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
  1166. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1215 -73
  1167. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
  1168. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
  1169. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
  1170. package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
  1171. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +46 -85
  1172. package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
  1173. package/src/modules/workspace/startup/steps/seed-tree.ts +159 -78
  1174. package/src/modules/workspace/startup/steps/template-files.step.ts +492 -141
  1175. package/src/modules/workspace/startup/steps/template-source.ts +168 -0
  1176. package/src/modules/workspace/workspace.routes.ts +687 -257
  1177. package/src/modules/workspace/workspace.service.ts +991 -303
  1178. package/src/modules/workspace/workspace.tools.ts +1787 -180
  1179. package/src/modules/workspace/write-denial.ts +189 -0
  1180. package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
  1181. package/src/shared/__tests__/email-identity.test.ts +35 -0
  1182. package/src/shared/__tests__/filename.test.ts +0 -0
  1183. package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
  1184. package/src/shared/__tests__/git-failure.test.ts +198 -0
  1185. package/src/shared/__tests__/git-internals.test.ts +115 -0
  1186. package/src/shared/__tests__/http-errors.test.ts +42 -0
  1187. package/src/shared/__tests__/logging.test.ts +185 -0
  1188. package/src/shared/__tests__/path-containment.test.ts +60 -0
  1189. package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
  1190. package/src/shared/__tests__/printable.test.ts +64 -0
  1191. package/src/shared/__tests__/redact-secret.test.ts +122 -0
  1192. package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
  1193. package/src/shared/__tests__/ssrf.test.ts +20 -0
  1194. package/src/shared/canonical-file-identity.ts +92 -0
  1195. package/src/shared/domain-errors.ts +149 -0
  1196. package/src/shared/email-identity.ts +38 -0
  1197. package/src/shared/fs.contract.ts +225 -0
  1198. package/src/shared/git-failure.ts +196 -0
  1199. package/src/shared/git-internals.ts +124 -0
  1200. package/src/shared/git.contract.ts +170 -0
  1201. package/src/shared/http-errors.ts +36 -0
  1202. package/src/shared/logger.contract.ts +141 -0
  1203. package/src/shared/logging.ts +96 -0
  1204. package/src/shared/path-containment.ts +34 -0
  1205. package/src/shared/pending-proposals.ts +161 -0
  1206. package/src/shared/printable.ts +68 -0
  1207. package/src/shared/redact-secret.ts +118 -0
  1208. package/src/shared/rename-no-replace.ts +386 -0
  1209. package/src/shared/ssrf.ts +44 -16
  1210. package/src/version.ts +10 -6
  1211. package/src/modules/diff/diff-paths.ts +0 -9
  1212. package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
  1213. package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
  1214. package/src/modules/mcp/mcp-session-store.ts +0 -162
  1215. package/src/shared/__tests__/fs-walk.test.ts +0 -63
  1216. package/src/shared/__tests__/kb-walk.test.ts +0 -107
  1217. package/src/shared/fs-errors.ts +0 -10
  1218. package/src/shared/fs-walk.ts +0 -45
  1219. package/src/shared/hash-email.ts +0 -14
  1220. package/src/shared/kb-walk.ts +0 -95
@@ -1,7 +1,6 @@
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';
5
4
  import type {
6
5
  AuthUser,
7
6
  BranchInfo,
@@ -13,11 +12,13 @@ import type {
13
12
  ShareChangesRequest,
14
13
  WorkingTreeStatus,
15
14
  } from '@bevel-software/platform-shared';
15
+ import { isFolderPlaceholder } from '@bevel-software/platform-shared';
16
16
  import type { WorkspaceService } from '../../workspace/workspace.service.js';
17
17
  import type { WorkflowHooks, CommitValidationContext } from '../workflow-hooks.js';
18
18
  import type { IAccessControl } from '../../access/access-control.interface.js';
19
19
  import { AccessDeniedError } from '../../access-model/access-errors.js';
20
20
  import { WorkspaceMutex } from '../../kb-fs/mutex.js';
21
+ import { isAbsence } from '../../../shared/fs.contract.js';
21
22
  import { assertInsideRepo } from '../../kb-fs/repo-path.js';
22
23
  import { cloneTrackingConfigArgs, SAFE_IMPLICIT_FETCH_ARGS } from '../../kb-fs/clone-config.js';
23
24
  import {
@@ -30,24 +31,28 @@ import {
30
31
  } from '../../kb-fs/branch-name.js';
31
32
  import {
32
33
  BranchAuthorshipError,
34
+ WorkflowDomainError,
33
35
  WorkflowValidationError,
34
36
  ProtectedBranchError,
35
37
  PullRebaseConflictError,
36
38
  RemoteBranchGoneError,
37
39
  isMissingRemoteBranchFailure,
38
40
  } from '../../../shared/domain-errors.js';
41
+ import {
42
+ GitRunError,
43
+ isGitTimeout,
44
+ redactGitToken,
45
+ type GitRunOptions,
46
+ type GitRunResult,
47
+ type IGitRunner,
48
+ } from '../../../shared/git.contract.js';
49
+ import { NodeGitRunner } from './node-git-runner.js';
50
+ import { printable } from '../../../shared/printable.js';
51
+ import { sanitizeError } from '../sanitize-error.js';
39
52
 
40
- const execFileAsync = promisify(execFile);
53
+ const log = logger('git');
54
+ const crLog = logger('cr');
41
55
 
42
- interface GitRunResult {
43
- stdout: string;
44
- stderr: string;
45
- }
46
-
47
- function redact(msg: string): string {
48
- const token = process.env.GITHUB_TOKEN;
49
- return token ? msg.replaceAll(token, '***') : msg;
50
- }
51
56
 
52
57
  /**
53
58
  * Fallback committer identity. Every workflow commit overrides the author via
@@ -243,24 +248,22 @@ async function synthesizeUntrackedSideDiff(
243
248
  ? synthesizeNewFileDiff(relativePath, text)
244
249
  : synthesizeDeletedFileDiff(relativePath, text);
245
250
  } catch (err) {
246
- if ((err as NodeJS.ErrnoException).code === 'ENOENT') return '';
251
+ if (isAbsence(err)) return '';
247
252
  throw err;
248
253
  }
249
254
  }
250
255
 
251
256
  /**
252
- * `stat` for the stray check: false only when nothing can be there (ENOENT,
253
- * or ENOTDIR when a parent segment is a file). Permission and I/O failures
254
- * propagate: treating them as "absent" would let an unreadable stray pass as
255
- * committed.
257
+ * `stat` for the stray check: false only when nothing can be there — the
258
+ * disk's own definition of absence. Permission and I/O failures propagate:
259
+ * treating them as "absent" would let an unreadable stray pass as committed.
256
260
  */
257
261
  async function existsForCommitCheck(absolutePath: string): Promise<boolean> {
258
262
  try {
259
263
  await fs.stat(absolutePath);
260
264
  return true;
261
265
  } catch (err) {
262
- const code = (err as NodeJS.ErrnoException).code;
263
- if (code === 'ENOENT' || code === 'ENOTDIR') return false;
266
+ if (isAbsence(err)) return false;
264
267
  throw err;
265
268
  }
266
269
  }
@@ -296,6 +299,14 @@ export class GitService implements IGitService {
296
299
  private readonly kbDirName: string,
297
300
  private readonly mutex: WorkspaceMutex = new WorkspaceMutex(),
298
301
  private readonly accessControl: IAccessControl | null = null,
302
+ /**
303
+ * How git is actually run — see `shared/git.contract.ts`. The composition
304
+ * root passes the one runner, carrying the deployment's configured
305
+ * deadline; the default here is the same runner on its default deadline,
306
+ * so a directly constructed service (every suite in this module) still has
307
+ * one rather than none.
308
+ */
309
+ private readonly gitRunner: IGitRunner = new NodeGitRunner(),
299
310
  ) {}
300
311
 
301
312
  /**
@@ -312,6 +323,19 @@ export class GitService implements IGitService {
312
323
  this.pendingCommitsProbe = probe;
313
324
  }
314
325
 
326
+ /**
327
+ * Late-bound by the composition root: notified with the branch names every
328
+ * `listBranches` returns. A listing is the one moment the platform sees
329
+ * origin's set of branches, and the workspace layer needs that memory to
330
+ * tell a branch that was deleted from a name that never existed — see
331
+ * `WorkspaceService.noteBranchesListed`.
332
+ */
333
+ private onBranchesListed: ((names: string[]) => void) | null = null;
334
+
335
+ setBranchesListedListener(listener: (names: string[]) => void): void {
336
+ this.onBranchesListed = listener;
337
+ }
338
+
315
339
  /**
316
340
  * Mark a workspace as freshly fetched. Called by `WorkspaceService` right
317
341
  * after a branch's clone is created — `git clone` already downloaded every
@@ -327,6 +351,22 @@ export class GitService implements IGitService {
327
351
  this.lastImplicitFetchOk.set(workspaceId, true);
328
352
  }
329
353
 
354
+ /**
355
+ * The most recent attempt any workspace made to reach the remote, and
356
+ * whether it succeeded — for the readiness answer. Null before the first
357
+ * attempt of this process's life. Read off the per-workspace fetch record
358
+ * above rather than kept separately, so it cannot disagree with it.
359
+ */
360
+ lastRemoteContact(): { at: number; ok: boolean } | null {
361
+ let latest: { at: number; ok: boolean } | null = null;
362
+ for (const [workspaceId, at] of this.lastImplicitFetchAt) {
363
+ if (latest === null || at > latest.at) {
364
+ latest = { at, ok: this.lastImplicitFetchOk.get(workspaceId) ?? false };
365
+ }
366
+ }
367
+ return latest;
368
+ }
369
+
330
370
  /**
331
371
  * Run the registered ADVISORY commit-validation hooks at a commit site.
332
372
  * Preserves the semantics of the injected validator this replaced: a
@@ -343,11 +383,11 @@ export class GitService implements IGitService {
343
383
  try {
344
384
  const report = await hook(ctx);
345
385
  if (report && report.mustFix.length > 0) {
346
- console.warn(formatWarning(report));
386
+ log.warn(formatWarning(report));
347
387
  }
348
388
  } catch (validatorErr) {
349
389
  // Validator failure is non-fatal — advisory only.
350
- console.warn('[git] validator crashed (advisory only, ignoring):', validatorErr instanceof Error ? validatorErr.message : validatorErr);
390
+ log.warn('validator crashed (advisory only, ignoring):', { err: validatorErr });
351
391
  }
352
392
  }
353
393
  }
@@ -520,6 +560,17 @@ export class GitService implements IGitService {
520
560
  }
521
561
  }),
522
562
  );
563
+ // Every name this listing showed — origin's refs and the local heads
564
+ // alike, since a branch checked out here is one the platform has
565
+ // plainly heard of. A listener that misbehaves must not turn a good
566
+ // listing into a failed one.
567
+ try {
568
+ this.onBranchesListed?.(infos.map((info) => info.name));
569
+ } catch (err) {
570
+ log.error(`branches-listed listener failed for workspace ${workspaceId}:`, {
571
+ detail: err instanceof Error ? err.message : String(err),
572
+ });
573
+ }
523
574
  return infos;
524
575
  });
525
576
  }
@@ -575,7 +626,7 @@ export class GitService implements IGitService {
575
626
  this.lastImplicitFetchOk.set(workspaceId, true);
576
627
  } catch (err) {
577
628
  const msg = err instanceof Error ? err.message : String(err);
578
- console.warn(`[git] implicit fetch failed for workspace ${workspaceId}: ${msg}`);
629
+ log.warn(`implicit fetch failed for workspace ${workspaceId}: ${msg}`);
579
630
  // Stamp the TTL on failure too so a hard-down origin doesn't make
580
631
  // every subsequent listBranches retry the (still-failing) fetch and
581
632
  // pile up 10s timeouts. Stale local refs are better than spinning.
@@ -938,19 +989,13 @@ export class GitService implements IGitService {
938
989
  }
939
990
  if (trackedAtHead) {
940
991
  await this.git(cwd, ['checkout', 'HEAD', '--', repoRelativePath]).catch((err) => {
941
- console.warn(
942
- `[git] discardPath checkout failed for "${repoRelativePath}":`,
943
- err instanceof Error ? err.message : err,
944
- );
992
+ log.warn(`discardPath checkout failed for "${repoRelativePath}":`, { err });
945
993
  });
946
994
  } else {
947
995
  // Untracked new file — remove from working tree.
948
996
  const fileAbs = path.join(cwd, repoRelativePath);
949
997
  await fs.rm(fileAbs, { force: true }).catch((err) => {
950
- console.warn(
951
- `[git] discardPath rm failed for "${repoRelativePath}":`,
952
- err instanceof Error ? err.message : err,
953
- );
998
+ log.warn(`discardPath rm failed for "${repoRelativePath}":`, { err });
954
999
  });
955
1000
  }
956
1001
  });
@@ -966,8 +1011,30 @@ export class GitService implements IGitService {
966
1011
  assertValidRelativePath(relativePath);
967
1012
  assertValidAuthor(user);
968
1013
  const repoRelativePath = this.stripRepoPrefix(relativePath);
969
- return this.mutex.run(workspaceId, async () => {
970
- const cwd = await this.repoDir(workspaceId);
1014
+ return this.mutex.run(workspaceId, async () =>
1015
+ this.commitFileUnlocked(
1016
+ workspaceId,
1017
+ await this.repoDir(workspaceId),
1018
+ user,
1019
+ relativePath,
1020
+ repoRelativePath,
1021
+ summary,
1022
+ skipValidator,
1023
+ ),
1024
+ );
1025
+ }
1026
+
1027
+ /** `commitFile`'s body, for a caller that already holds the workspace's reservation. */
1028
+ private async commitFileUnlocked(
1029
+ workspaceId: string,
1030
+ cwd: string,
1031
+ user: AuthUser,
1032
+ relativePath: string,
1033
+ repoRelativePath: string,
1034
+ summary?: string,
1035
+ skipValidator?: boolean,
1036
+ ): Promise<CommitAttribution | null> {
1037
+ {
971
1038
  const branch = await this.currentBranch(cwd);
972
1039
 
973
1040
  // Path-scoped status — is this specific file dirty? `--porcelain` on
@@ -1038,7 +1105,7 @@ export class GitService implements IGitService {
1038
1105
  subject: subj ?? subject,
1039
1106
  committedAt: committedAt?.trim() ?? new Date().toISOString(),
1040
1107
  };
1041
- });
1108
+ }
1042
1109
  }
1043
1110
 
1044
1111
  /**
@@ -1195,8 +1262,19 @@ export class GitService implements IGitService {
1195
1262
  user: AuthUser,
1196
1263
  opts?: { systemAuthorized?: boolean },
1197
1264
  ): Promise<void> {
1198
- return this.mutex.run(workspaceId, async () => {
1199
- const cwd = await this.repoDir(workspaceId);
1265
+ return this.mutex.run(workspaceId, async () =>
1266
+ this.pushUnlocked(workspaceId, await this.repoDir(workspaceId), user, opts),
1267
+ );
1268
+ }
1269
+
1270
+ /** `push`'s body, for a caller that already holds the workspace's reservation. */
1271
+ private async pushUnlocked(
1272
+ workspaceId: string,
1273
+ cwd: string,
1274
+ user: AuthUser,
1275
+ opts?: { systemAuthorized?: boolean },
1276
+ ): Promise<void> {
1277
+ {
1200
1278
  const branch = await this.currentBranch(cwd);
1201
1279
 
1202
1280
  // Access gate fires only when pushing to a protected branch. Pushing
@@ -1225,7 +1303,7 @@ export class GitService implements IGitService {
1225
1303
  }
1226
1304
 
1227
1305
  await this.git(cwd, ['push', '-u', 'origin', branch]);
1228
- });
1306
+ }
1229
1307
  }
1230
1308
 
1231
1309
  // `forcePush` method removed. The workflow layer no longer auto-force-pushes
@@ -1358,6 +1436,34 @@ export class GitService implements IGitService {
1358
1436
  targetBranch: string,
1359
1437
  commit: { subject: string; body: string },
1360
1438
  user: AuthUser,
1439
+ opts: {
1440
+ /**
1441
+ * Refuse before touching the clone when the target's working tree holds
1442
+ * edits that are not published yet. A change request's merge runs in a
1443
+ * repo-global clone where nothing but a previous attempt can be dirty;
1444
+ * this method is also the agent `merge_branch` path, which runs in the
1445
+ * TARGET BRANCH'S OWN workspace — the one the file tools read and write.
1446
+ * There the `reset --hard` below would silently destroy a save.
1447
+ *
1448
+ * Checked HERE, inside the reservation, and not by the caller: a save
1449
+ * landing between a caller's check and this reset is exactly the window
1450
+ * that has to be closed.
1451
+ */
1452
+ requireCleanTarget?: boolean;
1453
+ /**
1454
+ * Decide whether `user` may land this merge, given the target commit it
1455
+ * is actually built on and every path it writes. Throwing refuses, and
1456
+ * refuses before anything is committed or pushed.
1457
+ *
1458
+ * Also here rather than in the caller, and for the same reason: the
1459
+ * caller's workspace `HEAD` can be behind origin (a best-effort pull
1460
+ * that failed leaves it there), so authorizing against it would read
1461
+ * roles that the commit being published has already changed. The fetch
1462
+ * below is what makes the ref current, and a fetch that fails throws
1463
+ * instead of falling back to the stale one.
1464
+ */
1465
+ authorize?: (target: { sha: string; changedPaths: string[] }) => Promise<void>;
1466
+ } = {},
1361
1467
  ): Promise<{ kind: 'merged'; sha: string } | { kind: 'conflicts'; paths: string[] }> {
1362
1468
  assertValidBranchName(sourceBranch);
1363
1469
  assertValidBranchName(targetBranch);
@@ -1371,6 +1477,17 @@ export class GitService implements IGitService {
1371
1477
  // unknown" on a clone that lacks it).
1372
1478
  await this.git(cwd, ['config', 'user.name', BOT_NAME]);
1373
1479
  await this.git(cwd, ['config', 'user.email', BOT_EMAIL]);
1480
+ if (opts.requireCleanTarget) {
1481
+ const { stdout: dirty } = await this.git(cwd, ['status', '--porcelain=v1', '-z']);
1482
+ const unpushed = await this.hasUnpushedCommitsAt(cwd);
1483
+ if (parsePorcelainZ(dirty).length > 0 || unpushed) {
1484
+ throw new WorkflowDomainError(
1485
+ `"${targetBranch}" has edits that are not shared yet. Try the merge again once they are saved.`,
1486
+ 409,
1487
+ { kind: 'merge-target-busy', targetBranch },
1488
+ );
1489
+ }
1490
+ }
1374
1491
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
1375
1492
  // Explicit destination refspecs, because the two `origin/<branch>`
1376
1493
  // reads below MUST see the tips this fetch just retrieved. A bare
@@ -1398,6 +1515,20 @@ export class GitService implements IGitService {
1398
1515
  await this.git(cwd, ['checkout', targetBranch]);
1399
1516
  await this.git(cwd, ['reset', '--hard', `origin/${targetBranch}`]);
1400
1517
 
1518
+ if (opts.authorize) {
1519
+ // The tip this attempt merges into, and the paths it writes against
1520
+ // that tip. Re-read every attempt: a retry happens BECAUSE the
1521
+ // target moved, which can move its roles and its file set too.
1522
+ const { stdout: targetSha } = await this.git(cwd, ['rev-parse', `origin/${targetBranch}`]);
1523
+ const changedPaths = await this.prChangedPathsAt(
1524
+ cwd,
1525
+ `origin/${targetBranch}`,
1526
+ `origin/${sourceBranch}`,
1527
+ { forAccessCheck: true },
1528
+ );
1529
+ await opts.authorize({ sha: targetSha.trim(), changedPaths });
1530
+ }
1531
+
1401
1532
  // `--no-commit --no-ff` so we author the merge commit as the human and
1402
1533
  // never fast-forward past the merge record.
1403
1534
  try {
@@ -1409,7 +1540,7 @@ export class GitService implements IGitService {
1409
1540
  // Not a conflict — surface the underlying git error so the real cause
1410
1541
  // (e.g. missing identity, unrelated histories) is diagnosable.
1411
1542
  throw new Error(
1412
- `git merge failed without detectable conflicts: ${redact(
1543
+ `git merge failed without detectable conflicts: ${redactGitToken(
1413
1544
  err instanceof Error ? err.message : String(err),
1414
1545
  )}`,
1415
1546
  );
@@ -1590,7 +1721,9 @@ export class GitService implements IGitService {
1590
1721
  try {
1591
1722
  const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', rev]);
1592
1723
  return stdout.trim() || null;
1593
- } catch {
1724
+ } catch (err) {
1725
+ // Null is "no such rev"; a deadline is not that answer (see GitRunError).
1726
+ if (isGitTimeout(err)) throw err;
1594
1727
  return null;
1595
1728
  }
1596
1729
  }
@@ -1816,20 +1949,129 @@ export class GitService implements IGitService {
1816
1949
  * only ever see settled states.
1817
1950
  */
1818
1951
  async hasUnpushedCommits(workspaceId: string): Promise<boolean> {
1819
- return this.mutex.run(workspaceId, async () => {
1820
- const cwd = await this.repoDir(workspaceId);
1821
- const branch = await this.currentBranch(cwd);
1822
- try {
1823
- const { stdout } = await this.git(cwd, [
1824
- 'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
1825
- ]);
1826
- return Number(stdout.trim()) > 0;
1827
- } catch {
1828
- return true;
1829
- }
1830
- });
1952
+ return this.mutex.run(workspaceId, async () => this.hasUnpushedCommitsAt(await this.repoDir(workspaceId)));
1953
+ }
1954
+
1955
+ /**
1956
+ * `hasUnpushedCommits` without the workspace reservation — for a caller that
1957
+ * already holds one (`mergeChangeRequest`, which has to ask inside the same
1958
+ * reservation as the reset that would discard the answer). Fails closed: a
1959
+ * question that cannot be answered counts as "yes, unpushed".
1960
+ */
1961
+ private async hasUnpushedCommitsAt(cwd: string): Promise<boolean> {
1962
+ const branch = await this.currentBranch(cwd);
1963
+ try {
1964
+ const { stdout } = await this.git(cwd, [
1965
+ 'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
1966
+ ]);
1967
+ return Number(stdout.trim()) > 0;
1968
+ } catch {
1969
+ return true;
1970
+ }
1971
+ }
1972
+
1973
+ /**
1974
+ * Revert paths in several workspaces and publish the result as ONE git
1975
+ * operation. Every workspace involved is reserved together (`runAll`), and
1976
+ * reading each HEAD, restoring and committing every path, pushing each
1977
+ * branch and — when a step fails — undoing, all happen inside that single
1978
+ * reservation: no other git operation on those workspaces can land between
1979
+ * the HEAD an undo returns to and the commits it undoes.
1980
+ *
1981
+ * - Each workspace's HEAD is read first; one that can't be read throws
1982
+ * before anything changes.
1983
+ * - Every path in every workspace is restored from its `ref` and committed
1984
+ * (`subject`) before anything is pushed. A failed restore or commit
1985
+ * undoes all of them and rethrows: nothing is pushed.
1986
+ * - Branches are then pushed in order. A failed push undoes that workspace
1987
+ * and every later one and is reported in `failed`; the earlier pushes
1988
+ * stand, since a remote can't be un-pushed.
1989
+ *
1990
+ * An undo never rewrites history: each touched path is restored from the
1991
+ * HEAD recorded at the start and committed (`undoSubject`), so unpushed work
1992
+ * the checkout already had stays exactly as it was. Each path is undone on
1993
+ * its own, so one that fails doesn't leave the rest reverted.
1994
+ */
1995
+ async revertPathsAndPush(
1996
+ user: AuthUser,
1997
+ plans: {
1998
+ workspaceId: string;
1999
+ paths: { path: string; ref: string; subject: string; undoSubject: string }[];
2000
+ }[],
2001
+ ): Promise<{ pushed: string[]; failed: { workspaceId: string; error: unknown } | null }> {
2002
+ assertValidAuthor(user);
2003
+ for (const plan of plans) for (const entry of plan.paths) assertValidRelativePath(entry.path);
2004
+ return this.mutex.runAll(
2005
+ plans.map((plan) => plan.workspaceId),
2006
+ async () => {
2007
+ const steps: {
2008
+ workspaceId: string;
2009
+ cwd: string;
2010
+ head: string;
2011
+ paths: (typeof plans)[number]['paths'];
2012
+ }[] = [];
2013
+ for (const plan of plans) {
2014
+ const cwd = await this.repoDir(plan.workspaceId);
2015
+ const { stdout } = await this.git(cwd, ['rev-parse', '--verify', 'HEAD^{commit}']);
2016
+ steps.push({ workspaceId: plan.workspaceId, cwd, head: stdout.trim(), paths: plan.paths });
2017
+ }
2018
+ const undo = async (unpushed: typeof steps) => {
2019
+ for (const step of unpushed) {
2020
+ for (const entry of step.paths) {
2021
+ try {
2022
+ await this.restorePathUnlocked(step.cwd, step.head, entry.path);
2023
+ await this.commitFileUnlocked(
2024
+ step.workspaceId,
2025
+ step.cwd,
2026
+ user,
2027
+ entry.path,
2028
+ entry.path,
2029
+ entry.undoSubject,
2030
+ true, // skipValidator — this puts back the checkout's own version
2031
+ );
2032
+ } catch (err) {
2033
+ log.warn(
2034
+ `undo of ${printable(entry.path)} failed in workspace ${printable(step.workspaceId)}: ${printable(sanitizeError(err))}`,
2035
+ );
2036
+ }
2037
+ }
2038
+ }
2039
+ };
2040
+ try {
2041
+ for (const step of steps) {
2042
+ for (const entry of step.paths) {
2043
+ await this.restorePathUnlocked(step.cwd, entry.ref, entry.path);
2044
+ await this.commitFileUnlocked(
2045
+ step.workspaceId,
2046
+ step.cwd,
2047
+ user,
2048
+ entry.path,
2049
+ entry.path,
2050
+ entry.subject,
2051
+ true, // skipValidator — this restores an already-validated version
2052
+ );
2053
+ }
2054
+ }
2055
+ } catch (err) {
2056
+ await undo(steps);
2057
+ throw err;
2058
+ }
2059
+ const pushed: string[] = [];
2060
+ for (const [i, step] of steps.entries()) {
2061
+ try {
2062
+ await this.pushUnlocked(step.workspaceId, step.cwd, user);
2063
+ } catch (error) {
2064
+ await undo(steps.slice(i));
2065
+ return { pushed, failed: { workspaceId: step.workspaceId, error } };
2066
+ }
2067
+ pushed.push(step.workspaceId);
2068
+ }
2069
+ return { pushed, failed: null };
2070
+ },
2071
+ );
1831
2072
  }
1832
2073
 
2074
+
1833
2075
  /**
1834
2076
  * Hard-reset the workspace's checked-out branch to `origin/<branch>`, fetching
1835
2077
  * first. This is the break-glass primitive behind roles.yaml recovery: it
@@ -2219,8 +2461,8 @@ export class GitService implements IGitService {
2219
2461
  const cwd = await this.repoDir(workspaceId);
2220
2462
  await this.fetchPrRefs(cwd, baseBranch, headBranch);
2221
2463
  return this.mutex.run(workspaceId, async () => {
2222
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
2223
- const headRef = await this.resolveBranchRef(cwd, headBranch);
2464
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2465
+ const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
2224
2466
  const [{ stdout: baseSha }, { stdout: headSha }] = await Promise.all([
2225
2467
  this.git(cwd, ['rev-parse', baseRef]),
2226
2468
  this.git(cwd, ['rev-parse', headRef]),
@@ -2269,8 +2511,8 @@ export class GitService implements IGitService {
2269
2511
  const cwd = await this.repoDir(workspaceId);
2270
2512
  if (!opts.at) await this.fetchPrRefs(cwd, baseBranch, headBranch);
2271
2513
  return this.mutex.run(workspaceId, async () => {
2272
- const baseRef = opts.at ? opts.at.baseSha : await this.resolveBranchRef(cwd, baseBranch);
2273
- const headRef = opts.at ? opts.at.headSha : await this.resolveBranchRef(cwd, headBranch);
2514
+ const baseRef = opts.at ? opts.at.baseSha : await this.resolvePublishedBranchRef(cwd, baseBranch);
2515
+ const headRef = opts.at ? opts.at.headSha : await this.resolvePublishedBranchRef(cwd, headBranch);
2274
2516
  const range = `${baseRef}...${headRef}`; // three-dot = changes on head since merge-base
2275
2517
 
2276
2518
  const [{ stdout: nameStatusOut }, { stdout: numstatOut }] = await Promise.all([
@@ -2286,8 +2528,17 @@ export class GitService implements IGitService {
2286
2528
  // land. Filter it from the review surface entirely; the neutralisation
2287
2529
  // reads the raw refs itself and is unaffected. Both lists are filtered
2288
2530
  // IN STEP so the index-zip below stays aligned.
2289
- if (statuses.some((s) => s.path === 'roles.yaml')) {
2290
- const keep = statuses.map((s) => s.path !== 'roles.yaml');
2531
+ // The empty-folder placeholder is filtered the same way: it is never
2532
+ // content, so it is not a file to review or approve. It still merges
2533
+ // with the rest, and `changedPathsForPr` keeps it, so a request that
2534
+ // only creates a folder is not mistaken for an empty one and closed.
2535
+ // An empty file deleted as its folder gets the (equally empty)
2536
+ // placeholder reads to `-M` as a RENAME onto it; the real side of such
2537
+ // a pair is first made the plain removal (or addition) it is, so
2538
+ // dropping the placeholder never drops the file with it.
2539
+ statuses = statuses.map(withoutPlaceholderRename);
2540
+ if (statuses.some((s) => s.path === 'roles.yaml' || isFolderPlaceholder(s.path))) {
2541
+ const keep = statuses.map((s) => s.path !== 'roles.yaml' && !isFolderPlaceholder(s.path));
2291
2542
  statuses = statuses.filter((_, i) => keep[i]);
2292
2543
  if (counts.length === keep.length) counts = counts.filter((_, i) => keep[i]);
2293
2544
  }
@@ -2299,8 +2550,8 @@ export class GitService implements IGitService {
2299
2550
  // +/- to a file.
2300
2551
  const aligned = counts.length === statuses.length;
2301
2552
  if (!aligned) {
2302
- console.warn(
2303
- `[cr] diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
2553
+ crLog.warn(
2554
+ `diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
2304
2555
  `for ${range} — reporting file list without +/- counts`,
2305
2556
  );
2306
2557
  }
@@ -2336,35 +2587,103 @@ export class GitService implements IGitService {
2336
2587
  * Just the repo-relative paths changed by a change request (three-dot), no
2337
2588
  * patches — the cheap version used to build CR-list summaries and owner
2338
2589
  * routing. `changedFilesForPr` is the full version with statuses + diffs.
2590
+ *
2591
+ * `fetch: false` skips the per-request ref fetch, for a caller that has
2592
+ * ALREADY refreshed the clone's remote-tracking refs — which is the whole
2593
+ * clone in one round trip, against one per request here. A CR list is
2594
+ * exactly that caller: at five open requests the per-request fetch was
2595
+ * measured at ~0.55s each, and the list is re-read on every proposal and
2596
+ * every 60s poll. Only pass it when a fetch of the clone really has just
2597
+ * happened; the refs are otherwise as old as the last one, and the diff
2598
+ * would describe a stale head.
2599
+ *
2600
+ * It is a skip, not a promise never to fetch: a branch this clone has
2601
+ * never heard of is fetched anyway. That case is not a stale diff, it is
2602
+ * NO diff — an empty touched-path set, which is a change request missing
2603
+ * from its own author's tree. Two local `rev-parse`s are worth not being
2604
+ * that.
2339
2605
  */
2340
2606
  async changedPathsForPr(
2341
2607
  workspaceId: string,
2342
2608
  baseBranch: string,
2343
2609
  headBranch: string,
2610
+ opts: {
2611
+ fetch?: boolean;
2612
+ /**
2613
+ * Every path the change WRITES, for authorizing it rather than
2614
+ * describing it: `roles.yaml` kept (a merge, unlike a change request's,
2615
+ * does not strip it) and BOTH sides of every rename. A rename writes the
2616
+ * old path too — it deletes it — so an access check that saw only the
2617
+ * new name would clear a caller who cannot touch what the merge removes.
2618
+ */
2619
+ forAccessCheck?: boolean;
2620
+ } = {},
2344
2621
  ): Promise<string[]> {
2345
2622
  assertValidBranchName(baseBranch);
2346
2623
  assertValidBranchName(headBranch);
2347
2624
  // Fetch outside the mutex (network round-trip) so origin latency can't hold
2348
2625
  // the workspace lock; the lock guards only the local diff below.
2349
2626
  const cwd = await this.repoDir(workspaceId);
2350
- await this.fetchPrRefs(cwd, baseBranch, headBranch);
2627
+ // The skip decision and the diff are ONE reading of the refs, not two.
2628
+ // Both are resolved here, to COMMITS, and the commits are what the diff
2629
+ // below runs on — the same pinning `changedFilesForPr`'s `at` does. A
2630
+ // concurrent `fetch --prune origin` (`ensureRemotesFetched` and
2631
+ // `fetchPrRefs` both run outside this mutex, deliberately) can drop a
2632
+ // remote-tracking ref between the two, and re-resolving inside the mutex
2633
+ // would then throw `WorkflowValidationError` — which `touchedPathsFor`
2634
+ // swallows into an empty touched-path set, i.e. exactly the change
2635
+ // request missing from its own author's tree that the skip is guarded
2636
+ // against. The commits stay readable whatever happens to the ref names.
2637
+ const pinned =
2638
+ opts.fetch === false ? await this.publishedPrCommits(cwd, baseBranch, headBranch) : null;
2639
+ if (!pinned) {
2640
+ await this.fetchPrRefs(cwd, baseBranch, headBranch);
2641
+ }
2351
2642
  return this.mutex.run(workspaceId, async () => {
2352
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
2353
- const headRef = await this.resolveBranchRef(cwd, headBranch);
2354
- const { stdout } = await this.git(cwd, [
2355
- 'diff', '-M', '--name-only', `${baseRef}...${headRef}`,
2356
- ]);
2357
- return (
2358
- stdout
2359
- .split('\n')
2360
- .map((s) => s.trim())
2361
- .filter(Boolean)
2643
+ const baseRef = pinned ? pinned.base : await this.resolvePublishedBranchRef(cwd, baseBranch);
2644
+ const headRef = pinned ? pinned.head : await this.resolvePublishedBranchRef(cwd, headBranch);
2645
+ return this.prChangedPathsAt(cwd, baseRef, headRef, opts);
2646
+ });
2647
+ }
2648
+
2649
+ /**
2650
+ * The shaping behind `changedPathsForPr`, without its workspace
2651
+ * reservation: the caller must already hold one. `mergeChangeRequest` reads
2652
+ * the same set inside its OWN reservation, where re-entering this one would
2653
+ * deadlock — and where the refs are the ones the merge is actually built on.
2654
+ */
2655
+ private async prChangedPathsAt(
2656
+ cwd: string,
2657
+ baseRef: string,
2658
+ headRef: string,
2659
+ opts: { forAccessCheck?: boolean } = {},
2660
+ ): Promise<string[]> {
2661
+ const { stdout } = await this.git(cwd, [
2662
+ 'diff', '-M', '-z', '--name-status', `${baseRef}...${headRef}`,
2663
+ ]);
2664
+ // Deduped: both sides of a rename can collide with another entry's path.
2665
+ return [
2666
+ ...new Set(
2667
+ parseNameStatusZ(stdout)
2668
+ // A rename onto or off the placeholder is a real file removed or
2669
+ // added (see `withoutPlaceholderRename`): both of its paths are
2670
+ // touched, or filtering the placeholder would lose the file.
2671
+ //
2672
+ // Authorizing the change needs both sides of EVERY rename, not just
2673
+ // the placeholder's: git reports a rename under its new name alone,
2674
+ // and the old name is a path the change deletes.
2675
+ .flatMap((s) =>
2676
+ s.previousPath &&
2677
+ (opts.forAccessCheck || isFolderPlaceholder(s.path) || isFolderPlaceholder(s.previousPath))
2678
+ ? [s.previousPath, s.path]
2679
+ : [s.path],
2680
+ )
2362
2681
  // Same rule as `changedFilesForPr`: a roles.yaml change never
2363
2682
  // survives a merge, so it is not a touched path for routing or
2364
2683
  // summaries either.
2365
- .filter((p) => p !== 'roles.yaml')
2366
- );
2367
- });
2684
+ .filter((p) => opts.forAccessCheck || p !== 'roles.yaml'),
2685
+ ),
2686
+ ];
2368
2687
  }
2369
2688
 
2370
2689
  /**
@@ -2383,8 +2702,8 @@ export class GitService implements IGitService {
2383
2702
  const cwd = await this.repoDir(workspaceId);
2384
2703
  await this.fetchPrRefs(cwd, baseBranch, headBranch);
2385
2704
  return this.mutex.run(workspaceId, async () => {
2386
- const baseRef = await this.resolveBranchRef(cwd, baseBranch);
2387
- const headRef = await this.resolveBranchRef(cwd, headBranch);
2705
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2706
+ const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
2388
2707
  try {
2389
2708
  const { stdout } = await this.git(cwd, ['merge-base', baseRef, headRef]);
2390
2709
  return stdout.trim() || null;
@@ -2397,6 +2716,143 @@ export class GitService implements IGitService {
2397
2716
  });
2398
2717
  }
2399
2718
 
2719
+ /**
2720
+ * Where a change request forked from its target, and whether the target has
2721
+ * moved on since — for the two commits a detail read has ALREADY resolved
2722
+ * (no fetch, no ref resolution, same contract as `changedFilesForPr`'s `at`).
2723
+ *
2724
+ * `mergeBaseSha` is the "before" every file in the request is read against,
2725
+ * so an edit made on the target after the proposal never shows as something
2726
+ * the proposal deletes. `behind` is true when the target holds commits the
2727
+ * proposal does not (`head..base` is non-empty) — the proposal needs
2728
+ * updating before its diff and its eventual merge describe the same text.
2729
+ * No shared history: `mergeBaseSha: null`, and `behind` stays honest (the
2730
+ * target certainly has commits the proposal lacks).
2731
+ */
2732
+ async forkPointForPr(
2733
+ workspaceId: string,
2734
+ at: { baseSha: string; headSha: string },
2735
+ ): Promise<{ mergeBaseSha: string | null; behind: boolean }> {
2736
+ for (const sha of [at.baseSha, at.headSha]) {
2737
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2738
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2739
+ }
2740
+ }
2741
+ const cwd = await this.repoDir(workspaceId);
2742
+ return this.mutex.run(workspaceId, async () => {
2743
+ let mergeBaseSha: string | null = null;
2744
+ try {
2745
+ const { stdout } = await this.git(cwd, ['merge-base', at.baseSha, at.headSha]);
2746
+ mergeBaseSha = stdout.trim() || null;
2747
+ } catch (err) {
2748
+ // Exit 1 is git's "no common ancestor"; anything else is infra.
2749
+ if ((err as { exitCode?: number }).exitCode !== 1) throw err;
2750
+ }
2751
+ const { stdout: count } = await this.git(cwd, [
2752
+ 'rev-list', '--count', `${at.headSha}..${at.baseSha}`,
2753
+ ]);
2754
+ return { mergeBaseSha, behind: Number.parseInt(count.trim(), 10) > 0 };
2755
+ });
2756
+ }
2757
+
2758
+ /**
2759
+ * Every repo-relative path whose content differs between two commits on a
2760
+ * change request's own branch — what an update from the target actually
2761
+ * CHANGED about the proposal.
2762
+ *
2763
+ * Two-dot, because that is the question: not "what does this request
2764
+ * propose" (three-dot, against the target) but "what did the merge move
2765
+ * under the reviewers who already approved". `--no-renames` is what makes
2766
+ * the answer the conservative one: git detects renames BY DEFAULT
2767
+ * (`diff.renames` has been on since 2.9) and then reports only the new
2768
+ * path, which would leave an approval on the path a merge renamed AWAY
2769
+ * reading as untouched. Switched off, a rename reports as the old path
2770
+ * removed and the new one added, so an approval on either side is correctly
2771
+ * read as touched — a path that appears here loses its approval, and only a
2772
+ * path that does not appear keeps it.
2773
+ */
2774
+ async pathsChangedBetween(
2775
+ workspaceId: string,
2776
+ fromSha: string,
2777
+ toSha: string,
2778
+ ): Promise<string[]> {
2779
+ for (const sha of [fromSha, toSha]) {
2780
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2781
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2782
+ }
2783
+ }
2784
+ const cwd = await this.repoDir(workspaceId);
2785
+ return this.mutex.run(workspaceId, async () => {
2786
+ const { stdout } = await this.git(cwd, [
2787
+ 'diff', '--no-renames', '-z', '--name-only', fromSha, toSha,
2788
+ ]);
2789
+ // `-z` terminates each name with NUL, so the trailing field is empty —
2790
+ // and a tracked name may legally carry leading or trailing spaces, so
2791
+ // nothing is trimmed here.
2792
+ return [...new Set(stdout.split('\0').filter((name) => name !== ''))];
2793
+ });
2794
+ }
2795
+
2796
+ /**
2797
+ * One file as it stood at a change request's fork point — the "before" side
2798
+ * of the request dialog's diff. `sha` must be a commit on the target
2799
+ * branch's published history (`origin/<baseBranch>`); anything else is
2800
+ * refused, so this can never become a read of an arbitrary commit. `null`
2801
+ * means the path did not exist at the fork point (the request adds it).
2802
+ */
2803
+ async readFileAtForkPoint(
2804
+ workspaceId: string,
2805
+ baseBranch: string,
2806
+ sha: string,
2807
+ relativePath: string,
2808
+ ): Promise<string | null> {
2809
+ assertValidBranchName(baseBranch);
2810
+ assertValidRelativePath(relativePath);
2811
+ if (!/^[0-9a-f]{40,64}$/.test(sha)) {
2812
+ throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
2813
+ }
2814
+ const repoRelativePath = this.stripRepoPrefix(relativePath);
2815
+ return this.mutex.run(workspaceId, async () => {
2816
+ const cwd = await this.repoDir(workspaceId);
2817
+ const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
2818
+ try {
2819
+ await this.git(cwd, ['merge-base', '--is-ancestor', sha, baseRef]);
2820
+ } catch (err) {
2821
+ if ((err as { exitCode?: number }).exitCode === 1) {
2822
+ throw new WorkflowDomainError(
2823
+ `That commit is not part of "${baseBranch}" — it is not this request's fork point.`,
2824
+ 404,
2825
+ );
2826
+ }
2827
+ throw err;
2828
+ }
2829
+ await this.assertNotTreeAtRef(cwd, sha, repoRelativePath, relativePath);
2830
+ return this.readFileAtRef(workspaceId, sha, repoRelativePath);
2831
+ });
2832
+ }
2833
+
2834
+ /**
2835
+ * Whether `repoRelativePath` exists (as a file or tree) at `ref`. ONLY
2836
+ * git's own "that path is not at this ref" answer is false; an unresolvable
2837
+ * ref, a timeout, a locked or corrupt repository PROPAGATE — a revert that
2838
+ * read one of those as "absent" would decide wrongly what to restore.
2839
+ */
2840
+ async pathExistsAtRef(workspaceId: string, ref: string, repoRelativePath: string): Promise<boolean> {
2841
+ assertValidRelativePath(repoRelativePath);
2842
+ return this.mutex.run(workspaceId, async () => {
2843
+ const cwd = await this.repoDir(workspaceId);
2844
+ try {
2845
+ await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
2846
+ return true;
2847
+ } catch (err) {
2848
+ const stderr =
2849
+ (err as { stderr?: string }).stderr ?? (err instanceof Error ? err.message : String(err));
2850
+ if (/does not exist in|exists on disk, but not in/.test(stderr)) return false;
2851
+ throw err;
2852
+ }
2853
+ });
2854
+ }
2855
+
2400
2856
  /**
2401
2857
  * Restore one path in the working tree (and index) to its content at `ref`;
2402
2858
  * a path ABSENT at `ref` is deleted — the revert of an added file is its
@@ -2410,8 +2866,14 @@ export class GitService implements IGitService {
2410
2866
  repoRelativePath: string,
2411
2867
  ): Promise<void> {
2412
2868
  assertValidRelativePath(repoRelativePath);
2413
- return this.mutex.run(workspaceId, async () => {
2414
- const cwd = await this.repoDir(workspaceId);
2869
+ return this.mutex.run(workspaceId, async () =>
2870
+ this.restorePathUnlocked(await this.repoDir(workspaceId), ref, repoRelativePath),
2871
+ );
2872
+ }
2873
+
2874
+ /** `restorePathFromRef`'s body, for a caller that already holds the workspace's reservation. */
2875
+ private async restorePathUnlocked(cwd: string, ref: string, repoRelativePath: string): Promise<void> {
2876
+ {
2415
2877
  let existsAtRef = true;
2416
2878
  try {
2417
2879
  await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
@@ -2433,7 +2895,7 @@ export class GitService implements IGitService {
2433
2895
  // missing from disk is exactly what `git add` records as deleted.
2434
2896
  await fs.rm(path.join(cwd, repoRelativePath), { force: true });
2435
2897
  }
2436
- });
2898
+ }
2437
2899
  }
2438
2900
 
2439
2901
  /** Fetch the two branches a CR spans so origin refs reflect the latest push. */
@@ -2504,6 +2966,69 @@ export class GitService implements IGitService {
2504
2966
  : relativePath;
2505
2967
  }
2506
2968
 
2969
+ /**
2970
+ * The COMMITS both of a change request's branches point at in this clone
2971
+ * already, or null when either is unknown to it — the question behind
2972
+ * `changedPathsForPr`'s `fetch: false`, answered with the resolution itself
2973
+ * so the caller diffs precisely what it decided on. Only git's own "no such
2974
+ * ref" answers null; every other failure is the caller's to see, and
2975
+ * treating it as "not here" would spend a network fetch on a clone that is
2976
+ * broken for some other reason.
2977
+ */
2978
+ private async publishedPrCommits(
2979
+ cwd: string,
2980
+ baseBranch: string,
2981
+ headBranch: string,
2982
+ ): Promise<{ base: string; head: string } | null> {
2983
+ const shas: string[] = [];
2984
+ for (const branch of [baseBranch, headBranch]) {
2985
+ try {
2986
+ shas.push((await this.resolvePublishedBranch(cwd, branch)).sha);
2987
+ } catch (err) {
2988
+ if (err instanceof WorkflowValidationError) return null;
2989
+ throw err;
2990
+ }
2991
+ }
2992
+ return { base: shas[0], head: shas[1] };
2993
+ }
2994
+
2995
+ /**
2996
+ * `resolveBranchRef` with the preference reversed: the PUBLISHED ref
2997
+ * (`origin/<branch>`, just fetched by `fetchPrRefs`) before the local head.
2998
+ * A change request is a pair of published branches, and a clone's local
2999
+ * copy of the OTHER branch is whatever it was when the clone last touched
3000
+ * it — in a proposal's own workspace the local target is typically the
3001
+ * target as of the fork. Reading that made every request look up to date
3002
+ * and diffed it against a stale target. A local-only branch still resolves.
3003
+ */
3004
+ private async resolvePublishedBranchRef(cwd: string, branch: string): Promise<string> {
3005
+ return (await this.resolvePublishedBranch(cwd, branch)).ref;
3006
+ }
3007
+
3008
+ /**
3009
+ * {@link resolvePublishedBranchRef} with the commit it resolved to — the
3010
+ * `rev-parse` that verifies the ref already prints it, so pinning a diff to
3011
+ * the commit costs nothing over naming the ref.
3012
+ */
3013
+ private async resolvePublishedBranch(
3014
+ cwd: string,
3015
+ branch: string,
3016
+ ): Promise<{ ref: string; sha: string }> {
3017
+ for (const ref of [`refs/remotes/origin/${branch}`, `refs/heads/${branch}`]) {
3018
+ try {
3019
+ const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', ref]);
3020
+ return { ref, sha: stdout.trim() };
3021
+ } catch (err) {
3022
+ // Only git's own "no such ref" (exit 1 under --quiet) moves on to the
3023
+ // next candidate. A deadline or any other failure is not that answer:
3024
+ // skipping past it would pick the stale local copy, or call a real
3025
+ // branch unknown.
3026
+ if (!(err instanceof GitRunError) || err.timedOut || err.exitCode !== 1) throw err;
3027
+ }
3028
+ }
3029
+ throw new WorkflowValidationError(`unknown branch: ${branch}`);
3030
+ }
3031
+
2507
3032
  /**
2508
3033
  * Resolve a branch name to a concrete ref the local clone knows about.
2509
3034
  * Tries the local head first, falls back to the matching remote-tracking
@@ -2553,14 +3078,14 @@ export class GitService implements IGitService {
2553
3078
  }
2554
3079
  const dirtyList = porcelain.stdout.trim().replace(/\s+/g, ' ');
2555
3080
  if (queueExplainsIt) {
2556
- console.log(
2557
- `[git] workspace=${workspaceId} branch=${branch} working tree is dirty while ` +
3081
+ log.info(
3082
+ `workspace=${workspaceId} branch=${branch} working tree is dirty while ` +
2558
3083
  `pending commits drain (expected — the background worker is catching up). ` +
2559
3084
  `Files: ${dirtyList}`,
2560
3085
  );
2561
3086
  } else {
2562
- console.warn(
2563
- `[git] workspace=${workspaceId} branch=${branch} has a non-clean working ` +
3087
+ log.warn(
3088
+ `workspace=${workspaceId} branch=${branch} has a non-clean working ` +
2564
3089
  `tree under save=share with NO queued pending commits — this should never ` +
2565
3090
  `happen and likely indicates a missed lock-release commit. Files: ${dirtyList}`,
2566
3091
  );
@@ -2710,6 +3235,41 @@ export class GitService implements IGitService {
2710
3235
  }
2711
3236
  }
2712
3237
 
3238
+ /**
3239
+ * The commits on `ref` that touched `repoRelativePath`, newest first, at
3240
+ * most `limit` of them. What a skill's `version` is looked up through: each
3241
+ * commit is a copy of the file that may have declared a version. Renames are
3242
+ * not followed — a moved skill's earlier history is the old path's.
3243
+ */
3244
+ async pathHistory(
3245
+ workspaceId: string,
3246
+ ref: string,
3247
+ repoRelativePath: string,
3248
+ limit: number,
3249
+ ): Promise<string[]> {
3250
+ const cwd = await this.repoDir(workspaceId);
3251
+ const { stdout } = await this.git(cwd, [
3252
+ 'rev-list',
3253
+ `--max-count=${Math.max(1, Math.trunc(limit))}`,
3254
+ ref,
3255
+ '--',
3256
+ repoRelativePath,
3257
+ ]);
3258
+ return stdout.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
3259
+ }
3260
+
3261
+ /**
3262
+ * Every file under `folder` at `ref`, repo-root-relative and in tree order.
3263
+ * A folder that does not exist at that ref lists nothing. NUL-delimited, so
3264
+ * a non-ASCII name comes back as itself rather than C-quoted, and one with
3265
+ * a leading or trailing space keeps it.
3266
+ */
3267
+ async listFilesAtRef(workspaceId: string, ref: string, folder: string): Promise<string[]> {
3268
+ const cwd = await this.repoDir(workspaceId);
3269
+ const { stdout } = await this.git(cwd, ['ls-tree', '-r', '-z', '--name-only', ref, '--', folder]);
3270
+ return stdout.split('\0').filter((line) => line.length > 0);
3271
+ }
3272
+
2713
3273
  /**
2714
3274
  * A clean `git status` for a path has two honest readings (already
2715
3275
  * committed; a queued re-apply of something that landed) and one dishonest
@@ -2756,69 +3316,32 @@ export class GitService implements IGitService {
2756
3316
  return stdout.trim().length > 0;
2757
3317
  }
2758
3318
 
2759
- private async git(
2760
- cwd: string,
2761
- args: string[],
2762
- opts?: {
2763
- /**
2764
- * Bytes to feed the subprocess on stdin — used by the
2765
- * `--pathspec-from-file=-` commit/add paths so a several-hundred-file
2766
- * batch never has to ride the argv (Windows caps a command line at
2767
- * ~32K chars).
2768
- */
2769
- input?: string;
2770
- },
2771
- ): Promise<GitRunResult> {
2772
- try {
2773
- const pending = execFileAsync('git', args, {
2774
- cwd,
2775
- // `GIT_LITERAL_PATHSPECS=1` makes git treat every pathspec literally
2776
- // instead of interpreting `[`, `]`, `*`, `?`, `!`, or `:(magic)` as
2777
- // glob / magic syntax. KB files routinely arrive with bracketed
2778
- // prefixes like `[Approved] foo.docx` or `[Updated 2025] bar.md`; the
2779
- // upload pipeline (`writeFileBinary` + `releaseLock` + `commitFile`)
2780
- // passes the relative path straight through to `git add` / `git
2781
- // checkout -- <path>`, which would otherwise glob and either match the
2782
- // wrong file or no file at all. Setting it once here covers every git
2783
- // subprocess this service spawns.
2784
- //
2785
- // `LC_ALL=C` / `LANG=C` force git's human-readable output (including
2786
- // stderr) to stable, English, locale-independent text. Callers that
2787
- // classify errors by message — e.g. `readFileAtRef` distinguishing a
2788
- // "path does not exist in <ref>" absence from a hard failure — would
2789
- // otherwise misread a translated message on a non-English host and, for
2790
- // the fail-closed roles.yaml preservation path, fail OPEN.
2791
- env: { ...process.env, GIT_LITERAL_PATHSPECS: '1', LC_ALL: 'C', LANG: 'C' },
2792
- maxBuffer: 32 * 1024 * 1024,
2793
- });
2794
- if (opts?.input !== undefined && pending.child.stdin) {
2795
- // A dying git can close stdin mid-write; the promise below still
2796
- // rejects with the exit code, which is the error we want to surface.
2797
- pending.child.stdin.on('error', () => undefined);
2798
- pending.child.stdin.write(opts.input);
2799
- pending.child.stdin.end();
2800
- }
2801
- const { stdout, stderr } = await pending;
2802
- return { stdout: stdout.toString(), stderr: stderr.toString() };
2803
- } catch (err) {
2804
- const msg = err instanceof Error ? err.message : String(err);
2805
- // Skip past any leading `-c key=val` pairs so the error names the actual
2806
- // git subcommand that failed (e.g. "git fetch failed:" not "git -c failed:").
2807
- let i = 0;
2808
- while (i < args.length && args[i] === '-c') i += 2;
2809
- const subcommand = args[i] ?? args[0];
2810
- const wrapped = new Error(`git ${subcommand} failed: ${redact(msg)}`) as Error & {
2811
- exitCode?: number;
2812
- stderr?: string;
2813
- };
2814
- // Preserve the underlying exit code / stderr so callers can distinguish
2815
- // expected non-zero exits (e.g. merge-base exit 1 = no common ancestor)
2816
- // from infra failures (ENOENT, exit 128) without parsing message strings.
2817
- const original = err as { code?: unknown; stderr?: unknown };
2818
- if (typeof original.code === 'number') wrapped.exitCode = original.code;
2819
- if (typeof original.stderr === 'string') wrapped.stderr = redact(original.stderr);
2820
- throw wrapped;
2821
- }
3319
+ /**
3320
+ * Every git invocation this service makes goes through here, and from here
3321
+ * through the injected runner — which is what puts a deadline on it. The
3322
+ * environment, the buffer ceiling, the stdin handling and the error shape all
3323
+ * live in `NodeGitRunner` now; see `shared/git.contract.ts` for why.
3324
+ *
3325
+ * Errors arrive as `GitRunError`, which still carries `exitCode` and
3326
+ * `stderr`, so the callers that read those to tell an expected non-zero exit
3327
+ * from a real failure are unaffected.
3328
+ */
3329
+ private git(cwd: string, args: string[], opts?: Omit<GitRunOptions, 'encoding'>): Promise<GitRunResult> {
3330
+ return this.gitRunner.run(cwd, args, {
3331
+ ...opts,
3332
+ // `GIT_LITERAL_PATHSPECS=1` makes git treat every pathspec literally
3333
+ // instead of interpreting `[`, `]`, `*`, `?`, `!`, or `:(magic)` as glob
3334
+ // / magic syntax. KB files routinely arrive with bracketed prefixes like
3335
+ // `[Approved] foo.docx` or `[Updated 2025] bar.md`; the upload pipeline
3336
+ // (`writeFileBinary` + `releaseLock` + `commitFile`) passes the relative
3337
+ // path straight through to `git add` / `git checkout -- <path>`, which
3338
+ // would otherwise glob and either match the wrong file or no file at all.
3339
+ //
3340
+ // THIS service's setting, not the runner's: the startup runner spells its
3341
+ // literal paths as `:(literal)<path>`, which this variable would turn into
3342
+ // a search for a file literally named that.
3343
+ env: { GIT_LITERAL_PATHSPECS: '1', ...opts?.env },
3344
+ });
2822
3345
  }
2823
3346
  }
2824
3347
 
@@ -2869,6 +3392,23 @@ export function parseNameStatusZ(out: string): NameStatusEntry[] {
2869
3392
  return entries;
2870
3393
  }
2871
3394
 
3395
+ /**
3396
+ * Undo a rename git detected between a real file and the empty-folder
3397
+ * placeholder. Deleting a folder's last file writes the placeholder, and
3398
+ * when that file was empty too, `-M` pairs the two as a 100% rename. The
3399
+ * pair is really a removal (or, the other way round, an addition) plus the
3400
+ * placeholder, which is never reviewed — so the entry becomes the real side
3401
+ * alone, and the caller's placeholder filter has nothing of it to drop.
3402
+ */
3403
+ export function withoutPlaceholderRename(entry: NameStatusEntry): NameStatusEntry {
3404
+ const { previousPath } = entry;
3405
+ if (!previousPath || entry.status !== 'renamed') return entry;
3406
+ const toPlaceholder = isFolderPlaceholder(entry.path);
3407
+ const fromPlaceholder = isFolderPlaceholder(previousPath);
3408
+ if (toPlaceholder === fromPlaceholder) return entry;
3409
+ return toPlaceholder ? { status: 'removed', path: previousPath } : { status: 'added', path: entry.path };
3410
+ }
3411
+
2872
3412
  interface NumstatEntry {
2873
3413
  additions: number;
2874
3414
  deletions: number;