@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,150 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import type { AuthUser, IWorkflowService } from '@bevel-software/platform-shared';
6
+ import type { IDiffService } from '../diff.interface.js';
7
+ import { workspaceIdForBranch } from '../../workspace/workspace.service.js';
8
+ import { WorkflowValidationError } from '../../workflow/workflow.errors.js';
9
+ import { rejectPathsLocked } from '../diff.routes.js';
10
+
11
+ /**
12
+ * `rejectPathsLocked` — the BATCH reject. All paths go through ONE
13
+ * `LockingFilesystem.writeFiles` cycle: every lock acquired before the first
14
+ * disk mutation, baselines restored / agent-added files deleted, then a single
15
+ * `commitChanges` and lock release. Fail-closed: contention or a commit error
16
+ * aborts with nothing committed.
17
+ */
18
+
19
+ const USER: AuthUser = { id: 'user-1', email: 'alice@example.com', name: 'Alice' };
20
+ const BRANCH = 'alice/revert-test';
21
+ const WORKSPACE_ID = workspaceIdForBranch(BRANCH);
22
+
23
+ interface Harness {
24
+ workflow: IWorkflowService;
25
+ diff: IDiffService;
26
+ calls: string[];
27
+ workspaceDir: string;
28
+ }
29
+
30
+ async function makeHarness(overrides: {
31
+ /** Per-path acquire outcome. Default: every path acquires cleanly. */
32
+ acquire?: (relPath: string) => { acquired: boolean; lock: { holderName: string } };
33
+ /** Throw from commitChanges to simulate a commit failure. */
34
+ commit?: () => void;
35
+ } = {}): Promise<Harness> {
36
+ const calls: string[] = [];
37
+ const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-reject-'));
38
+ // Baseline-carrying file (backup exists → revert = write baseline bytes)
39
+ // and an agent-added file (no backup → revert = delete).
40
+ await fs.mkdir(path.join(workspaceDir, 'knowledge-base'), { recursive: true });
41
+ await fs.writeFile(path.join(workspaceDir, 'knowledge-base/a.md'), 'agent version\n');
42
+ await fs.writeFile(path.join(workspaceDir, 'knowledge-base/added.md'), 'agent-added\n');
43
+
44
+ const acquireLock = vi.fn(async (_ws: string, _branch: string, relPath: string) => {
45
+ const result = overrides.acquire
46
+ ? overrides.acquire(relPath)
47
+ : { acquired: true, lock: { holderName: 'Alice' } };
48
+ calls.push(`acquire:${relPath}:${result.acquired}`);
49
+ return result;
50
+ });
51
+ const releaseLockNoCommit = vi.fn(async (_ws: string, _branch: string, relPath: string) => {
52
+ calls.push(`releaseNoCommit:${relPath}`);
53
+ });
54
+ const commitChanges = vi.fn(async (_ws: string, _user: AuthUser, summary: string) => {
55
+ calls.push(`commit:${summary}`);
56
+ overrides.commit?.();
57
+ return { sha: 'abc', summary } as never;
58
+ });
59
+ const workflow = { acquireLock, releaseLockNoCommit, commitChanges } as unknown as IWorkflowService;
60
+
61
+ const revertPlan = vi.fn(async (_ws: string, paths: string[]) => ({
62
+ workspaceDir,
63
+ writes: paths
64
+ .filter((p) => p.endsWith('a.md'))
65
+ .map((p) => ({ path: p, content: Buffer.from('baseline\n') })),
66
+ deletes: paths.filter((p) => p.endsWith('added.md')),
67
+ }));
68
+ const diff = { revertPlan } as unknown as IDiffService;
69
+ return { workflow, diff, calls, workspaceDir };
70
+ }
71
+
72
+ describe('rejectPathsLocked', () => {
73
+ let workspaceDir: string | undefined;
74
+ beforeEach(() => {
75
+ workspaceDir = undefined;
76
+ });
77
+ afterEach(async () => {
78
+ if (workspaceDir) await fs.rm(workspaceDir, { recursive: true, force: true });
79
+ });
80
+
81
+ it('acquires EVERY lock before mutating, restores + deletes, then commits ONCE and releases', async () => {
82
+ const h = await makeHarness();
83
+ workspaceDir = h.workspaceDir;
84
+
85
+ await rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
86
+ 'knowledge-base/a.md',
87
+ 'knowledge-base/added.md',
88
+ ]);
89
+
90
+ // All locks first (sorted), ONE commit, then per-path release (no per-file commit).
91
+ expect(h.calls).toEqual([
92
+ 'acquire:knowledge-base/a.md:true',
93
+ 'acquire:knowledge-base/added.md:true',
94
+ 'commit:Revert agent changes (2 files)',
95
+ 'releaseNoCommit:knowledge-base/a.md',
96
+ 'releaseNoCommit:knowledge-base/added.md',
97
+ ]);
98
+ // Disk: baseline restored, agent-added file gone.
99
+ expect(await fs.readFile(path.join(h.workspaceDir, 'knowledge-base/a.md'), 'utf-8')).toBe('baseline\n');
100
+ await expect(fs.access(path.join(h.workspaceDir, 'knowledge-base/added.md'))).rejects.toThrow();
101
+ });
102
+
103
+ it('is a no-op for an empty path list', async () => {
104
+ const h = await makeHarness();
105
+ workspaceDir = h.workspaceDir;
106
+ await expect(
107
+ rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, []),
108
+ ).resolves.toBeUndefined();
109
+ expect(h.calls).toEqual([]);
110
+ });
111
+
112
+ it('fails CLOSED on a contended lock: nothing mutated, nothing committed', async () => {
113
+ const h = await makeHarness({
114
+ acquire: (relPath) =>
115
+ relPath.endsWith('added.md')
116
+ ? { acquired: false, lock: { holderName: 'Bob' } }
117
+ : { acquired: true, lock: { holderName: 'Alice' } },
118
+ });
119
+ workspaceDir = h.workspaceDir;
120
+
121
+ await expect(
122
+ rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
123
+ 'knowledge-base/a.md',
124
+ 'knowledge-base/added.md',
125
+ ]),
126
+ ).rejects.toBeInstanceOf(WorkflowValidationError);
127
+
128
+ // No commit landed and the disk is untouched — the batch is atomic.
129
+ expect(h.calls.some((c) => c.startsWith('commit:'))).toBe(false);
130
+ expect(await fs.readFile(path.join(h.workspaceDir, 'knowledge-base/a.md'), 'utf-8')).toBe('agent version\n');
131
+ await expect(fs.access(path.join(h.workspaceDir, 'knowledge-base/added.md'))).resolves.toBeUndefined();
132
+ }, 15_000);
133
+
134
+ it('surfaces a commit failure as a WorkflowValidationError and releases without committing', async () => {
135
+ const h = await makeHarness({
136
+ commit: () => {
137
+ throw new Error('push rejected');
138
+ },
139
+ });
140
+ workspaceDir = h.workspaceDir;
141
+
142
+ await expect(
143
+ rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
144
+ 'knowledge-base/a.md',
145
+ ]),
146
+ ).rejects.toBeInstanceOf(WorkflowValidationError);
147
+ // Locks were dropped via the no-commit path after the failure.
148
+ expect(h.calls.filter((c) => c.startsWith('releaseNoCommit:'))).toHaveLength(1);
149
+ });
150
+ });
@@ -0,0 +1,184 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { WorkspaceService, workspaceIdForBranch } from '../../workspace/workspace.service.js';
6
+ import { WorkspaceMutex } from '../../workflow/git/mutex.js';
7
+ import { DiffService } from '../diff.service.js';
8
+
9
+ /**
10
+ * Atomic-seed regression suite.
11
+ *
12
+ * Bug we're guarding against (reproduced once in production): two users
13
+ * switch to a fresh, not-yet-cloned branch concurrently. One of them sees
14
+ * EVERY cloned file as an `added` pending change.
15
+ *
16
+ * Root cause: the original `ensureSeededUnlocked` did `mkdir(backupDir)`
17
+ * THEN `copyDiffableTree(...)`. If the copy threw mid-stream (any I/O
18
+ * hiccup, EACCES on one file, the process restarted, …) `backupDir`
19
+ * already existed but was empty. The next call's `fs.access(backupDir)`
20
+ * succeeded → skipped seeding → `listPendingUnlocked` saw "workspace has
21
+ * files, backup is empty" and produced phantom `added` entries for every
22
+ * cloned file.
23
+ *
24
+ * Fix: stage into `<backupDir>.seed-<rand>`, then `fs.rename` to commit.
25
+ * Either the fully-seeded backup appears at its canonical path, or
26
+ * nothing does and the next caller retries from scratch. Same pattern
27
+ * applies to `reseedUnlocked` (branch-switch hook).
28
+ */
29
+
30
+ const TEST_BRANCH = 'seed-test-branch';
31
+
32
+ interface Fixture {
33
+ workspacesRoot: string;
34
+ backupsRoot: string;
35
+ workspaceDir: string;
36
+ repoDir: string;
37
+ workspaceId: string;
38
+ workspaceService: WorkspaceService;
39
+ diffService: DiffService;
40
+ }
41
+
42
+ async function mkTmpRoot(prefix: string): Promise<string> {
43
+ return fs.mkdtemp(path.join(os.tmpdir(), prefix));
44
+ }
45
+
46
+ async function setup(): Promise<Fixture> {
47
+ const workspacesRoot = await mkTmpRoot('bevel-seed-ws-');
48
+ const backupsRoot = await mkTmpRoot('bevel-seed-backup-');
49
+ const workspaceId = workspaceIdForBranch(TEST_BRANCH);
50
+ const workspaceDir = path.join(workspacesRoot, workspaceId);
51
+ const repoDir = path.join(workspaceDir, 'knowledge-base');
52
+ await fs.mkdir(path.join(repoDir, '.git'), { recursive: true });
53
+ const workspaceService = new WorkspaceService(
54
+ workspacesRoot,
55
+ 'https://github.com/Bevel-Software/knowledge-base.git',
56
+ 'knowledge-base',
57
+ );
58
+ const mutex = new WorkspaceMutex();
59
+ const diffService = new DiffService(
60
+ workspaceService,
61
+ mutex,
62
+ workspacesRoot,
63
+ backupsRoot,
64
+ 'knowledge-base',
65
+ );
66
+ workspaceService.setDiffService(diffService);
67
+ return { workspacesRoot, backupsRoot, workspaceDir, repoDir, workspaceId, workspaceService, diffService };
68
+ }
69
+
70
+ async function teardown(fx: Fixture): Promise<void> {
71
+ await fs.rm(fx.workspacesRoot, { recursive: true, force: true });
72
+ await fs.rm(fx.backupsRoot, { recursive: true, force: true });
73
+ }
74
+
75
+ async function listDirEntries(dir: string): Promise<string[]> {
76
+ try {
77
+ return await fs.readdir(dir);
78
+ } catch {
79
+ return [];
80
+ }
81
+ }
82
+
83
+ describe('DiffService — atomic seed (production regression suite)', () => {
84
+ let fx: Fixture;
85
+ beforeEach(async () => { fx = await setup(); });
86
+ afterEach(async () => { await teardown(fx); });
87
+
88
+ it('a failed seed leaves NO backupDir behind — the next call retries from scratch', async () => {
89
+ // Stub `fs.copyFile` so the inner copy throws partway through. The
90
+ // seed must then clean up its staging dir AND propagate the error
91
+ // without leaving a phantom backup root. Otherwise the next seed
92
+ // would no-op on the empty backupDir and report every file as
93
+ // `added`.
94
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'a\n', 'utf-8');
95
+ await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'b\n', 'utf-8');
96
+
97
+ const copySpy = vi.spyOn(fs, 'copyFile').mockRejectedValueOnce(new Error('disk full'));
98
+ await expect(fx.diffService.currentSession(fx.workspaceId)).rejects.toThrow(/disk full/);
99
+ copySpy.mockRestore();
100
+
101
+ // The CANONICAL backupDir must not exist.
102
+ const backupDir = path.join(fx.backupsRoot, fx.workspaceId);
103
+ await expect(fs.access(backupDir)).rejects.toThrow();
104
+
105
+ // And no staging dirs left behind (the production symptom of the
106
+ // old bug was the directory shell sitting there empty).
107
+ const siblings = await listDirEntries(fx.backupsRoot);
108
+ expect(siblings).toEqual([]);
109
+
110
+ // A follow-up call with the failure cleared seeds cleanly and
111
+ // reports no pending changes.
112
+ const session = await fx.diffService.currentSession(fx.workspaceId);
113
+ expect(session).toBeNull();
114
+ expect(await fs.readFile(path.join(backupDir, 'knowledge-base', 'a.md'), 'utf-8')).toBe('a\n');
115
+ });
116
+
117
+ it('two concurrent seeds against the same workspace produce a single backup with no phantom pending', async () => {
118
+ // Mutex-serialised in practice, but we exercise the concurrency
119
+ // anyway so a future refactor that drops the mutex still has to
120
+ // keep the seed atomic. Both callers should observe `session === null`
121
+ // (workspace == backup); neither should see phantom `added` entries.
122
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'foo\n', 'utf-8');
123
+ await fs.writeFile(path.join(fx.repoDir, 'bar.md'), 'bar\n', 'utf-8');
124
+
125
+ const [s1, s2] = await Promise.all([
126
+ fx.diffService.currentSession(fx.workspaceId),
127
+ fx.diffService.currentSession(fx.workspaceId),
128
+ ]);
129
+
130
+ expect(s1).toBeNull();
131
+ expect(s2).toBeNull();
132
+
133
+ // Backup is fully populated.
134
+ const backupRepo = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base');
135
+ expect(await fs.readFile(path.join(backupRepo, 'foo.md'), 'utf-8')).toBe('foo\n');
136
+ expect(await fs.readFile(path.join(backupRepo, 'bar.md'), 'utf-8')).toBe('bar\n');
137
+
138
+ // No stray staging dirs.
139
+ const entries = await listDirEntries(fx.backupsRoot);
140
+ expect(entries).toEqual([fx.workspaceId]);
141
+ });
142
+
143
+ it('reseed (branch-switch hook) leaves the previous backup intact when the new copy fails', async () => {
144
+ // The branch-switch reseed wipes the existing backup and replaces it
145
+ // with a fresh copy of the new branch's working tree. If the copy
146
+ // fails mid-stream, we must NOT vaporise the old backup — the user
147
+ // would otherwise see every file on the OLD branch flip to pending.
148
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'before\n', 'utf-8');
149
+ // Seed the initial backup.
150
+ await fx.diffService.currentSession(fx.workspaceId);
151
+ const backupFile = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'a.md');
152
+ expect(await fs.readFile(backupFile, 'utf-8')).toBe('before\n');
153
+
154
+ // Disk advances (e.g. branch checkout swapped the bytes), then reseed.
155
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'after\n', 'utf-8');
156
+ const copySpy = vi.spyOn(fs, 'copyFile').mockRejectedValueOnce(new Error('disk full'));
157
+ await expect(fx.diffService.resetBackup(fx.workspaceId)).rejects.toThrow(/disk full/);
158
+ copySpy.mockRestore();
159
+
160
+ // The OLD backup is still there. `listPendingUnlocked` sees `after`
161
+ // on disk vs `before` in backup → one modified entry (correct
162
+ // behavior because the new bytes haven't been promoted yet).
163
+ expect(await fs.readFile(backupFile, 'utf-8')).toBe('before\n');
164
+ const session = await fx.diffService.currentSession(fx.workspaceId);
165
+ expect(session?.changes.length).toBe(1);
166
+ expect(session!.changes[0]).toMatchObject({
167
+ path: 'knowledge-base/a.md',
168
+ kind: 'modified',
169
+ });
170
+ });
171
+
172
+ it('reseed succeeds: old backup vaporised, new backup matches new disk state, session is clean', async () => {
173
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
174
+ await fx.diffService.currentSession(fx.workspaceId);
175
+ // Branch switched (simulated): disk now has different bytes.
176
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
177
+ await fx.diffService.resetBackup(fx.workspaceId);
178
+
179
+ // Backup advanced; session reports no drift.
180
+ const backupFile = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'a.md');
181
+ expect(await fs.readFile(backupFile, 'utf-8')).toBe('v2\n');
182
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
183
+ });
184
+ });
@@ -0,0 +1,202 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { WorkspaceService, workspaceIdForBranch } from '../../workspace/workspace.service.js';
6
+ import { WorkspaceMutex } from '../../workflow/git/mutex.js';
7
+ import { DiffService } from '../diff.service.js';
8
+
9
+ async function mkTmpRoot(prefix: string): Promise<string> {
10
+ return fs.mkdtemp(path.join(os.tmpdir(), prefix));
11
+ }
12
+
13
+ // Workspace-per-branch (PLAN §3) — the test fixture uses a fake branch
14
+ // `diff-test-branch`. The workspace id is `encodeURIComponent(branch)`
15
+ // and the on-disk path is `<workspacesRoot>/<id>/<kbDirName>/`. We
16
+ // pre-seed the inner `.git` so `WorkspaceService.resolveWorkspaceDir`
17
+ // accepts the path without falling back to the clone-from-origin
18
+ // bootstrap that the unit suite can't perform.
19
+ const TEST_BRANCH = 'diff-test-branch';
20
+
21
+ interface Fixture {
22
+ workspacesRoot: string;
23
+ backupsRoot: string;
24
+ workspaceDir: string;
25
+ repoDir: string;
26
+ workspaceId: string;
27
+ workspaceService: WorkspaceService;
28
+ diffService: DiffService;
29
+ }
30
+
31
+ async function setup(): Promise<Fixture> {
32
+ const workspacesRoot = await mkTmpRoot('bevel-diff-ws-');
33
+ const backupsRoot = await mkTmpRoot('bevel-diff-backup-');
34
+ const workspaceId = workspaceIdForBranch(TEST_BRANCH);
35
+ const workspaceDir = path.join(workspacesRoot, workspaceId);
36
+ const repoDir = path.join(workspaceDir, 'knowledge-base');
37
+ // Inner `.git` dir is the marker `resolveWorkspaceDir` looks for.
38
+ await fs.mkdir(path.join(repoDir, '.git'), { recursive: true });
39
+ const workspaceService = new WorkspaceService(
40
+ workspacesRoot,
41
+ 'https://github.com/Bevel-Software/knowledge-base.git',
42
+ 'knowledge-base',
43
+ );
44
+ const mutex = new WorkspaceMutex();
45
+ const diffService = new DiffService(
46
+ workspaceService,
47
+ mutex,
48
+ workspacesRoot,
49
+ backupsRoot,
50
+ 'knowledge-base',
51
+ );
52
+ workspaceService.setDiffService(diffService);
53
+ return { workspacesRoot, backupsRoot, workspaceDir, repoDir, workspaceId, workspaceService, diffService };
54
+ }
55
+
56
+ async function teardown(fx: Fixture): Promise<void> {
57
+ await fs.rm(fx.workspacesRoot, { recursive: true, force: true });
58
+ await fs.rm(fx.backupsRoot, { recursive: true, force: true });
59
+ }
60
+
61
+ describe('DiffService', () => {
62
+ let fx: Fixture;
63
+ beforeEach(async () => { fx = await setup(); });
64
+ afterEach(async () => { await teardown(fx); });
65
+
66
+ it('seeds the backup folder lazily on first session read and reports no drift on a clean tree', async () => {
67
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), '# Hello\n', 'utf-8');
68
+ const session = await fx.diffService.currentSession(fx.workspaceId);
69
+ // Nothing pending — disk == backup after seed.
70
+ expect(session).toBeNull();
71
+ // Backup file was written.
72
+ const backupPath = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'foo.md');
73
+ expect(await fs.readFile(backupPath, 'utf-8')).toBe('# Hello\n');
74
+ });
75
+
76
+ it('surfaces an agent-created untracked file as added (the juju.md regression)', async () => {
77
+ // First session call seeds with no diffable files.
78
+ await fx.diffService.currentSession(fx.workspaceId);
79
+ // Agent writes a new file directly to disk (bypassing WorkspaceService).
80
+ await fs.writeFile(path.join(fx.repoDir, 'juju.md'), '# new\n', 'utf-8');
81
+ const session = await fx.diffService.currentSession(fx.workspaceId);
82
+ expect(session?.changes.length).toBe(1);
83
+ expect(session!.changes[0]).toMatchObject({
84
+ path: 'knowledge-base/juju.md',
85
+ kind: 'added',
86
+ isBinary: false,
87
+ });
88
+ });
89
+
90
+ it('user-saved edit through WorkspaceService advances the backup so it does NOT show as pending', async () => {
91
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'before\n', 'utf-8');
92
+ await fx.diffService.currentSession(fx.workspaceId); // seed
93
+ // User edit goes through WorkspaceService → triggers diffService.syncFromDisk.
94
+ await fx.workspaceService.writeFile(fx.workspaceId, 'knowledge-base/foo.md', 'after\n');
95
+ const session = await fx.diffService.currentSession(fx.workspaceId);
96
+ expect(session).toBeNull(); // backup advanced; no drift
97
+ });
98
+
99
+ it('detects a modified file (agent edit on top of seeded backup)', async () => {
100
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'before\n', 'utf-8');
101
+ await fx.diffService.currentSession(fx.workspaceId); // seed
102
+ // Agent edits via shell — disk only.
103
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'after\n', 'utf-8');
104
+ const session = await fx.diffService.currentSession(fx.workspaceId);
105
+ expect(session?.changes[0]).toMatchObject({
106
+ path: 'knowledge-base/foo.md',
107
+ kind: 'modified',
108
+ linesAdded: 1,
109
+ linesRemoved: 1,
110
+ });
111
+ });
112
+
113
+ it('acceptOne promotes disk → backup so the next session is clean', async () => {
114
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
115
+ await fx.diffService.currentSession(fx.workspaceId);
116
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
117
+ expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(1);
118
+ await fx.diffService.acceptOne(fx.workspaceId, 'knowledge-base/foo.md');
119
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
120
+ });
121
+
122
+ it('acceptAll with an allow-list accepts only those paths in ONE batch; the rest stay pending', async () => {
123
+ // The accept route passes the caller's READABLE paths — a restricted node
124
+ // must stay pending. One service call covers the whole batch (looping
125
+ // acceptOne per path re-walks the workspace per file, O(changes × files)).
126
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
127
+ await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'v1\n', 'utf-8');
128
+ await fx.diffService.currentSession(fx.workspaceId); // seed
129
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
130
+ await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'v2\n', 'utf-8');
131
+ expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(2);
132
+ await fx.diffService.acceptAll(fx.workspaceId, ['knowledge-base/a.md']);
133
+ const session = await fx.diffService.currentSession(fx.workspaceId);
134
+ expect(session?.changes.map((c) => c.path)).toEqual(['knowledge-base/b.md']);
135
+ });
136
+
137
+ it('acceptAll without an allow-list accepts everything', async () => {
138
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
139
+ await fx.diffService.currentSession(fx.workspaceId); // seed
140
+ await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
141
+ await fx.diffService.acceptAll(fx.workspaceId);
142
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
143
+ });
144
+
145
+ it('rejectOne restores disk ← backup', async () => {
146
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
147
+ await fx.diffService.currentSession(fx.workspaceId);
148
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
149
+ await fx.diffService.rejectOne(fx.workspaceId, 'knowledge-base/foo.md');
150
+ expect(await fs.readFile(path.join(fx.repoDir, 'foo.md'), 'utf-8')).toBe('v1\n');
151
+ });
152
+
153
+ it('rejectOne on an agent-created file removes it from disk', async () => {
154
+ await fx.diffService.currentSession(fx.workspaceId); // seed (empty)
155
+ await fs.writeFile(path.join(fx.repoDir, 'created.md'), 'agent\n', 'utf-8');
156
+ await fx.diffService.rejectOne(fx.workspaceId, 'knowledge-base/created.md');
157
+ await expect(fs.access(path.join(fx.repoDir, 'created.md'))).rejects.toThrow();
158
+ });
159
+
160
+ it('non-diffable extensions never enter the pending list', async () => {
161
+ await fs.writeFile(path.join(fx.repoDir, 'image.png'), Buffer.from([0xff, 0xd8, 0xff]));
162
+ await fx.diffService.currentSession(fx.workspaceId);
163
+ // Agent writes another binary.
164
+ await fs.writeFile(path.join(fx.repoDir, 'other.png'), Buffer.from([0x89, 0x50, 0x4e]));
165
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
166
+ });
167
+
168
+ it('resetBackup wipes + reseeds (branch-switch hook): pending agent change disappears', async () => {
169
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
170
+ await fx.diffService.currentSession(fx.workspaceId);
171
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
172
+ expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(1);
173
+ await fx.diffService.resetBackup(fx.workspaceId);
174
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
175
+ });
176
+
177
+ it('rejects path-traversal attempts (must use a diffable extension to even reach the path check)', async () => {
178
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x', 'utf-8');
179
+ await fx.diffService.currentSession(fx.workspaceId);
180
+ await expect(
181
+ fx.diffService.acceptOne(fx.workspaceId, '../../../etc/passwd.md'),
182
+ ).rejects.toThrow(/Path traversal/);
183
+ });
184
+
185
+ it('reports kind=deleted when the user deletes a file via WorkspaceService — backup drops, no phantom', async () => {
186
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x', 'utf-8');
187
+ await fx.diffService.currentSession(fx.workspaceId); // seeds backup with foo.md
188
+ await fx.workspaceService.deleteFile(fx.workspaceId, 'knowledge-base/foo.md');
189
+ expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
190
+ });
191
+
192
+ it('agent-deleted file (disk-rm without WorkspaceService) shows as kind=deleted', async () => {
193
+ await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x\n', 'utf-8');
194
+ await fx.diffService.currentSession(fx.workspaceId); // seed
195
+ await fs.rm(path.join(fx.repoDir, 'foo.md'));
196
+ const session = await fx.diffService.currentSession(fx.workspaceId);
197
+ expect(session?.changes[0]).toMatchObject({
198
+ path: 'knowledge-base/foo.md',
199
+ kind: 'deleted',
200
+ });
201
+ });
202
+ });
@@ -0,0 +1,9 @@
1
+ import path from 'node:path';
2
+
3
+ export function assertWithinDirectory(absolutePath: string, dir: string): void {
4
+ const resolved = path.resolve(absolutePath);
5
+ const root = path.resolve(dir);
6
+ if (!resolved.startsWith(root + path.sep) && resolved !== root) {
7
+ throw new Error('Path traversal detected');
8
+ }
9
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * File extensions that participate in diff review.
3
+ * Only these extensions get backup copies under <backupsRoot>/<userId>/...
4
+ * and surface in the pending-changes list.
5
+ */
6
+ export const DIFFABLE_EXTENSIONS: ReadonlySet<string> = new Set(['.md']);
7
+
8
+ export function isDiffable(filePath: string): boolean {
9
+ const dot = filePath.lastIndexOf('.');
10
+ if (dot < 0) return false;
11
+ return DIFFABLE_EXTENSIONS.has(filePath.slice(dot).toLowerCase());
12
+ }
@@ -0,0 +1,78 @@
1
+ import type { FileDiffPayload, ReviewSession } from '@bevel-software/platform-shared';
2
+
3
+ /**
4
+ * Shape of the diff service. Backups live in a sibling root that mirrors the
5
+ * workspaces layout: each user/repo has `<backupsRoot>/<userId>/<repoName>/...`
6
+ * holding the "last user-confirmed state" of every diffable file. Drift between
7
+ * disk and backup is the pending-change set.
8
+ *
9
+ * Triggers that update the backup:
10
+ * - First-time mount (lazy via `currentSession`) seeds from disk.
11
+ * - User write/delete/rename through WorkspaceService → `syncFromDisk` /
12
+ * `markUserDeleted`.
13
+ * - User accept/reject of pending changes.
14
+ *
15
+ * Under the per-branch workspace model each branch has its own clone +
16
+ * its own backup folder, both seeded at bootstrap, so the legacy
17
+ * "branch switch wipes + reseeds the backup" trigger is gone. `resetBackup`
18
+ * + `resetBackupUnlocked` are still on the interface as primitives but
19
+ * have **no production callers** today — kept for potential future
20
+ * mutex-holding use cases.
21
+ *
22
+ * Agent edits, agent shell commits, and `git pull/merge/fetch/commit/push`
23
+ * leave the backup alone — the resulting drift is what the user reviews.
24
+ */
25
+ export interface IDiffService {
26
+ /** Wipe the backup folder and reseed from the current working tree. */
27
+ resetBackup(workspaceId: string): Promise<void>;
28
+
29
+ /**
30
+ * Same as `resetBackup` but assumes the caller is already holding the
31
+ * workspace mutex (via `WorkspaceMutex.run`). Originally the post-switch
32
+ * backup-reseed hook for `GitService.switchBranch`; that caller is gone
33
+ * with the per-branch workspace model (each branch has its own workspace
34
+ * + its own backup, seeded at bootstrap), so this currently has no
35
+ * production callers — kept in the interface as a primitive for future
36
+ * mutex-holding callers that need a backup reset.
37
+ */
38
+ resetBackupUnlocked(workspaceId: string): Promise<void>;
39
+
40
+ /** Pending changes for the current branch, or null when none. */
41
+ currentSession(workspaceId: string): Promise<ReviewSession | null>;
42
+
43
+ /** Diff payload (baseline + current) for one path. */
44
+ fileDiff(workspaceId: string, relativePath: string): Promise<FileDiffPayload>;
45
+
46
+ /** Promote disk → backup for `relativePath`. Handles file or directory. */
47
+ syncFromDisk(workspaceId: string, relativePath: string): Promise<void>;
48
+
49
+ /** Drop backup entry for `relativePath` (file or dir, recursive). */
50
+ markUserDeleted(workspaceId: string, relativePath: string): Promise<void>;
51
+
52
+ /** Accept one pending path: backup ← disk (or drop backup if disk-gone). */
53
+ acceptOne(workspaceId: string, relativePath: string): Promise<ReviewSession | null>;
54
+
55
+ /** Reject one pending path: disk ← backup (or rm disk if backup-gone). */
56
+ rejectOne(workspaceId: string, relativePath: string): Promise<ReviewSession | null>;
57
+
58
+ /**
59
+ * Compute the batch revert plan for `paths` WITHOUT mutating anything: each
60
+ * diffable path resolves to either a write of its pre-agent baseline bytes
61
+ * (backup exists) or a delete (agent-added file, no backup). Also returns
62
+ * the workspace dir so the caller can run the plan through a
63
+ * `LockingFilesystem.writeFiles` batch — one lock sweep, one commit + push.
64
+ * Non-diffable paths are skipped.
65
+ */
66
+ revertPlan(
67
+ workspaceId: string,
68
+ paths: string[],
69
+ ): Promise<{ workspaceDir: string; writes: { path: string; content: Buffer }[]; deletes: string[] }>;
70
+
71
+ /**
72
+ * Accept every pending path in ONE batch (single mutex hold, single
73
+ * pending-list walk). `paths` optionally restricts the batch to an
74
+ * allow-list — the accept route passes the caller's readable paths so
75
+ * restricted nodes stay pending.
76
+ */
77
+ acceptAll(workspaceId: string, paths?: string[]): Promise<void>;
78
+ }