@bevel-software/platform-core-backend 0.1.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 (720) hide show
  1. package/LICENSE +202 -0
  2. package/dist/assets.d.ts +5 -0
  3. package/dist/assets.d.ts.map +1 -0
  4. package/dist/assets.js +23 -0
  5. package/dist/assets.js.map +1 -0
  6. package/dist/core/core-ports.d.ts +83 -0
  7. package/dist/core/core-ports.d.ts.map +1 -0
  8. package/dist/core/core-ports.js +12 -0
  9. package/dist/core/core-ports.js.map +1 -0
  10. package/dist/core/create-core-server.d.ts +62 -0
  11. package/dist/core/create-core-server.d.ts.map +1 -0
  12. package/dist/core/create-core-server.js +204 -0
  13. package/dist/core/create-core-server.js.map +1 -0
  14. package/dist/core/create-core-services.d.ts +95 -0
  15. package/dist/core/create-core-services.d.ts.map +1 -0
  16. package/dist/core/create-core-services.js +352 -0
  17. package/dist/core/create-core-services.js.map +1 -0
  18. package/dist/core-config.d.ts +119 -0
  19. package/dist/core-config.d.ts.map +1 -0
  20. package/dist/core-config.js +240 -0
  21. package/dist/core-config.js.map +1 -0
  22. package/dist/index.d.ts +36 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +36 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/modules/access/access-control.interface.d.ts +348 -0
  27. package/dist/modules/access/access-control.interface.d.ts.map +1 -0
  28. package/dist/modules/access/access-control.interface.js +2 -0
  29. package/dist/modules/access/access-control.interface.js.map +1 -0
  30. package/dist/modules/access/access-control.service.d.ts +323 -0
  31. package/dist/modules/access/access-control.service.d.ts.map +1 -0
  32. package/dist/modules/access/access-control.service.js +1431 -0
  33. package/dist/modules/access/access-control.service.js.map +1 -0
  34. package/dist/modules/access/access-errors.d.ts +34 -0
  35. package/dist/modules/access/access-errors.d.ts.map +1 -0
  36. package/dist/modules/access/access-errors.js +40 -0
  37. package/dist/modules/access/access-errors.js.map +1 -0
  38. package/dist/modules/access/access-mutation.service.d.ts +176 -0
  39. package/dist/modules/access/access-mutation.service.d.ts.map +1 -0
  40. package/dist/modules/access/access-mutation.service.js +307 -0
  41. package/dist/modules/access/access-mutation.service.js.map +1 -0
  42. package/dist/modules/access/access-splice.d.ts +98 -0
  43. package/dist/modules/access/access-splice.d.ts.map +1 -0
  44. package/dist/modules/access/access-splice.js +346 -0
  45. package/dist/modules/access/access-splice.js.map +1 -0
  46. package/dist/modules/access/access.routes.d.ts +10 -0
  47. package/dist/modules/access/access.routes.d.ts.map +1 -0
  48. package/dist/modules/access/access.routes.js +757 -0
  49. package/dist/modules/access/access.routes.js.map +1 -0
  50. package/dist/modules/access/creator-access.d.ts +113 -0
  51. package/dist/modules/access/creator-access.d.ts.map +1 -0
  52. package/dist/modules/access/creator-access.js +212 -0
  53. package/dist/modules/access/creator-access.js.map +1 -0
  54. package/dist/modules/access/kb-read-filter.d.ts +41 -0
  55. package/dist/modules/access/kb-read-filter.d.ts.map +1 -0
  56. package/dist/modules/access/kb-read-filter.js +60 -0
  57. package/dist/modules/access/kb-read-filter.js.map +1 -0
  58. package/dist/modules/access/roles-admin.service.d.ts +240 -0
  59. package/dist/modules/access/roles-admin.service.d.ts.map +1 -0
  60. package/dist/modules/access/roles-admin.service.js +630 -0
  61. package/dist/modules/access/roles-admin.service.js.map +1 -0
  62. package/dist/modules/access/roles-edit.d.ts +91 -0
  63. package/dist/modules/access/roles-edit.d.ts.map +1 -0
  64. package/dist/modules/access/roles-edit.js +232 -0
  65. package/dist/modules/access/roles-edit.js.map +1 -0
  66. package/dist/modules/access/roles-yaml-guard.d.ts +56 -0
  67. package/dist/modules/access/roles-yaml-guard.d.ts.map +1 -0
  68. package/dist/modules/access/roles-yaml-guard.js +79 -0
  69. package/dist/modules/access/roles-yaml-guard.js.map +1 -0
  70. package/dist/modules/admin/admin-access.routes.d.ts +18 -0
  71. package/dist/modules/admin/admin-access.routes.d.ts.map +1 -0
  72. package/dist/modules/admin/admin-access.routes.js +26 -0
  73. package/dist/modules/admin/admin-access.routes.js.map +1 -0
  74. package/dist/modules/admin/admin-access.service.d.ts +24 -0
  75. package/dist/modules/admin/admin-access.service.d.ts.map +1 -0
  76. package/dist/modules/admin/admin-access.service.js +41 -0
  77. package/dist/modules/admin/admin-access.service.js.map +1 -0
  78. package/dist/modules/admin/admin.interface.d.ts +11 -0
  79. package/dist/modules/admin/admin.interface.d.ts.map +1 -0
  80. package/dist/modules/admin/admin.interface.js +2 -0
  81. package/dist/modules/admin/admin.interface.js.map +1 -0
  82. package/dist/modules/auth/account-erasure.service.d.ts +80 -0
  83. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -0
  84. package/dist/modules/auth/account-erasure.service.js +91 -0
  85. package/dist/modules/auth/account-erasure.service.js.map +1 -0
  86. package/dist/modules/auth/auth.middleware.d.ts +23 -0
  87. package/dist/modules/auth/auth.middleware.d.ts.map +1 -0
  88. package/dist/modules/auth/auth.middleware.js +68 -0
  89. package/dist/modules/auth/auth.middleware.js.map +1 -0
  90. package/dist/modules/auth/auth.routes.d.ts +25 -0
  91. package/dist/modules/auth/auth.routes.d.ts.map +1 -0
  92. package/dist/modules/auth/auth.routes.js +72 -0
  93. package/dist/modules/auth/auth.routes.js.map +1 -0
  94. package/dist/modules/auth/auth.service.d.ts +76 -0
  95. package/dist/modules/auth/auth.service.d.ts.map +1 -0
  96. package/dist/modules/auth/auth.service.js +168 -0
  97. package/dist/modules/auth/auth.service.js.map +1 -0
  98. package/dist/modules/code-mode/code-mode-names.d.ts +16 -0
  99. package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -0
  100. package/dist/modules/code-mode/code-mode-names.js +30 -0
  101. package/dist/modules/code-mode/code-mode-names.js.map +1 -0
  102. package/dist/modules/code-mode/code-mode.tool.d.ts +13 -0
  103. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -0
  104. package/dist/modules/code-mode/code-mode.tool.js +112 -0
  105. package/dist/modules/code-mode/code-mode.tool.js.map +1 -0
  106. package/dist/modules/code-mode/index.d.ts +3 -0
  107. package/dist/modules/code-mode/index.d.ts.map +1 -0
  108. package/dist/modules/code-mode/index.js +3 -0
  109. package/dist/modules/code-mode/index.js.map +1 -0
  110. package/dist/modules/database/connection.d.ts +6 -0
  111. package/dist/modules/database/connection.d.ts.map +1 -0
  112. package/dist/modules/database/connection.js +12 -0
  113. package/dist/modules/database/connection.js.map +1 -0
  114. package/dist/modules/database/core-schema.d.ts +2273 -0
  115. package/dist/modules/database/core-schema.d.ts.map +1 -0
  116. package/dist/modules/database/core-schema.js +413 -0
  117. package/dist/modules/database/core-schema.js.map +1 -0
  118. package/dist/modules/database/migrate.d.ts +10 -0
  119. package/dist/modules/database/migrate.d.ts.map +1 -0
  120. package/dist/modules/database/migrate.js +40 -0
  121. package/dist/modules/database/migrate.js.map +1 -0
  122. package/dist/modules/database/schema.d.ts +12 -0
  123. package/dist/modules/database/schema.d.ts.map +1 -0
  124. package/dist/modules/database/schema.js +12 -0
  125. package/dist/modules/database/schema.js.map +1 -0
  126. package/dist/modules/diff/diff-paths.d.ts +2 -0
  127. package/dist/modules/diff/diff-paths.d.ts.map +1 -0
  128. package/dist/modules/diff/diff-paths.js +9 -0
  129. package/dist/modules/diff/diff-paths.js.map +1 -0
  130. package/dist/modules/diff/diff.config.d.ts +8 -0
  131. package/dist/modules/diff/diff.config.d.ts.map +1 -0
  132. package/dist/modules/diff/diff.config.js +13 -0
  133. package/dist/modules/diff/diff.config.js.map +1 -0
  134. package/dist/modules/diff/diff.interface.d.ts +73 -0
  135. package/dist/modules/diff/diff.interface.d.ts.map +1 -0
  136. package/dist/modules/diff/diff.interface.js +2 -0
  137. package/dist/modules/diff/diff.interface.js.map +1 -0
  138. package/dist/modules/diff/diff.routes.d.ts +35 -0
  139. package/dist/modules/diff/diff.routes.d.ts.map +1 -0
  140. package/dist/modules/diff/diff.routes.js +228 -0
  141. package/dist/modules/diff/diff.routes.js.map +1 -0
  142. package/dist/modules/diff/diff.service.d.ts +51 -0
  143. package/dist/modules/diff/diff.service.d.ts.map +1 -0
  144. package/dist/modules/diff/diff.service.js +489 -0
  145. package/dist/modules/diff/diff.service.js.map +1 -0
  146. package/dist/modules/diff/line-diff.d.ts +16 -0
  147. package/dist/modules/diff/line-diff.d.ts.map +1 -0
  148. package/dist/modules/diff/line-diff.js +67 -0
  149. package/dist/modules/diff/line-diff.js.map +1 -0
  150. package/dist/modules/mcp/mcp-auth.middleware.d.ts +27 -0
  151. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -0
  152. package/dist/modules/mcp/mcp-auth.middleware.js +116 -0
  153. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -0
  154. package/dist/modules/mcp/mcp-session-store.d.ts +74 -0
  155. package/dist/modules/mcp/mcp-session-store.d.ts.map +1 -0
  156. package/dist/modules/mcp/mcp-session-store.js +131 -0
  157. package/dist/modules/mcp/mcp-session-store.js.map +1 -0
  158. package/dist/modules/mcp/mcp.routes.d.ts +24 -0
  159. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -0
  160. package/dist/modules/mcp/mcp.routes.js +229 -0
  161. package/dist/modules/mcp/mcp.routes.js.map +1 -0
  162. package/dist/modules/mcp/mcp.service.d.ts +198 -0
  163. package/dist/modules/mcp/mcp.service.d.ts.map +1 -0
  164. package/dist/modules/mcp/mcp.service.js +840 -0
  165. package/dist/modules/mcp/mcp.service.js.map +1 -0
  166. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +76 -0
  167. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -0
  168. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +291 -0
  169. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -0
  170. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +18 -0
  171. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -0
  172. package/dist/modules/mcp/oauth/oauth-consent.routes.js +52 -0
  173. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -0
  174. package/dist/modules/mcp/oauth/oauth-state.d.ts +32 -0
  175. package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -0
  176. package/dist/modules/mcp/oauth/oauth-state.js +34 -0
  177. package/dist/modules/mcp/oauth/oauth-state.js.map +1 -0
  178. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +92 -0
  179. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -0
  180. package/dist/modules/secrets-vault/db-secrets-vault.service.js +728 -0
  181. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -0
  182. package/dist/modules/secrets-vault/index.d.ts +7 -0
  183. package/dist/modules/secrets-vault/index.d.ts.map +1 -0
  184. package/dist/modules/secrets-vault/index.js +6 -0
  185. package/dist/modules/secrets-vault/index.js.map +1 -0
  186. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts +63 -0
  187. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -0
  188. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +269 -0
  189. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -0
  190. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +46 -0
  191. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -0
  192. package/dist/modules/secrets-vault/secrets-variable-loader.js +87 -0
  193. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -0
  194. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +210 -0
  195. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -0
  196. package/dist/modules/secrets-vault/secrets-vault.contract.js +90 -0
  197. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -0
  198. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +30 -0
  199. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -0
  200. package/dist/modules/secrets-vault/secrets-vault.routes.js +490 -0
  201. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -0
  202. package/dist/modules/skills/index.d.ts +5 -0
  203. package/dist/modules/skills/index.d.ts.map +1 -0
  204. package/dist/modules/skills/index.js +4 -0
  205. package/dist/modules/skills/index.js.map +1 -0
  206. package/dist/modules/skills/skills.contract.d.ts +58 -0
  207. package/dist/modules/skills/skills.contract.d.ts.map +1 -0
  208. package/dist/modules/skills/skills.contract.js +10 -0
  209. package/dist/modules/skills/skills.contract.js.map +1 -0
  210. package/dist/modules/skills/skills.routes.d.ts +15 -0
  211. package/dist/modules/skills/skills.routes.d.ts.map +1 -0
  212. package/dist/modules/skills/skills.routes.js +34 -0
  213. package/dist/modules/skills/skills.routes.js.map +1 -0
  214. package/dist/modules/skills/skills.service.d.ts +21 -0
  215. package/dist/modules/skills/skills.service.d.ts.map +1 -0
  216. package/dist/modules/skills/skills.service.js +202 -0
  217. package/dist/modules/skills/skills.service.js.map +1 -0
  218. package/dist/modules/skills/skills.tools.d.ts +15 -0
  219. package/dist/modules/skills/skills.tools.d.ts.map +1 -0
  220. package/dist/modules/skills/skills.tools.js +96 -0
  221. package/dist/modules/skills/skills.tools.js.map +1 -0
  222. package/dist/modules/tool-auth/external-api-key.errors.d.ts +14 -0
  223. package/dist/modules/tool-auth/external-api-key.errors.d.ts.map +1 -0
  224. package/dist/modules/tool-auth/external-api-key.errors.js +23 -0
  225. package/dist/modules/tool-auth/external-api-key.errors.js.map +1 -0
  226. package/dist/modules/tool-auth/external-api-key.interface.d.ts +90 -0
  227. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -0
  228. package/dist/modules/tool-auth/external-api-key.interface.js +2 -0
  229. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -0
  230. package/dist/modules/tool-auth/external-api-key.service.d.ts +25 -0
  231. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -0
  232. package/dist/modules/tool-auth/external-api-key.service.js +164 -0
  233. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -0
  234. package/dist/modules/tool-auth/internal-token.service.d.ts +62 -0
  235. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -0
  236. package/dist/modules/tool-auth/internal-token.service.js +85 -0
  237. package/dist/modules/tool-auth/internal-token.service.js.map +1 -0
  238. package/dist/modules/tool-auth/llm-usage-meter.d.ts +16 -0
  239. package/dist/modules/tool-auth/llm-usage-meter.d.ts.map +1 -0
  240. package/dist/modules/tool-auth/llm-usage-meter.js +6 -0
  241. package/dist/modules/tool-auth/llm-usage-meter.js.map +1 -0
  242. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +101 -0
  243. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -0
  244. package/dist/modules/tool-auth/tool-auth.middleware.js +166 -0
  245. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -0
  246. package/dist/modules/tool-helpers/index.d.ts +19 -0
  247. package/dist/modules/tool-helpers/index.d.ts.map +1 -0
  248. package/dist/modules/tool-helpers/index.js +18 -0
  249. package/dist/modules/tool-helpers/index.js.map +1 -0
  250. package/dist/modules/tool-helpers/tool-context.d.ts +35 -0
  251. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -0
  252. package/dist/modules/tool-helpers/tool-context.js +66 -0
  253. package/dist/modules/tool-helpers/tool-context.js.map +1 -0
  254. package/dist/modules/tool-helpers/tool-def.d.ts +33 -0
  255. package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -0
  256. package/dist/modules/tool-helpers/tool-def.js +57 -0
  257. package/dist/modules/tool-helpers/tool-def.js.map +1 -0
  258. package/dist/modules/tool-helpers/tool-handler.d.ts +19 -0
  259. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -0
  260. package/dist/modules/tool-helpers/tool-handler.js +84 -0
  261. package/dist/modules/tool-helpers/tool-handler.js.map +1 -0
  262. package/dist/modules/tool-helpers/tool.contract.d.ts +64 -0
  263. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -0
  264. package/dist/modules/tool-helpers/tool.contract.js +24 -0
  265. package/dist/modules/tool-helpers/tool.contract.js.map +1 -0
  266. package/dist/modules/tool-helpers/validate-token.d.ts +31 -0
  267. package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -0
  268. package/dist/modules/tool-helpers/validate-token.js +20 -0
  269. package/dist/modules/tool-helpers/validate-token.js.map +1 -0
  270. package/dist/modules/tool-manuals/index.d.ts +5 -0
  271. package/dist/modules/tool-manuals/index.d.ts.map +1 -0
  272. package/dist/modules/tool-manuals/index.js +5 -0
  273. package/dist/modules/tool-manuals/index.js.map +1 -0
  274. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +182 -0
  275. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -0
  276. package/dist/modules/tool-manuals/tool-manuals.contract.js +19 -0
  277. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -0
  278. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +24 -0
  279. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -0
  280. package/dist/modules/tool-manuals/tool-manuals.routes.js +102 -0
  281. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -0
  282. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +108 -0
  283. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -0
  284. package/dist/modules/tool-manuals/tool-manuals.service.js +710 -0
  285. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -0
  286. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +35 -0
  287. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -0
  288. package/dist/modules/tool-manuals/tool-manuals.tools.js +157 -0
  289. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -0
  290. package/dist/modules/tool-registry/manual.routes.d.ts +6 -0
  291. package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -0
  292. package/dist/modules/tool-registry/manual.routes.js +76 -0
  293. package/dist/modules/tool-registry/manual.routes.js.map +1 -0
  294. package/dist/modules/tool-registry/tool-registry.d.ts +23 -0
  295. package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -0
  296. package/dist/modules/tool-registry/tool-registry.js +46 -0
  297. package/dist/modules/tool-registry/tool-registry.js.map +1 -0
  298. package/dist/modules/tool-registry/tool.contract.d.ts +48 -0
  299. package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -0
  300. package/dist/modules/tool-registry/tool.contract.js +2 -0
  301. package/dist/modules/tool-registry/tool.contract.js.map +1 -0
  302. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +14 -0
  303. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -0
  304. package/dist/modules/workflow/agent-tools/workflow.tools.js +437 -0
  305. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -0
  306. package/dist/modules/workflow/event-bus.d.ts +107 -0
  307. package/dist/modules/workflow/event-bus.d.ts.map +1 -0
  308. package/dist/modules/workflow/event-bus.js +224 -0
  309. package/dist/modules/workflow/event-bus.js.map +1 -0
  310. package/dist/modules/workflow/events.routes.d.ts +80 -0
  311. package/dist/modules/workflow/events.routes.d.ts.map +1 -0
  312. package/dist/modules/workflow/events.routes.js +216 -0
  313. package/dist/modules/workflow/events.routes.js.map +1 -0
  314. package/dist/modules/workflow/file-change-notifier.d.ts +38 -0
  315. package/dist/modules/workflow/file-change-notifier.d.ts.map +1 -0
  316. package/dist/modules/workflow/file-change-notifier.js +22 -0
  317. package/dist/modules/workflow/file-change-notifier.js.map +1 -0
  318. package/dist/modules/workflow/file-lock.service.d.ts +57 -0
  319. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -0
  320. package/dist/modules/workflow/file-lock.service.js +202 -0
  321. package/dist/modules/workflow/file-lock.service.js.map +1 -0
  322. package/dist/modules/workflow/git/branch-name.d.ts +10 -0
  323. package/dist/modules/workflow/git/branch-name.d.ts.map +1 -0
  324. package/dist/modules/workflow/git/branch-name.js +76 -0
  325. package/dist/modules/workflow/git/branch-name.js.map +1 -0
  326. package/dist/modules/workflow/git/clone-config.d.ts +61 -0
  327. package/dist/modules/workflow/git/clone-config.d.ts.map +1 -0
  328. package/dist/modules/workflow/git/clone-config.js +69 -0
  329. package/dist/modules/workflow/git/clone-config.js.map +1 -0
  330. package/dist/modules/workflow/git/git-version.d.ts +34 -0
  331. package/dist/modules/workflow/git/git-version.d.ts.map +1 -0
  332. package/dist/modules/workflow/git/git-version.js +63 -0
  333. package/dist/modules/workflow/git/git-version.js.map +1 -0
  334. package/dist/modules/workflow/git/git.service.d.ts +427 -0
  335. package/dist/modules/workflow/git/git.service.d.ts.map +1 -0
  336. package/dist/modules/workflow/git/git.service.js +1987 -0
  337. package/dist/modules/workflow/git/git.service.js.map +1 -0
  338. package/dist/modules/workflow/git/mutex.d.ts +11 -0
  339. package/dist/modules/workflow/git/mutex.d.ts.map +1 -0
  340. package/dist/modules/workflow/git/mutex.js +23 -0
  341. package/dist/modules/workflow/git/mutex.js.map +1 -0
  342. package/dist/modules/workflow/git/pull-request.service.d.ts +124 -0
  343. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -0
  344. package/dist/modules/workflow/git/pull-request.service.js +328 -0
  345. package/dist/modules/workflow/git/pull-request.service.js.map +1 -0
  346. package/dist/modules/workflow/locking-filesystem.d.ts +133 -0
  347. package/dist/modules/workflow/locking-filesystem.d.ts.map +1 -0
  348. package/dist/modules/workflow/locking-filesystem.js +400 -0
  349. package/dist/modules/workflow/locking-filesystem.js.map +1 -0
  350. package/dist/modules/workflow/pending-commits.service.d.ts +221 -0
  351. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -0
  352. package/dist/modules/workflow/pending-commits.service.js +364 -0
  353. package/dist/modules/workflow/pending-commits.service.js.map +1 -0
  354. package/dist/modules/workflow/pending-commits.worker.d.ts +148 -0
  355. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -0
  356. package/dist/modules/workflow/pending-commits.worker.js +245 -0
  357. package/dist/modules/workflow/pending-commits.worker.js.map +1 -0
  358. package/dist/modules/workflow/read-only-filesystem.d.ts +24 -0
  359. package/dist/modules/workflow/read-only-filesystem.d.ts.map +1 -0
  360. package/dist/modules/workflow/read-only-filesystem.js +39 -0
  361. package/dist/modules/workflow/read-only-filesystem.js.map +1 -0
  362. package/dist/modules/workflow/recovery-bot.d.ts +31 -0
  363. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -0
  364. package/dist/modules/workflow/recovery-bot.js +64 -0
  365. package/dist/modules/workflow/recovery-bot.js.map +1 -0
  366. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +137 -0
  367. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -0
  368. package/dist/modules/workflow/review-workflow/review-workflow.interface.js +2 -0
  369. package/dist/modules/workflow/review-workflow/review-workflow.interface.js.map +1 -0
  370. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +26 -0
  371. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -0
  372. package/dist/modules/workflow/review-workflow/review-workflow.service.js +636 -0
  373. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -0
  374. package/dist/modules/workflow/sanitize-error.d.ts +29 -0
  375. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -0
  376. package/dist/modules/workflow/sanitize-error.js +60 -0
  377. package/dist/modules/workflow/sanitize-error.js.map +1 -0
  378. package/dist/modules/workflow/session-ontology.policy.d.ts +52 -0
  379. package/dist/modules/workflow/session-ontology.policy.d.ts.map +1 -0
  380. package/dist/modules/workflow/session-ontology.policy.js +62 -0
  381. package/dist/modules/workflow/session-ontology.policy.js.map +1 -0
  382. package/dist/modules/workflow/session-ontology.service.d.ts +100 -0
  383. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -0
  384. package/dist/modules/workflow/session-ontology.service.js +143 -0
  385. package/dist/modules/workflow/session-ontology.service.js.map +1 -0
  386. package/dist/modules/workflow/workflow-hooks.d.ts +87 -0
  387. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -0
  388. package/dist/modules/workflow/workflow-hooks.js +35 -0
  389. package/dist/modules/workflow/workflow-hooks.js.map +1 -0
  390. package/dist/modules/workflow/workflow.errors.d.ts +171 -0
  391. package/dist/modules/workflow/workflow.errors.d.ts.map +1 -0
  392. package/dist/modules/workflow/workflow.errors.js +260 -0
  393. package/dist/modules/workflow/workflow.errors.js.map +1 -0
  394. package/dist/modules/workflow/workflow.routes.d.ts +26 -0
  395. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -0
  396. package/dist/modules/workflow/workflow.routes.js +804 -0
  397. package/dist/modules/workflow/workflow.routes.js.map +1 -0
  398. package/dist/modules/workflow/workflow.service.d.ts +385 -0
  399. package/dist/modules/workflow/workflow.service.d.ts.map +1 -0
  400. package/dist/modules/workflow/workflow.service.js +1155 -0
  401. package/dist/modules/workflow/workflow.service.js.map +1 -0
  402. package/dist/modules/workspace/bevel-ignore.d.ts +25 -0
  403. package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -0
  404. package/dist/modules/workspace/bevel-ignore.js +57 -0
  405. package/dist/modules/workspace/bevel-ignore.js.map +1 -0
  406. package/dist/modules/workspace/kb-seed.interface.d.ts +36 -0
  407. package/dist/modules/workspace/kb-seed.interface.d.ts.map +1 -0
  408. package/dist/modules/workspace/kb-seed.interface.js +2 -0
  409. package/dist/modules/workspace/kb-seed.interface.js.map +1 -0
  410. package/dist/modules/workspace/kb-seed.service.d.ts +70 -0
  411. package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -0
  412. package/dist/modules/workspace/kb-seed.service.js +325 -0
  413. package/dist/modules/workspace/kb-seed.service.js.map +1 -0
  414. package/dist/modules/workspace/routine-write-policy.d.ts +49 -0
  415. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -0
  416. package/dist/modules/workspace/routine-write-policy.js +46 -0
  417. package/dist/modules/workspace/routine-write-policy.js.map +1 -0
  418. package/dist/modules/workspace/session-ontology.gate.d.ts +112 -0
  419. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -0
  420. package/dist/modules/workspace/session-ontology.gate.js +161 -0
  421. package/dist/modules/workspace/session-ontology.gate.js.map +1 -0
  422. package/dist/modules/workspace/session-sink.d.ts +25 -0
  423. package/dist/modules/workspace/session-sink.d.ts.map +1 -0
  424. package/dist/modules/workspace/session-sink.js +8 -0
  425. package/dist/modules/workspace/session-sink.js.map +1 -0
  426. package/dist/modules/workspace/spill-store.d.ts +24 -0
  427. package/dist/modules/workspace/spill-store.d.ts.map +1 -0
  428. package/dist/modules/workspace/spill-store.js +87 -0
  429. package/dist/modules/workspace/spill-store.js.map +1 -0
  430. package/dist/modules/workspace/workspace.routes.d.ts +25 -0
  431. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -0
  432. package/dist/modules/workspace/workspace.routes.js +1046 -0
  433. package/dist/modules/workspace/workspace.routes.js.map +1 -0
  434. package/dist/modules/workspace/workspace.service.d.ts +339 -0
  435. package/dist/modules/workspace/workspace.service.d.ts.map +1 -0
  436. package/dist/modules/workspace/workspace.service.js +1221 -0
  437. package/dist/modules/workspace/workspace.service.js.map +1 -0
  438. package/dist/modules/workspace/workspace.tools.d.ts +19 -0
  439. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -0
  440. package/dist/modules/workspace/workspace.tools.js +767 -0
  441. package/dist/modules/workspace/workspace.tools.js.map +1 -0
  442. package/dist/shared/frontmatter-id.d.ts +35 -0
  443. package/dist/shared/frontmatter-id.d.ts.map +1 -0
  444. package/dist/shared/frontmatter-id.js +68 -0
  445. package/dist/shared/frontmatter-id.js.map +1 -0
  446. package/dist/shared/fs-walk.d.ts +9 -0
  447. package/dist/shared/fs-walk.d.ts.map +1 -0
  448. package/dist/shared/fs-walk.js +34 -0
  449. package/dist/shared/fs-walk.js.map +1 -0
  450. package/dist/shared/hash-email.d.ts +11 -0
  451. package/dist/shared/hash-email.d.ts.map +1 -0
  452. package/dist/shared/hash-email.js +14 -0
  453. package/dist/shared/hash-email.js.map +1 -0
  454. package/dist/shared/kb-layout.d.ts +38 -0
  455. package/dist/shared/kb-layout.d.ts.map +1 -0
  456. package/dist/shared/kb-layout.js +102 -0
  457. package/dist/shared/kb-layout.js.map +1 -0
  458. package/dist/shared/kb-layout.test.d.ts +2 -0
  459. package/dist/shared/kb-layout.test.d.ts.map +1 -0
  460. package/dist/shared/kb-layout.test.js +67 -0
  461. package/dist/shared/kb-layout.test.js.map +1 -0
  462. package/dist/shared/ssrf.d.ts +20 -0
  463. package/dist/shared/ssrf.d.ts.map +1 -0
  464. package/dist/shared/ssrf.js +89 -0
  465. package/dist/shared/ssrf.js.map +1 -0
  466. package/dist/shared/token-crypto.d.ts +16 -0
  467. package/dist/shared/token-crypto.d.ts.map +1 -0
  468. package/dist/shared/token-crypto.js +49 -0
  469. package/dist/shared/token-crypto.js.map +1 -0
  470. package/dist/shared/ttl-cache.d.ts +16 -0
  471. package/dist/shared/ttl-cache.d.ts.map +1 -0
  472. package/dist/shared/ttl-cache.js +27 -0
  473. package/dist/shared/ttl-cache.js.map +1 -0
  474. package/dist/shared/utcp-namespace.d.ts +46 -0
  475. package/dist/shared/utcp-namespace.d.ts.map +1 -0
  476. package/dist/shared/utcp-namespace.js +82 -0
  477. package/dist/shared/utcp-namespace.js.map +1 -0
  478. package/dist/version.d.ts +17 -0
  479. package/dist/version.d.ts.map +1 -0
  480. package/dist/version.js +19 -0
  481. package/dist/version.js.map +1 -0
  482. package/kb-template/.bevelignore +21 -0
  483. package/kb-template/Agents/.gitkeep +0 -0
  484. package/kb-template/Agents/README.md +38 -0
  485. package/kb-template/CLAUDE.md +419 -0
  486. package/kb-template/Data/.gitkeep +0 -0
  487. package/kb-template/Data/README.md +39 -0
  488. package/kb-template/KnowledgeBase/.gitkeep +0 -0
  489. package/kb-template/KnowledgeBase/README.md +37 -0
  490. package/kb-template/Pipelines/.gitkeep +0 -0
  491. package/kb-template/Pipelines/README.md +38 -0
  492. package/kb-template/Skills/.gitkeep +0 -0
  493. package/kb-template/Skills/README.md +32 -0
  494. package/kb-template/Tools/.gitkeep +0 -0
  495. package/kb-template/Tools/README.md +42 -0
  496. package/kb-template/access.md +36 -0
  497. package/migrations/0000_core_init.sql +243 -0
  498. package/migrations/meta/0000_snapshot.json +1480 -0
  499. package/migrations/meta/_journal.json +13 -0
  500. package/package.json +80 -0
  501. package/src/assets.ts +25 -0
  502. package/src/core/core-ports.ts +91 -0
  503. package/src/core/create-core-server.ts +338 -0
  504. package/src/core/create-core-services.ts +501 -0
  505. package/src/core-config.ts +257 -0
  506. package/src/index.ts +69 -0
  507. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -0
  508. package/src/modules/access/__tests__/access-control.read-batch.test.ts +132 -0
  509. package/src/modules/access/__tests__/access-control.service.test.ts +1011 -0
  510. package/src/modules/access/__tests__/access-mutation.service.test.ts +439 -0
  511. package/src/modules/access/__tests__/access-splice.test.ts +247 -0
  512. package/src/modules/access/__tests__/access.routes.revoke.test.ts +407 -0
  513. package/src/modules/access/__tests__/creator-access.test.ts +217 -0
  514. package/src/modules/access/__tests__/grant-sources.test.ts +204 -0
  515. package/src/modules/access/__tests__/kb-read-filter.test.ts +70 -0
  516. package/src/modules/access/__tests__/roles-admin.service.test.ts +469 -0
  517. package/src/modules/access/__tests__/roles-edit.test.ts +138 -0
  518. package/src/modules/access/__tests__/roles-yaml-guard.test.ts +70 -0
  519. package/src/modules/access/__tests__/roles.routes.test.ts +234 -0
  520. package/src/modules/access/access-control.interface.ts +383 -0
  521. package/src/modules/access/access-control.service.ts +1794 -0
  522. package/src/modules/access/access-errors.ts +60 -0
  523. package/src/modules/access/access-mutation.service.ts +369 -0
  524. package/src/modules/access/access-splice.ts +413 -0
  525. package/src/modules/access/access.routes.ts +894 -0
  526. package/src/modules/access/creator-access.ts +270 -0
  527. package/src/modules/access/kb-read-filter.ts +76 -0
  528. package/src/modules/access/roles-admin.service.ts +741 -0
  529. package/src/modules/access/roles-edit.ts +248 -0
  530. package/src/modules/access/roles-yaml-guard.ts +89 -0
  531. package/src/modules/admin/admin-access.routes.ts +29 -0
  532. package/src/modules/admin/admin-access.service.ts +38 -0
  533. package/src/modules/admin/admin.interface.ts +10 -0
  534. package/src/modules/auth/account-erasure.service.ts +183 -0
  535. package/src/modules/auth/auth.middleware.ts +78 -0
  536. package/src/modules/auth/auth.routes.ts +97 -0
  537. package/src/modules/auth/auth.service.ts +199 -0
  538. package/src/modules/code-mode/code-mode-names.ts +38 -0
  539. package/src/modules/code-mode/code-mode.tool.ts +117 -0
  540. package/src/modules/code-mode/index.ts +6 -0
  541. package/src/modules/database/connection.ts +15 -0
  542. package/src/modules/database/core-schema.ts +431 -0
  543. package/src/modules/database/migrate.ts +43 -0
  544. package/src/modules/database/schema.ts +11 -0
  545. package/src/modules/diff/__tests__/diff.routes.read-gate.test.ts +87 -0
  546. package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -0
  547. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +184 -0
  548. package/src/modules/diff/__tests__/diff.service.test.ts +202 -0
  549. package/src/modules/diff/diff-paths.ts +9 -0
  550. package/src/modules/diff/diff.config.ts +12 -0
  551. package/src/modules/diff/diff.interface.ts +78 -0
  552. package/src/modules/diff/diff.routes.ts +277 -0
  553. package/src/modules/diff/diff.service.ts +508 -0
  554. package/src/modules/diff/line-diff.ts +79 -0
  555. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +287 -0
  556. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +249 -0
  557. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +253 -0
  558. package/src/modules/mcp/__tests__/mcp-session-store.test.ts +151 -0
  559. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +150 -0
  560. package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +92 -0
  561. package/src/modules/mcp/__tests__/mcp.service.test.ts +414 -0
  562. package/src/modules/mcp/mcp-auth.middleware.ts +126 -0
  563. package/src/modules/mcp/mcp-session-store.ts +162 -0
  564. package/src/modules/mcp/mcp.routes.ts +263 -0
  565. package/src/modules/mcp/mcp.service.ts +973 -0
  566. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +401 -0
  567. package/src/modules/mcp/oauth/oauth-consent.routes.ts +60 -0
  568. package/src/modules/mcp/oauth/oauth-state.ts +61 -0
  569. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +277 -0
  570. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +206 -0
  571. package/src/modules/secrets-vault/__tests__/mcp-oauth-discovery.service.test.ts +178 -0
  572. package/src/modules/secrets-vault/__tests__/scopes-covered.test.ts +101 -0
  573. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +54 -0
  574. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -0
  575. package/src/modules/secrets-vault/db-secrets-vault.service.ts +792 -0
  576. package/src/modules/secrets-vault/index.ts +25 -0
  577. package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +289 -0
  578. package/src/modules/secrets-vault/secrets-variable-loader.ts +104 -0
  579. package/src/modules/secrets-vault/secrets-vault.contract.ts +270 -0
  580. package/src/modules/secrets-vault/secrets-vault.routes.ts +526 -0
  581. package/src/modules/skills/__tests__/skills.service.test.ts +176 -0
  582. package/src/modules/skills/index.ts +10 -0
  583. package/src/modules/skills/skills.contract.ts +54 -0
  584. package/src/modules/skills/skills.routes.ts +38 -0
  585. package/src/modules/skills/skills.service.ts +245 -0
  586. package/src/modules/skills/skills.tools.ts +121 -0
  587. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +358 -0
  588. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +79 -0
  589. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +88 -0
  590. package/src/modules/tool-auth/external-api-key.errors.ts +25 -0
  591. package/src/modules/tool-auth/external-api-key.interface.ts +99 -0
  592. package/src/modules/tool-auth/external-api-key.service.ts +209 -0
  593. package/src/modules/tool-auth/internal-token.service.ts +124 -0
  594. package/src/modules/tool-auth/llm-usage-meter.ts +19 -0
  595. package/src/modules/tool-auth/tool-auth.middleware.ts +220 -0
  596. package/src/modules/tool-helpers/__tests__/phase0-loopback-spike.test.ts +129 -0
  597. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +95 -0
  598. package/src/modules/tool-helpers/__tests__/tools-surface.test.ts +146 -0
  599. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +75 -0
  600. package/src/modules/tool-helpers/index.ts +18 -0
  601. package/src/modules/tool-helpers/tool-context.ts +98 -0
  602. package/src/modules/tool-helpers/tool-def.ts +73 -0
  603. package/src/modules/tool-helpers/tool-handler.ts +91 -0
  604. package/src/modules/tool-helpers/tool.contract.ts +85 -0
  605. package/src/modules/tool-helpers/validate-token.ts +50 -0
  606. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +200 -0
  607. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +499 -0
  608. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -0
  609. package/src/modules/tool-manuals/index.ts +12 -0
  610. package/src/modules/tool-manuals/tool-manuals.contract.ts +183 -0
  611. package/src/modules/tool-manuals/tool-manuals.routes.ts +110 -0
  612. package/src/modules/tool-manuals/tool-manuals.service.ts +781 -0
  613. package/src/modules/tool-manuals/tool-manuals.tools.ts +202 -0
  614. package/src/modules/tool-registry/__tests__/tool-registry.test.ts +64 -0
  615. package/src/modules/tool-registry/manual.routes.ts +87 -0
  616. package/src/modules/tool-registry/tool-registry.ts +55 -0
  617. package/src/modules/tool-registry/tool.contract.ts +52 -0
  618. package/src/modules/workflow/__tests__/event-bus.test.ts +322 -0
  619. package/src/modules/workflow/__tests__/file-change-notifier.test.ts +51 -0
  620. package/src/modules/workflow/__tests__/format-affected-owners.test.ts +67 -0
  621. package/src/modules/workflow/__tests__/locking-filesystem.test.ts +445 -0
  622. package/src/modules/workflow/__tests__/pending-commits.canonical.test.ts +36 -0
  623. package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +243 -0
  624. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +308 -0
  625. package/src/modules/workflow/__tests__/sanitize-error.test.ts +67 -0
  626. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +62 -0
  627. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +200 -0
  628. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +232 -0
  629. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +380 -0
  630. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +340 -0
  631. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +192 -0
  632. package/src/modules/workflow/agent-tools/workflow.tools.ts +502 -0
  633. package/src/modules/workflow/event-bus.ts +262 -0
  634. package/src/modules/workflow/events.routes.ts +266 -0
  635. package/src/modules/workflow/file-change-notifier.ts +54 -0
  636. package/src/modules/workflow/file-lock.service.ts +269 -0
  637. package/src/modules/workflow/git/__tests__/branch-name.test.ts +86 -0
  638. package/src/modules/workflow/git/__tests__/branchAuthor.test.ts +80 -0
  639. package/src/modules/workflow/git/__tests__/clone-config.test.ts +15 -0
  640. package/src/modules/workflow/git/__tests__/git-test-helpers.ts +47 -0
  641. package/src/modules/workflow/git/__tests__/git-version.failure.test.ts +24 -0
  642. package/src/modules/workflow/git/__tests__/git-version.test.ts +49 -0
  643. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +389 -0
  644. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
  645. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +105 -0
  646. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +211 -0
  647. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +154 -0
  648. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +320 -0
  649. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +202 -0
  650. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +453 -0
  651. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +198 -0
  652. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +125 -0
  653. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +219 -0
  654. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +145 -0
  655. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +240 -0
  656. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +272 -0
  657. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +232 -0
  658. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +101 -0
  659. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +127 -0
  660. package/src/modules/workflow/git/__tests__/mutex.test.ts +78 -0
  661. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +225 -0
  662. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +101 -0
  663. package/src/modules/workflow/git/branch-name.ts +70 -0
  664. package/src/modules/workflow/git/clone-config.ts +71 -0
  665. package/src/modules/workflow/git/git-version.ts +70 -0
  666. package/src/modules/workflow/git/git.service.ts +2248 -0
  667. package/src/modules/workflow/git/mutex.ts +22 -0
  668. package/src/modules/workflow/git/pull-request.service.ts +447 -0
  669. package/src/modules/workflow/locking-filesystem.ts +489 -0
  670. package/src/modules/workflow/pending-commits.service.ts +444 -0
  671. package/src/modules/workflow/pending-commits.worker.ts +361 -0
  672. package/src/modules/workflow/read-only-filesystem.ts +54 -0
  673. package/src/modules/workflow/recovery-bot.ts +73 -0
  674. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +560 -0
  675. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +176 -0
  676. package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +258 -0
  677. package/src/modules/workflow/review-workflow/review-workflow.interface.ts +199 -0
  678. package/src/modules/workflow/review-workflow/review-workflow.service.ts +806 -0
  679. package/src/modules/workflow/sanitize-error.ts +62 -0
  680. package/src/modules/workflow/session-ontology.policy.ts +70 -0
  681. package/src/modules/workflow/session-ontology.service.ts +178 -0
  682. package/src/modules/workflow/workflow-hooks.ts +101 -0
  683. package/src/modules/workflow/workflow.errors.ts +285 -0
  684. package/src/modules/workflow/workflow.routes.ts +858 -0
  685. package/src/modules/workflow/workflow.service.ts +1555 -0
  686. package/src/modules/workspace/__tests__/kb-seed.service.test.ts +213 -0
  687. package/src/modules/workspace/__tests__/routine-write-policy.test.ts +68 -0
  688. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -0
  689. package/src/modules/workspace/__tests__/spill-store.test.ts +43 -0
  690. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +233 -0
  691. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +176 -0
  692. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +303 -0
  693. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +276 -0
  694. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +120 -0
  695. package/src/modules/workspace/__tests__/workspace.service.test.ts +565 -0
  696. package/src/modules/workspace/__tests__/workspace.tools.test.ts +504 -0
  697. package/src/modules/workspace/bevel-ignore.ts +62 -0
  698. package/src/modules/workspace/kb-seed.interface.ts +36 -0
  699. package/src/modules/workspace/kb-seed.service.ts +361 -0
  700. package/src/modules/workspace/routine-write-policy.ts +79 -0
  701. package/src/modules/workspace/session-ontology.gate.ts +189 -0
  702. package/src/modules/workspace/session-sink.ts +25 -0
  703. package/src/modules/workspace/spill-store.ts +87 -0
  704. package/src/modules/workspace/workspace.routes.ts +1114 -0
  705. package/src/modules/workspace/workspace.service.ts +1343 -0
  706. package/src/modules/workspace/workspace.tools.ts +885 -0
  707. package/src/shared/__tests__/frontmatter-id.test.ts +88 -0
  708. package/src/shared/__tests__/fs-walk.test.ts +42 -0
  709. package/src/shared/__tests__/ssrf.test.ts +53 -0
  710. package/src/shared/__tests__/utcp-namespace.test.ts +19 -0
  711. package/src/shared/frontmatter-id.ts +72 -0
  712. package/src/shared/fs-walk.ts +30 -0
  713. package/src/shared/hash-email.ts +14 -0
  714. package/src/shared/kb-layout.test.ts +88 -0
  715. package/src/shared/kb-layout.ts +101 -0
  716. package/src/shared/ssrf.ts +78 -0
  717. package/src/shared/token-crypto.ts +54 -0
  718. package/src/shared/ttl-cache.ts +27 -0
  719. package/src/shared/utcp-namespace.ts +88 -0
  720. package/src/version.ts +19 -0
@@ -0,0 +1,858 @@
1
+ /**
2
+ * `/workflow/*` HTTP surface — the route family the frontend and agent
3
+ * should reach for instead of `/git/*` and `/pr/*`. Today these are additive
4
+ * (the old families keep working); migration plan in `bevel-platform/PLAN.md`.
5
+ *
6
+ * URL layout:
7
+ * /workspace/:id/workflow/... — workspace-scoped ops (branches, changes, locks, compare)
8
+ * /workflow/change-requests/... — change-request ops (list/detail/comments/approvals/merge/reject)
9
+ *
10
+ * Workspace ids are `encodeURIComponent(branch)` (PLAN §3) — branches are
11
+ * shared by every user editing them, so route handlers gate on
12
+ * authentication only. Coordination of concurrent edits happens via the
13
+ * file-lock service, not via per-user clones.
14
+ *
15
+ * Error mapping: every workflow error class extends `WorkflowDomainError`
16
+ * which exposes `.status` + optional `.payload`, so a single `toHttpError`
17
+ * covers all branches/changes/CR errors uniformly.
18
+ */
19
+
20
+ import express from 'express';
21
+ import type {
22
+ AuthUser,
23
+ ChangeInput,
24
+ IWorkflowService,
25
+ OpenChangeRequestInput,
26
+ PostChangeRequestCommentInput,
27
+ } from '@bevel-software/platform-shared';
28
+ import type { AuthService } from '../auth/auth.service.js';
29
+ import type { WorkspaceService } from '../workspace/workspace.service.js';
30
+ import { branchForWorkspaceId } from '../workspace/workspace.service.js';
31
+ import type { WorkflowEventBus } from './event-bus.js';
32
+ import { WorkflowDomainError } from './workflow.errors.js';
33
+ import '../auth/auth.middleware.js'; // Express Request augmentation
34
+
35
+ function toHttpError(
36
+ err: unknown,
37
+ ): { status: number; body: Record<string, unknown> } {
38
+ if (err instanceof WorkflowDomainError) {
39
+ return {
40
+ status: err.status,
41
+ body: { error: err.message, ...(err.payload ?? {}) },
42
+ };
43
+ }
44
+ console.error('[workflow.routes] unhandled error:', err);
45
+ return { status: 500, body: { error: 'Internal server error' } };
46
+ }
47
+
48
+ function parsePrNumber(raw: string): number | null {
49
+ if (!/^\d+$/.test(raw)) return null;
50
+ const n = Number(raw);
51
+ return n > 0 && Number.isSafeInteger(n) ? n : null;
52
+ }
53
+
54
+ function isTruthyQuery(v: unknown): boolean {
55
+ return v === '1' || v === 'true';
56
+ }
57
+
58
+ export function createWorkflowRoutes(
59
+ workflow: IWorkflowService,
60
+ workspaceService: WorkspaceService,
61
+ authService: AuthService,
62
+ events: WorkflowEventBus,
63
+ ): express.Router {
64
+ const router = express.Router({ mergeParams: true });
65
+
66
+ /**
67
+ * Authentication gate. Per PLAN §3, branches are shared workspaces —
68
+ * any authenticated user can access any branch. Authorization for
69
+ * mutations happens at the workflow / git layer (access tree, file
70
+ * locks, change-request approval gate). This helper just resolves the
71
+ * caller's `AuthUser` record; route bodies pass it to workflow methods
72
+ * for per-operation auth.
73
+ */
74
+ async function requireUser(
75
+ req: express.Request,
76
+ res: express.Response,
77
+ ): Promise<AuthUser | null> {
78
+ if (!req.userId) {
79
+ res.status(401).json({ error: 'Unauthenticated' });
80
+ return null;
81
+ }
82
+ try {
83
+ const user = await authService.getUserById(req.userId);
84
+ if (!user) {
85
+ res.status(401).json({ error: 'User not found' });
86
+ return null;
87
+ }
88
+ return user;
89
+ } catch (err) {
90
+ console.error('[workflow.routes] requireUser failed:', err);
91
+ res.status(500).json({ error: 'Internal server error' });
92
+ return null;
93
+ }
94
+ }
95
+
96
+ // ── Branches ──────────────────────────────────────────────────────────────
97
+
98
+ router.get('/workspace/:id/workflow/branches', async (req, res) => {
99
+ if (!(await requireUser(req, res))) return;
100
+ try {
101
+ // `?fresh=1` forces a `git fetch --prune` (bypasses the implicit-fetch
102
+ // TTL) so a user-initiated refresh — e.g. opening the branch selector —
103
+ // immediately reflects branches another workspace just created/deleted.
104
+ const freshFetch = isTruthyQuery(req.query.fresh);
105
+ res.json(await workflow.listBranches(req.params.id, { freshFetch }));
106
+ } catch (err) {
107
+ const { status, body } = toHttpError(err);
108
+ res.status(status).json(body);
109
+ }
110
+ });
111
+
112
+ router.post('/workspace/:id/workflow/branches', async (req, res) => {
113
+ if (!(await requireUser(req, res))) return;
114
+ const { name, fromBase } = req.body as { name?: string; fromBase?: string };
115
+ if (!name) {
116
+ res.status(400).json({ error: 'name is required' });
117
+ return;
118
+ }
119
+ try {
120
+ res.json(await workflow.createBranch(req.params.id, name, fromBase));
121
+ } catch (err) {
122
+ const { status, body } = toHttpError(err);
123
+ res.status(status).json(body);
124
+ }
125
+ });
126
+
127
+ router.delete('/workspace/:id/workflow/branches/:name', async (req, res) => {
128
+ const user = await requireUser(req, res);
129
+ if (!user) return;
130
+ const onlyIfNoRemote = isTruthyQuery(req.query.onlyIfNoRemote);
131
+ try {
132
+ await workflow.deleteBranch(req.params.id, req.params.name, user, { onlyIfNoRemote });
133
+ res.json({ status: 'deleted' });
134
+ } catch (err) {
135
+ const { status, body } = toHttpError(err);
136
+ res.status(status).json(body);
137
+ }
138
+ });
139
+
140
+ // The legacy `POST /workspace/:id/workflow/switch` route is gone. Under
141
+ // the per-branch workspace model the UI switches branches by navigating
142
+ // the URL — the frontend's workspace bootstrap clones (or selects) the
143
+ // destination branch's per-branch workspace dir. There is no separate
144
+ // server-side "switch" step on the source workspace's clone, which was
145
+ // the only place a dirty working tree could leak as a user-facing 409.
146
+
147
+ // `POST /workspace/:id/workflow/fork-draft` removed: the dirty-tree
148
+ // carry-along escape hatch has no callers under save=share + per-branch
149
+ // workspaces. Use `POST /workflow/branches` (createBranch) instead.
150
+
151
+ router.get('/workspace/:id/workflow/branch-status', async (req, res) => {
152
+ if (!(await requireUser(req, res))) return;
153
+ try {
154
+ // Make sure the per-branch workspace is fully cloned before we run any
155
+ // git command against it. Without this, a frontend hitting branch-status
156
+ // on first load can race the cold clone and `git rev-parse HEAD` returns
157
+ // "ambiguous argument 'HEAD'" against a half-initialised .git directory.
158
+ // The call is idempotent and ~free in the warm case (Map lookup), so it
159
+ // costs nothing once the workspace is already on disk.
160
+ await workspaceService.getOrCreateForBranch(branchForWorkspaceId(req.params.id));
161
+ res.json(await workflow.branchStatus(req.params.id));
162
+ } catch (err) {
163
+ const { status, body } = toHttpError(err);
164
+ res.status(status).json(body);
165
+ }
166
+ });
167
+
168
+ // `POST /workspace/:id/workflow/discard` removed: under save=share the
169
+ // working tree is never dirty, so there's nothing to discard.
170
+
171
+ router.post('/workspace/:id/workflow/share', async (req, res) => {
172
+ const user = await requireUser(req, res);
173
+ if (!user) return;
174
+ try {
175
+ await workflow.shareCurrentBranch(req.params.id, user);
176
+ res.json({ status: 'shared' });
177
+ } catch (err) {
178
+ const { status, body } = toHttpError(err);
179
+ res.status(status).json(body);
180
+ }
181
+ });
182
+
183
+ router.post('/workspace/:id/workflow/refresh-remotes', async (req, res) => {
184
+ if (!(await requireUser(req, res))) return;
185
+ try {
186
+ await workflow.refreshRemotes(req.params.id);
187
+ res.json({ status: 'refreshed' });
188
+ } catch (err) {
189
+ const { status, body } = toHttpError(err);
190
+ res.status(status).json(body);
191
+ }
192
+ });
193
+
194
+ router.post('/workspace/:id/workflow/update-from-remote', async (req, res) => {
195
+ if (!(await requireUser(req, res))) return;
196
+ try {
197
+ await workflow.updateFromRemote(req.params.id);
198
+ res.json({ status: 'updated' });
199
+ } catch (err) {
200
+ const { status, body } = toHttpError(err);
201
+ res.status(status).json(body);
202
+ }
203
+ });
204
+
205
+ router.get('/workspace/:id/workflow/fork-base', async (req, res) => {
206
+ if (!(await requireUser(req, res))) return;
207
+ const branch = typeof req.query.branch === 'string' ? req.query.branch : '';
208
+ if (!branch) {
209
+ res.status(400).json({ error: 'branch is required' });
210
+ return;
211
+ }
212
+ try {
213
+ const base = await workflow.resolveForkBase(req.params.id, branch);
214
+ res.json({ base });
215
+ } catch (err) {
216
+ const { status, body } = toHttpError(err);
217
+ res.status(status).json(body);
218
+ }
219
+ });
220
+
221
+ router.get('/workspace/:id/workflow/pending-changes', async (req, res) => {
222
+ if (!(await requireUser(req, res))) return;
223
+ try {
224
+ const paths = await workflow.listPendingChangePaths(req.params.id);
225
+ res.json({ paths });
226
+ } catch (err) {
227
+ const { status, body } = toHttpError(err);
228
+ res.status(status).json(body);
229
+ }
230
+ });
231
+
232
+ // `GET /workspace/:id/workflow/working-changes` + `working-diff` removed:
233
+ // working-tree dirty state is always empty under save=share.
234
+
235
+ // ── Changes ───────────────────────────────────────────────────────────────
236
+
237
+ router.post('/workspace/:id/workflow/changes', async (req, res) => {
238
+ const user = await requireUser(req, res);
239
+ if (!user) return;
240
+ const input = req.body as ChangeInput;
241
+ try {
242
+ res.json(await workflow.commitChange(req.params.id, user, input));
243
+ } catch (err) {
244
+ const { status, body } = toHttpError(err);
245
+ res.status(status).json(body);
246
+ }
247
+ });
248
+
249
+ router.get('/workspace/:id/workflow/changes', async (req, res) => {
250
+ if (!(await requireUser(req, res))) return;
251
+ const pathParam = typeof req.query.path === 'string' ? req.query.path : '';
252
+ if (!pathParam) {
253
+ res.status(400).json({ error: 'path is required' });
254
+ return;
255
+ }
256
+ const rawLimit = Number.parseInt(String(req.query.limit ?? '20'), 10);
257
+ const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 100)) : 20;
258
+ try {
259
+ res.json({ changes: await workflow.listChangesForFile(req.params.id, pathParam, limit) });
260
+ } catch (err) {
261
+ const { status, body } = toHttpError(err);
262
+ res.status(status).json(body);
263
+ }
264
+ });
265
+
266
+ router.post('/workspace/:id/workflow/changes/:sha/revert', async (req, res) => {
267
+ const user = await requireUser(req, res);
268
+ if (!user) return;
269
+ try {
270
+ res.json(await workflow.revertChange(req.params.id, user, req.params.sha));
271
+ } catch (err) {
272
+ const { status, body } = toHttpError(err);
273
+ res.status(status).json(body);
274
+ }
275
+ });
276
+
277
+ router.get('/workspace/:id/workflow/compare-file', async (req, res) => {
278
+ if (!(await requireUser(req, res))) return;
279
+ const pathParam = typeof req.query.path === 'string' ? req.query.path : '';
280
+ const fromBranch = typeof req.query.from === 'string' ? req.query.from : '';
281
+ const toBranch = typeof req.query.to === 'string' ? req.query.to : '';
282
+ if (!pathParam || !fromBranch || !toBranch) {
283
+ res.status(400).json({ error: 'path, from, and to are required' });
284
+ return;
285
+ }
286
+ try {
287
+ const diff = await workflow.compareFile(req.params.id, pathParam, fromBranch, toBranch);
288
+ res.json({ diff });
289
+ } catch (err) {
290
+ const { status, body } = toHttpError(err);
291
+ res.status(status).json(body);
292
+ }
293
+ });
294
+
295
+ router.get('/workspace/:id/workflow/show-file', async (req, res) => {
296
+ if (!(await requireUser(req, res))) return;
297
+ const pathParam = typeof req.query.path === 'string' ? req.query.path : '';
298
+ const sha = typeof req.query.sha === 'string' ? req.query.sha : '';
299
+ if (!pathParam || !sha) {
300
+ res.status(400).json({ error: 'path and sha are required' });
301
+ return;
302
+ }
303
+ try {
304
+ const diff = await workflow.showFileAtChange(req.params.id, pathParam, sha);
305
+ res.json({ diff });
306
+ } catch (err) {
307
+ const { status, body } = toHttpError(err);
308
+ res.status(status).json(body);
309
+ }
310
+ });
311
+
312
+ // ── File locks ────────────────────────────────────────────────────────────
313
+
314
+ router.post('/workspace/:id/workflow/locks', async (req, res) => {
315
+ const user = await requireUser(req, res);
316
+ if (!user) return;
317
+ const { branch, path: targetPath } = req.body as { branch?: string; path?: string };
318
+ if (!branch || !targetPath) {
319
+ res.status(400).json({ error: 'branch and path are required' });
320
+ return;
321
+ }
322
+ try {
323
+ res.json(await workflow.acquireLock(req.params.id, branch, targetPath, user));
324
+ } catch (err) {
325
+ const { status, body } = toHttpError(err);
326
+ res.status(status).json(body);
327
+ }
328
+ });
329
+
330
+ // Release: branch + path travel via the body, not the query string,
331
+ // because DELETE bodies are reliable for our own clients. Under the
332
+ // pending-commits queue the release no longer returns a `Change` — the
333
+ // commit hasn't happened yet (the background worker drains the queue
334
+ // out of band). The route's `{ queued: true }` reply just signals
335
+ // "lock released, commit scheduled."
336
+ router.delete('/workspace/:id/workflow/locks', async (req, res) => {
337
+ const user = await requireUser(req, res);
338
+ if (!user) return;
339
+ const body = (req.body ?? {}) as { branch?: string; path?: string };
340
+ const branch = body.branch ?? (typeof req.query.branch === 'string' ? req.query.branch : '');
341
+ const targetPath = body.path ?? (typeof req.query.path === 'string' ? req.query.path : '');
342
+ if (!branch || !targetPath) {
343
+ res.status(400).json({ error: 'branch and path are required' });
344
+ return;
345
+ }
346
+ try {
347
+ await workflow.releaseLock(req.params.id, branch, targetPath, user);
348
+ res.json({ queued: true });
349
+ } catch (err) {
350
+ const { status, body } = toHttpError(err);
351
+ res.status(status).json(body);
352
+ }
353
+ });
354
+
355
+ // Commit-while-locked — autosave checkpoint. Lock stays held; the
356
+ // file's accumulated edits land as a one-file change.
357
+ router.post('/workspace/:id/workflow/locks/checkpoint', async (req, res) => {
358
+ const user = await requireUser(req, res);
359
+ if (!user) return;
360
+ const { branch, path: targetPath, summary } = req.body as {
361
+ branch?: string;
362
+ path?: string;
363
+ summary?: string;
364
+ };
365
+ if (!branch || !targetPath) {
366
+ res.status(400).json({ error: 'branch and path are required' });
367
+ return;
368
+ }
369
+ try {
370
+ const change = await workflow.commitFileWhileLocked(
371
+ req.params.id,
372
+ branch,
373
+ targetPath,
374
+ user,
375
+ summary,
376
+ );
377
+ res.json({ change });
378
+ } catch (err) {
379
+ const { status, body } = toHttpError(err);
380
+ res.status(status).json(body);
381
+ }
382
+ });
383
+
384
+ router.post('/workspace/:id/workflow/locks/heartbeat', async (req, res) => {
385
+ const user = await requireUser(req, res);
386
+ if (!user) return;
387
+ const { branch, path: targetPath } = req.body as { branch?: string; path?: string };
388
+ if (!branch || !targetPath) {
389
+ res.status(400).json({ error: 'branch and path are required' });
390
+ return;
391
+ }
392
+ try {
393
+ res.json(await workflow.heartbeatLock(req.params.id, branch, targetPath, user));
394
+ } catch (err) {
395
+ const { status, body } = toHttpError(err);
396
+ res.status(status).json(body);
397
+ }
398
+ });
399
+
400
+ router.get('/workspace/:id/workflow/locks', async (req, res) => {
401
+ if (!(await requireUser(req, res))) return;
402
+ const branch = typeof req.query.branch === 'string' ? req.query.branch : '';
403
+ const targetPath = typeof req.query.path === 'string' ? req.query.path : '';
404
+ if (!branch || !targetPath) {
405
+ res.status(400).json({ error: 'branch and path are required' });
406
+ return;
407
+ }
408
+ try {
409
+ res.json({ lock: await workflow.getLock(req.params.id, branch, targetPath) });
410
+ } catch (err) {
411
+ const { status, body } = toHttpError(err);
412
+ res.status(status).json(body);
413
+ }
414
+ });
415
+
416
+ // ── Change Requests ───────────────────────────────────────────────────────
417
+
418
+ router.get('/workflow/change-requests', async (req, res) => {
419
+ const fresh = isTruthyQuery(req.query.fresh);
420
+ try {
421
+ res.json(await workflow.listChangeRequests({ fresh }));
422
+ } catch (err) {
423
+ const { status, body } = toHttpError(err);
424
+ res.status(status).json(body);
425
+ }
426
+ });
427
+
428
+ router.get('/workflow/change-requests/mine', async (req, res) => {
429
+ const user = await requireUser(req, res);
430
+ if (!user) return;
431
+ try {
432
+ res.json(await workflow.listChangeRequestsAuthoredBy(user.email));
433
+ } catch (err) {
434
+ const { status, body } = toHttpError(err);
435
+ res.status(status).json(body);
436
+ }
437
+ });
438
+
439
+ router.get('/workflow/change-requests/for-me', async (req, res) => {
440
+ const user = await requireUser(req, res);
441
+ if (!user) return;
442
+ const fresh = isTruthyQuery(req.query.fresh);
443
+ try {
444
+ const workspace = await workspaceService.getOrCreateForUser(user);
445
+ res.json(await workflow.listChangeRequestsForUser(workspace.id, user.email, { fresh }));
446
+ } catch (err) {
447
+ const { status, body } = toHttpError(err);
448
+ res.status(status).json(body);
449
+ }
450
+ });
451
+
452
+ router.get('/workflow/change-requests/:number', async (req, res) => {
453
+ const num = parsePrNumber(req.params.number);
454
+ if (num === null) {
455
+ res.status(400).json({ error: 'invalid change request number' });
456
+ return;
457
+ }
458
+ try {
459
+ const cr = await workflow.getChangeRequest(num);
460
+ if (!cr) {
461
+ res.status(404).json({ error: 'change request not found' });
462
+ return;
463
+ }
464
+ res.json(cr);
465
+ } catch (err) {
466
+ const { status, body } = toHttpError(err);
467
+ res.status(status).json(body);
468
+ }
469
+ });
470
+
471
+ router.get('/workflow/change-requests/:number/detail', async (req, res) => {
472
+ const num = parsePrNumber(req.params.number);
473
+ if (num === null) {
474
+ res.status(400).json({ error: 'invalid change request number' });
475
+ return;
476
+ }
477
+ try {
478
+ const fresh = isTruthyQuery(req.query.fresh);
479
+ let workspaceId: string | undefined;
480
+ let viewerEmail: string | undefined;
481
+ // Best-effort caller resolution — anonymous detail fetches still work
482
+ // (mirroring the legacy /pr/:n/detail behaviour), they just don't get
483
+ // `viewerCanApprove` populated.
484
+ if (req.userId) {
485
+ try {
486
+ const user = await authService.getUserById(req.userId);
487
+ if (user) {
488
+ const ws = await workspaceService.getOrCreateForUser(user);
489
+ workspaceId = ws.id;
490
+ viewerEmail = user.email;
491
+ }
492
+ } catch {
493
+ // Swallow — keeps the detail fetch resilient to a transient auth
494
+ // blip when the caller's workspace isn't yet provisioned.
495
+ }
496
+ }
497
+ const detail = await workflow.getChangeRequestDetail(num, {
498
+ fresh,
499
+ workspaceId,
500
+ viewerEmail,
501
+ });
502
+ if (!detail) {
503
+ res.status(404).json({ error: 'change request not found' });
504
+ return;
505
+ }
506
+ res.json(detail);
507
+ } catch (err) {
508
+ const { status, body } = toHttpError(err);
509
+ res.status(status).json(body);
510
+ }
511
+ });
512
+
513
+ router.post('/workflow/change-requests', async (req, res) => {
514
+ const user = await requireUser(req, res);
515
+ if (!user) return;
516
+ const body = req.body as Partial<OpenChangeRequestInput>;
517
+ if (!body.sourceBranch || !body.targetBranch || !body.title) {
518
+ res.status(400).json({ error: 'sourceBranch, targetBranch and title are required' });
519
+ return;
520
+ }
521
+ try {
522
+ // Resolve the source-branch workspace — `git.mergeFromOrigin`
523
+ // requires the clone to be checked out to `sourceBranch`, and with
524
+ // workspace-per-branch the right clone is `workspaces/<encoded
525
+ // sourceBranch>/...`. Falling back to `getOrCreateForUser` returned
526
+ // the default workspace (target-company-state), which broke CRs
527
+ // whose source wasn't the default. `getOrCreateForBranch` lazily
528
+ // bootstraps the clone if this is the first request for it.
529
+ const workspace = await workspaceService.getOrCreateForBranch(body.sourceBranch);
530
+ res.json(
531
+ await workflow.openChangeRequest(workspace.id, user, {
532
+ sourceBranch: body.sourceBranch,
533
+ targetBranch: body.targetBranch,
534
+ title: body.title,
535
+ description: body.description,
536
+ }),
537
+ );
538
+ } catch (err) {
539
+ const { status, body } = toHttpError(err);
540
+ res.status(status).json(body);
541
+ }
542
+ });
543
+
544
+ router.post('/workflow/change-requests/:number/update-from-target', async (req, res) => {
545
+ const num = parsePrNumber(req.params.number);
546
+ if (num === null) {
547
+ res.status(400).json({ error: 'invalid change request number' });
548
+ return;
549
+ }
550
+ const user = await requireUser(req, res);
551
+ if (!user) return;
552
+ try {
553
+ // `updateFromTarget` re-runs the `target → source` merge, which
554
+ // needs the source branch's workspace (the on-disk clone checked
555
+ // out to that branch). Discover the source branch from the CR
556
+ // first, then resolve the matching workspace — same reasoning as
557
+ // `openChangeRequest` above. `getOrCreateForBranch` lazily clones
558
+ // if the source branch hasn't been touched on this replica yet.
559
+ const cr = await workflow.getChangeRequest(num);
560
+ if (!cr) {
561
+ res.status(404).json({ error: 'change request not found' });
562
+ return;
563
+ }
564
+ const workspace = await workspaceService.getOrCreateForBranch(cr.branch);
565
+ res.json(await workflow.updateFromTarget(workspace.id, user, num));
566
+ } catch (err) {
567
+ const { status, body } = toHttpError(err);
568
+ res.status(status).json(body);
569
+ }
570
+ });
571
+
572
+ router.get('/workflow/change-requests/:number/comments', async (req, res) => {
573
+ const num = parsePrNumber(req.params.number);
574
+ if (num === null) {
575
+ res.status(400).json({ error: 'invalid change request number' });
576
+ return;
577
+ }
578
+ try {
579
+ res.json(await workflow.listComments(num));
580
+ } catch (err) {
581
+ const { status, body } = toHttpError(err);
582
+ res.status(status).json(body);
583
+ }
584
+ });
585
+
586
+ router.post('/workflow/change-requests/:number/comments', async (req, res) => {
587
+ const num = parsePrNumber(req.params.number);
588
+ if (num === null) {
589
+ res.status(400).json({ error: 'invalid change request number' });
590
+ return;
591
+ }
592
+ const user = await requireUser(req, res);
593
+ if (!user) return;
594
+ const body = (req.body ?? {}) as Partial<PostChangeRequestCommentInput>;
595
+ if (typeof body.body !== 'string') {
596
+ res.status(400).json({ error: 'body is required' });
597
+ return;
598
+ }
599
+ try {
600
+ // Resolve the current head from the detail endpoint so the comment
601
+ // anchors against what the user is actually viewing. Mirrors the
602
+ // legacy /pr/:n/comments handler.
603
+ const detail = await workflow.getChangeRequestDetail(num, { fresh: true });
604
+ if (!detail) {
605
+ res.status(404).json({ error: 'change request not found' });
606
+ return;
607
+ }
608
+ const comment = await workflow.postComment(
609
+ num,
610
+ user,
611
+ {
612
+ body: body.body,
613
+ path: body.path,
614
+ line: body.line,
615
+ parentId: body.parentId,
616
+ },
617
+ detail.headSha,
618
+ );
619
+ res.json(comment);
620
+ } catch (err) {
621
+ const { status, body } = toHttpError(err);
622
+ res.status(status).json(body);
623
+ }
624
+ });
625
+
626
+ router.patch('/workflow/change-requests/:number/comments/:id', async (req, res) => {
627
+ const num = parsePrNumber(req.params.number);
628
+ if (num === null) {
629
+ res.status(400).json({ error: 'invalid change request number' });
630
+ return;
631
+ }
632
+ const user = await requireUser(req, res);
633
+ if (!user) return;
634
+ const body = (req.body ?? {}) as { body?: unknown };
635
+ if (typeof body.body !== 'string') {
636
+ res.status(400).json({ error: 'body is required' });
637
+ return;
638
+ }
639
+ try {
640
+ res.json(await workflow.editComment(req.params.id, num, user, body.body));
641
+ } catch (err) {
642
+ const { status, body } = toHttpError(err);
643
+ res.status(status).json(body);
644
+ }
645
+ });
646
+
647
+ router.delete('/workflow/change-requests/:number/comments/:id', async (req, res) => {
648
+ const num = parsePrNumber(req.params.number);
649
+ if (num === null) {
650
+ res.status(400).json({ error: 'invalid change request number' });
651
+ return;
652
+ }
653
+ const user = await requireUser(req, res);
654
+ if (!user) return;
655
+ try {
656
+ await workflow.deleteComment(req.params.id, num, user);
657
+ res.json({ status: 'deleted' });
658
+ } catch (err) {
659
+ const { status, body } = toHttpError(err);
660
+ res.status(status).json(body);
661
+ }
662
+ });
663
+
664
+ // Approve / unapprove. Both fetch fresh detail so the approval pins to the
665
+ // current head — a stale cached head could let the gate accept an approval
666
+ // the user never actually saw.
667
+ router.post('/workflow/change-requests/:number/files/approve', async (req, res) => {
668
+ const num = parsePrNumber(req.params.number);
669
+ if (num === null) {
670
+ res.status(400).json({ error: 'invalid change request number' });
671
+ return;
672
+ }
673
+ const user = await requireUser(req, res);
674
+ if (!user) return;
675
+ const body = (req.body ?? {}) as { path?: string };
676
+ if (typeof body.path !== 'string' || !body.path) {
677
+ res.status(400).json({ error: 'path is required' });
678
+ return;
679
+ }
680
+ try {
681
+ const workspace = await workspaceService.getOrCreateForUser(user);
682
+ const detail = await workflow.getChangeRequestDetail(num, {
683
+ fresh: true,
684
+ workspaceId: workspace.id,
685
+ viewerEmail: user.email,
686
+ });
687
+ if (!detail) {
688
+ res.status(404).json({ error: 'change request not found' });
689
+ return;
690
+ }
691
+ const approvals = await workflow.approveFile(
692
+ num,
693
+ body.path,
694
+ user,
695
+ detail.files,
696
+ detail.headSha,
697
+ detail.base,
698
+ detail.authorId ?? null,
699
+ workspace.id,
700
+ );
701
+ res.json({ approvals });
702
+ } catch (err) {
703
+ const { status, body } = toHttpError(err);
704
+ res.status(status).json(body);
705
+ }
706
+ });
707
+
708
+ router.delete('/workflow/change-requests/:number/files/approve', async (req, res) => {
709
+ const num = parsePrNumber(req.params.number);
710
+ if (num === null) {
711
+ res.status(400).json({ error: 'invalid change request number' });
712
+ return;
713
+ }
714
+ const user = await requireUser(req, res);
715
+ if (!user) return;
716
+ const targetPath = typeof req.query.path === 'string' ? req.query.path : '';
717
+ if (!targetPath) {
718
+ res.status(400).json({ error: 'path is required' });
719
+ return;
720
+ }
721
+ try {
722
+ const workspace = await workspaceService.getOrCreateForUser(user);
723
+ const detail = await workflow.getChangeRequestDetail(num, {
724
+ fresh: true,
725
+ workspaceId: workspace.id,
726
+ viewerEmail: user.email,
727
+ });
728
+ if (!detail) {
729
+ res.status(404).json({ error: 'change request not found' });
730
+ return;
731
+ }
732
+ const approvals = await workflow.unapproveFile(
733
+ num,
734
+ targetPath,
735
+ user,
736
+ detail.files,
737
+ detail.headSha,
738
+ detail.base,
739
+ detail.authorId ?? null,
740
+ workspace.id,
741
+ );
742
+ res.json({ approvals });
743
+ } catch (err) {
744
+ const { status, body } = toHttpError(err);
745
+ res.status(status).json(body);
746
+ }
747
+ });
748
+
749
+ router.post('/workflow/change-requests/:number/reject', async (req, res) => {
750
+ const num = parsePrNumber(req.params.number);
751
+ if (num === null) {
752
+ res.status(400).json({ error: 'invalid change request number' });
753
+ return;
754
+ }
755
+ const user = await requireUser(req, res);
756
+ if (!user) return;
757
+ try {
758
+ const workspace = await workspaceService.getOrCreateForUser(user);
759
+ const detail = await workflow.getChangeRequestDetail(num, {
760
+ fresh: true,
761
+ workspaceId: workspace.id,
762
+ viewerEmail: user.email,
763
+ });
764
+ if (!detail) {
765
+ res.status(404).json({ error: 'change request not found' });
766
+ return;
767
+ }
768
+ const result = await workflow.rejectChangeRequest(
769
+ num,
770
+ user,
771
+ detail.state,
772
+ detail.authorId ?? null,
773
+ detail.base,
774
+ workspace.id,
775
+ );
776
+ res.json(result);
777
+ } catch (err) {
778
+ const { status, body } = toHttpError(err);
779
+ res.status(status).json(body);
780
+ }
781
+ });
782
+
783
+ router.post('/workflow/change-requests/:number/merge', async (req, res) => {
784
+ const num = parsePrNumber(req.params.number);
785
+ if (num === null) {
786
+ res.status(400).json({ error: 'invalid change request number' });
787
+ return;
788
+ }
789
+ const user = await requireUser(req, res);
790
+ if (!user) return;
791
+ const body = (req.body ?? {}) as { bypass?: unknown };
792
+ const bypass = body.bypass === true;
793
+
794
+ // Async merge. The gate re-validation fetches the PR's full file list from
795
+ // GitHub (paginated — slow on a large change request) and `gh pr merge` can
796
+ // itself take tens of seconds, long enough to outlive the gateway's idle
797
+ // timeout — surfacing to the user as a 502 even while the merge is still
798
+ // running. So we ack with 202 and run the merge in the background, reporting
799
+ // the result over the event bus: `change-request-merged` on success (emitted
800
+ // by the workflow service; the PR viewer refreshes off it), and a
801
+ // user-scoped `change-request-merge-failed` on a gate block / conflict /
802
+ // merge error so the UI that kicked it off can react.
803
+ res.status(202).json({ status: 'merging', number: num });
804
+ void (async () => {
805
+ try {
806
+ const workspace = await workspaceService.getOrCreateForUser(user);
807
+ const detail = await workflow.getChangeRequestDetail(num, {
808
+ fresh: true,
809
+ workspaceId: workspace.id,
810
+ viewerEmail: user.email,
811
+ });
812
+ if (!detail) {
813
+ events.emit({
814
+ kind: 'change-request-merge-failed',
815
+ forUserId: user.id,
816
+ number: num,
817
+ reason: 'change request not found',
818
+ conflicts: false,
819
+ });
820
+ return;
821
+ }
822
+ const outcome = await workflow.mergeChangeRequest(
823
+ num,
824
+ user,
825
+ detail.headSha,
826
+ detail.approvals,
827
+ detail.state,
828
+ detail.title,
829
+ detail.base,
830
+ workspace.id,
831
+ { bypass },
832
+ );
833
+ if (outcome.kind === 'conflicts-need-resolution') {
834
+ events.emit({
835
+ kind: 'change-request-merge-failed',
836
+ forUserId: user.id,
837
+ number: num,
838
+ reason: 'This draft conflicts with the target and needs resolving first.',
839
+ conflicts: true,
840
+ });
841
+ }
842
+ // Success path: `workflow.mergeChangeRequest` emits `change-request-merged`.
843
+ } catch (err) {
844
+ const { body: errBody } = toHttpError(err);
845
+ console.error(`[workflow.routes] async merge of change request #${num} failed:`, err);
846
+ events.emit({
847
+ kind: 'change-request-merge-failed',
848
+ forUserId: user.id,
849
+ number: num,
850
+ reason: typeof errBody.error === 'string' ? errBody.error : 'Merge failed',
851
+ conflicts: false,
852
+ });
853
+ }
854
+ })();
855
+ });
856
+
857
+ return router;
858
+ }