@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,303 @@
1
+ import type { Server } from 'node:http';
2
+ import type { AddressInfo } from 'node:net';
3
+ import express from 'express';
4
+ import { describe, it, expect, afterEach, vi } from 'vitest';
5
+ import type { IWorkflowService } from '@bevel-software/platform-shared';
6
+ import type { IAccessControl } from '../../access/access-control.interface.js';
7
+ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
8
+ import type { AuthService } from '../../auth/auth.service.js';
9
+ import { AccessConfigError } from '../../access/access-errors.js';
10
+ import { createWorkspaceRoutes } from '../workspace.routes.js';
11
+ import type { ICreatorAccess } from '../../access/creator-access.js';
12
+ import { FolderTooLargeError, type WorkspaceService } from '../workspace.service.js';
13
+
14
+ const stubCreatorAccess: ICreatorAccess = {
15
+ planForCreate: async () => null,
16
+ grantInExtractedFile: async () => null,
17
+ noteAccessFileWritten: () => {},
18
+ };
19
+
20
+ /**
21
+ * Contract test for the `?download=1` flavours of the workspace routes:
22
+ *
23
+ * - GET /workspace/:id/file/raw?download=1 (single-file save)
24
+ * - GET /workspace/:id/folder/zip?download=1 (folder → zip save)
25
+ *
26
+ * Both are gated on the dedicated `Download` role in roles.yaml. Admin
27
+ * status is fully orthogonal — being in the `Admin` role does NOT
28
+ * implicitly grant Download; users must be listed under the `Download`
29
+ * role to receive bytes. The inline branch of /file/raw (no flag) stays
30
+ * open to every authenticated user so the PdfRenderer and image renderers
31
+ * keep working for everyone.
32
+ */
33
+
34
+ const USER_ID = 'user-1';
35
+ const USER = { id: USER_ID, email: 'alice@example.com', name: 'Alice' };
36
+ const WORKSPACE_ID = 'target-company-state';
37
+ const FILE_BYTES = Buffer.from('hello world');
38
+ const ZIP_BYTES = Buffer.from('PK\x03\x04 fake-zip-bytes');
39
+
40
+ interface Harness {
41
+ server: Server;
42
+ baseUrl: string;
43
+ canDownload: ReturnType<typeof vi.fn>;
44
+ canWrite: ReturnType<typeof vi.fn>;
45
+ createFolderZip: ReturnType<typeof vi.fn>;
46
+ }
47
+
48
+ async function makeHarness(opts: {
49
+ canDownload: boolean;
50
+ folderZip?: () => Promise<Buffer> | Buffer;
51
+ /** When set, canDownload rejects with this error instead of returning a bool. */
52
+ canDownloadError?: Error;
53
+ }): Promise<Harness> {
54
+ const canDownload = vi.fn(async () => {
55
+ if (opts.canDownloadError) throw opts.canDownloadError;
56
+ return opts.canDownload;
57
+ });
58
+ const canWrite = vi.fn();
59
+ const accessControl: IAccessControl = {
60
+ canWrite,
61
+ canWriteBatch: vi.fn(),
62
+ // The /file/raw route now read-gates before the download gate; these tests
63
+ // exercise the download verb, so reads pass.
64
+ canRead: vi.fn(async () => true),
65
+ canReadBatch: vi.fn(async (_w: string, _e: string, paths: string[]) => new Map(paths.map((p) => [p, true]))),
66
+ canDownload,
67
+ eligibleWriters: vi.fn(),
68
+ eligibleWriterEmails: vi.fn(),
69
+ invalidate: vi.fn(),
70
+ findEmailByHash: vi.fn(),
71
+ canWriteAtRef: vi.fn(),
72
+ canWriteBatchAtRef: vi.fn(),
73
+ eligibleWritersAtRef: vi.fn(),
74
+ eligibleWritersForPathsAtRef: vi.fn(),
75
+ } as unknown as IAccessControl;
76
+
77
+ const createFolderZip = vi.fn(async () => {
78
+ if (opts.folderZip) return opts.folderZip();
79
+ return ZIP_BYTES;
80
+ });
81
+
82
+ // Partial<WorkspaceService> + final cast keeps the contract typed at the
83
+ // call site (createWorkspaceRoutes wants a full WorkspaceService) while
84
+ // letting us stub only the two methods these tests exercise.
85
+ const workspaceServiceMock: Partial<WorkspaceService> = {
86
+ readFileBinary: vi.fn(async () => FILE_BYTES),
87
+ createFolderZip,
88
+ };
89
+ const workspaceService = workspaceServiceMock as WorkspaceService;
90
+
91
+ const authServiceMock: Partial<AuthService> = {
92
+ getUserById: vi.fn(async () => USER),
93
+ };
94
+ const authService = authServiceMock as AuthService;
95
+
96
+ const workflowService = {} as unknown as IWorkflowService;
97
+ const eventBus = {} as unknown as WorkflowEventBus;
98
+
99
+ const app = express();
100
+ app.use(express.json());
101
+ app.use('/api', (req, _res, next) => {
102
+ (req as any).userId = USER_ID;
103
+ next();
104
+ });
105
+ app.use('/api', createWorkspaceRoutes(
106
+ workspaceService,
107
+ authService,
108
+ workflowService,
109
+ eventBus,
110
+ accessControl,
111
+ 'knowledge-base',
112
+ stubCreatorAccess,
113
+ ));
114
+
115
+ const server = await new Promise<Server>((resolve) => {
116
+ const s = app.listen(0, () => resolve(s));
117
+ });
118
+ // `server.address()` returns `AddressInfo | string | null`. The string
119
+ // branch is for Unix-domain sockets and the null branch for an
120
+ // unbound listener — neither can happen here (we awaited `listen(0,
121
+ // ...)` on a TCP port), so a narrow + throw is sufficient and avoids
122
+ // the `as any` lint.
123
+ const addr = server.address();
124
+ if (addr === null || typeof addr === 'string') {
125
+ throw new Error(`Unexpected server.address() shape: ${JSON.stringify(addr)}`);
126
+ }
127
+ const port = (addr as AddressInfo).port;
128
+ return { server, baseUrl: `http://127.0.0.1:${port}`, canDownload, canWrite, createFolderZip };
129
+ }
130
+
131
+ async function closeServer(server: Server): Promise<void> {
132
+ await new Promise<void>((resolve, reject) =>
133
+ server.close((err) => (err ? reject(err) : resolve())),
134
+ );
135
+ }
136
+
137
+ describe('GET /workspace/:id/file/raw — ?download=1 gated on Download role', () => {
138
+ let h: Harness | null = null;
139
+ afterEach(async () => { if (h) await closeServer(h.server); h = null; });
140
+
141
+ it('user with Download role gets 200 + attachment disposition + bytes', async () => {
142
+ h = await makeHarness({ canDownload: true });
143
+ const res = await fetch(
144
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
145
+ );
146
+ expect(res.status).toBe(200);
147
+ const dispo = res.headers.get('content-disposition');
148
+ expect(dispo).toBeTruthy();
149
+ expect(dispo).toContain('attachment');
150
+ expect(dispo).toContain(`filename*=UTF-8''${encodeURIComponent('Foo.md')}`);
151
+ expect(res.headers.get('x-content-type-options')).toBe('nosniff');
152
+ const buf = Buffer.from(await res.arrayBuffer());
153
+ expect(buf.equals(FILE_BYTES)).toBe(true);
154
+ // canDownload is path-scoped now; the route strips the kbDirName prefix
155
+ // before consulting the access service (mirrors useFileAccess on the
156
+ // frontend), so the resolver sees the repo-relative path.
157
+ expect(h.canDownload).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, 'Knowledge/Foo.md');
158
+ // canWrite must NOT be consulted by the download gate — the two verbs
159
+ // are independent.
160
+ expect(h.canWrite).not.toHaveBeenCalled();
161
+ });
162
+
163
+ it('user without Download role gets 403 with no body bytes', async () => {
164
+ h = await makeHarness({ canDownload: false });
165
+ const res = await fetch(
166
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
167
+ );
168
+ expect(res.status).toBe(403);
169
+ const body = (await res.json()) as { error: string };
170
+ expect(body.error).toBe('Download permission required');
171
+ expect(res.headers.get('content-disposition')).toBeNull();
172
+ });
173
+
174
+ it('inline view (no ?download flag) returns 200 for any authed user without consulting access-control', async () => {
175
+ // Even a user with no download permission must be able to view PDFs /
176
+ // images inline — that's the PdfRenderer happy path. The route must not
177
+ // call canDownload at all on this branch.
178
+ h = await makeHarness({ canDownload: false });
179
+ const res = await fetch(
180
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}`,
181
+ );
182
+ expect(res.status).toBe(200);
183
+ expect(res.headers.get('content-disposition')).toBeNull();
184
+ expect(h.canDownload).not.toHaveBeenCalled();
185
+ });
186
+
187
+ it('returns 500 with AccessConfigError payload when roles.yaml / access.md fails to load', async () => {
188
+ // Regression guard: if `canDownload` throws inside requireDownloadPermission
189
+ // (e.g. roles.yaml is missing or the access tree is unrecoverably
190
+ // malformed) and we don't catch it, the async rejection bubbles past
191
+ // Express and the request hangs (or 500s without the helpful
192
+ // config-error payload). The route should surface the
193
+ // WorkflowDomainError shape — same JSON the per-path /access endpoint
194
+ // returns when it hits the same exception — so the operator can see
195
+ // which access.md file is broken.
196
+ const configErr = new AccessConfigError([
197
+ "Knowledge/Sales/access.md: unknown role 'Product Manager'",
198
+ ]);
199
+ h = await makeHarness({ canDownload: true, canDownloadError: configErr });
200
+ const res = await fetch(
201
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
202
+ );
203
+ expect(res.status).toBe(500);
204
+ const body = (await res.json()) as { error: string; accessConfigErrors?: string[] };
205
+ expect(body.error).toContain('Access-control config is invalid');
206
+ expect(body.accessConfigErrors?.[0]).toContain("unknown role 'Product Manager'");
207
+ });
208
+
209
+ it('forces SVG to octet-stream when downloading (anti-XSS), preserves image/svg+xml inline', async () => {
210
+ h = await makeHarness({ canDownload: true });
211
+ const dl = await fetch(
212
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('icon.svg')}&download=1`,
213
+ );
214
+ expect(dl.headers.get('content-type')).toBe('application/octet-stream');
215
+
216
+ await closeServer(h.server);
217
+ h = await makeHarness({ canDownload: false });
218
+ const inline = await fetch(
219
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('icon.svg')}`,
220
+ );
221
+ expect(inline.headers.get('content-type')).toBe('image/svg+xml');
222
+ });
223
+ });
224
+
225
+ describe('GET /workspace/:id/folder/zip — gated on Download role', () => {
226
+ let h: Harness | null = null;
227
+ afterEach(async () => { if (h) await closeServer(h.server); h = null; });
228
+
229
+ it('user with Download role gets a 200 zip with attachment disposition', async () => {
230
+ h = await makeHarness({ canDownload: true });
231
+ const res = await fetch(
232
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}&download=1`,
233
+ );
234
+ expect(res.status).toBe(200);
235
+ expect(res.headers.get('content-type')).toBe('application/zip');
236
+ expect(res.headers.get('x-content-type-options')).toBe('nosniff');
237
+ const dispo = res.headers.get('content-disposition');
238
+ expect(dispo).toBeTruthy();
239
+ expect(dispo).toContain('attachment');
240
+ expect(dispo).toContain(`filename*=UTF-8''${encodeURIComponent('Sales.zip')}`);
241
+ const buf = Buffer.from(await res.arrayBuffer());
242
+ expect(buf.equals(ZIP_BYTES)).toBe(true);
243
+ expect(h.canDownload).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, 'Knowledge/Sales');
244
+ expect(h.createFolderZip).toHaveBeenCalledWith(WORKSPACE_ID, 'Knowledge/Sales');
245
+ });
246
+
247
+ it('user without Download role gets 403 and no zip is built', async () => {
248
+ h = await makeHarness({ canDownload: false });
249
+ const res = await fetch(
250
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}&download=1`,
251
+ );
252
+ expect(res.status).toBe(403);
253
+ const body = (await res.json()) as { error: string };
254
+ expect(body.error).toBe('Download permission required');
255
+ expect(h.createFolderZip).not.toHaveBeenCalled();
256
+ });
257
+
258
+ it('requires ?download=1 — a stray inline call is rejected with 400', async () => {
259
+ // Folder zip is download-only; refuse the inline shape so the URL contract
260
+ // matches the file route and there's no ambiguity about what should happen.
261
+ h = await makeHarness({ canDownload: true });
262
+ const res = await fetch(
263
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}`,
264
+ );
265
+ expect(res.status).toBe(400);
266
+ expect(h.createFolderZip).not.toHaveBeenCalled();
267
+ });
268
+
269
+ it('maps FolderTooLargeError to 413 with the limit-bearing message', async () => {
270
+ h = await makeHarness({
271
+ canDownload: true,
272
+ folderZip: async () => { throw new FolderTooLargeError(500 * 1024 * 1024); },
273
+ });
274
+ const res = await fetch(
275
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Big')}&download=1`,
276
+ );
277
+ expect(res.status).toBe(413);
278
+ const body = (await res.json()) as { error: string };
279
+ expect(body.error).toContain('524288000');
280
+ });
281
+
282
+ it('maps Path traversal detected to 403', async () => {
283
+ h = await makeHarness({
284
+ canDownload: true,
285
+ folderZip: async () => { throw new Error('Path traversal detected'); },
286
+ });
287
+ const res = await fetch(
288
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('../escape')}&download=1`,
289
+ );
290
+ expect(res.status).toBe(403);
291
+ });
292
+
293
+ it('maps Not a directory to 400 (user pointed at a file)', async () => {
294
+ h = await makeHarness({
295
+ canDownload: true,
296
+ folderZip: async () => { throw new Error('Not a directory'); },
297
+ });
298
+ const res = await fetch(
299
+ `${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
300
+ );
301
+ expect(res.status).toBe(400);
302
+ });
303
+ });
@@ -0,0 +1,276 @@
1
+ import type { Server } from 'node:http';
2
+ import type { AddressInfo } from 'node:net';
3
+ import express from 'express';
4
+ import { describe, it, expect, afterEach, vi } from 'vitest';
5
+ import type { IWorkflowService } from '@bevel-software/platform-shared';
6
+ import type { IAccessControl } from '../../access/access-control.interface.js';
7
+ import type { ICreatorAccess } from '../../access/creator-access.js';
8
+ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
9
+ import type { AuthService } from '../../auth/auth.service.js';
10
+ import { createWorkspaceRoutes } from '../workspace.routes.js';
11
+ import type { WorkspaceService, ReadTreeFilter } from '../workspace.service.js';
12
+
13
+ /**
14
+ * Contract test for the read-permission gates on the human read routes:
15
+ * GET /file, /file/raw (inline), /resolve-id, /kb-files, /files.
16
+ * `allow(path)` decides each verdict; by convention any path containing
17
+ * "Secret" is denied. The harness wires the real route layer over mocked
18
+ * access + workspace services. (The /graph + /resolve-id routes live in the
19
+ * enterprise kb-graph module; their gate tests moved with them.)
20
+ */
21
+
22
+ const USER_ID = 'user-1';
23
+ const USER = { id: USER_ID, email: 'alice@example.com', name: 'Alice' };
24
+ const WS = 'target-company-state';
25
+ const KB = 'knowledge-base';
26
+
27
+ const allow = (p: string) => !p.includes('Secret');
28
+
29
+ const stubCreatorAccess: ICreatorAccess = {
30
+ planForCreate: async () => null,
31
+ grantInExtractedFile: async () => null,
32
+ noteAccessFileWritten: () => {},
33
+ };
34
+
35
+
36
+ interface Harness {
37
+ server: Server;
38
+ baseUrl: string;
39
+ canRead: ReturnType<typeof vi.fn>;
40
+ canReadBatchFull: ReturnType<typeof vi.fn>;
41
+ listFilesFilter: () => ReadTreeFilter | undefined;
42
+ }
43
+
44
+ async function makeHarness(opts: {
45
+ kbFiles?: Record<string, string>;
46
+ allowFn?: (p: string) => boolean;
47
+ } = {}): Promise<Harness> {
48
+ const allowP = opts.allowFn ?? allow;
49
+ const canRead = vi.fn(async (_w: string, _e: string, p: string) => allowP(p));
50
+ const batch = async (_w: string, _e: string, paths: string[]) =>
51
+ new Map(paths.map((p) => [p, allowP(p)]));
52
+ const canReadBatchFull = vi.fn(batch);
53
+ const accessControl = {
54
+ canRead,
55
+ canReadBatch: canReadBatchFull,
56
+ } as unknown as IAccessControl;
57
+
58
+ let captured: ReadTreeFilter | undefined;
59
+ const workspaceService = {
60
+ getOrCreateForBranch: vi.fn(async (branch: string) => ({
61
+ id: branch,
62
+ name: branch,
63
+ absolutePath: `/tmp/${branch}`,
64
+ createdAt: new Date(0).toISOString(),
65
+ kbDirName: KB,
66
+ })),
67
+ readFile: vi.fn(async () => 'CONTENT'),
68
+ readFileBinary: vi.fn(async () => Buffer.from('BYTES')),
69
+ readAllKbFiles: vi.fn(async () => opts.kbFiles ?? {}),
70
+ listFiles: vi.fn(async (_id: string, filter?: ReadTreeFilter) => {
71
+ captured = filter;
72
+ return { name: 'root', relativePath: '.', type: 'directory', children: [] };
73
+ }),
74
+ } as unknown as WorkspaceService;
75
+
76
+ const authService = { getUserById: vi.fn(async () => USER) } as unknown as AuthService;
77
+
78
+ const app = express();
79
+ app.use(express.json());
80
+ app.use('/api', (req, _res, next) => {
81
+ (req as unknown as { userId: string }).userId = USER_ID;
82
+ next();
83
+ });
84
+ app.use(
85
+ '/api',
86
+ createWorkspaceRoutes(
87
+ workspaceService,
88
+ authService,
89
+ {} as unknown as IWorkflowService,
90
+ {} as unknown as WorkflowEventBus,
91
+ accessControl,
92
+ KB,
93
+ stubCreatorAccess,
94
+ ),
95
+ );
96
+ const server = await new Promise<Server>((resolve) => {
97
+ const s = app.listen(0, () => resolve(s));
98
+ });
99
+ const addr = server.address() as AddressInfo;
100
+ return {
101
+ server,
102
+ baseUrl: `http://127.0.0.1:${addr.port}`,
103
+ canRead,
104
+ canReadBatchFull,
105
+ listFilesFilter: () => captured,
106
+ };
107
+ }
108
+
109
+ function close(s: Server): Promise<void> {
110
+ return new Promise((resolve, reject) => s.close((e) => (e ? reject(e) : resolve())));
111
+ }
112
+
113
+ describe('read-permission gates on human read routes', () => {
114
+ let h: Harness | null = null;
115
+ afterEach(async () => { if (h) await close(h.server); h = null; });
116
+
117
+ const get = (path: string) => fetch(`${h!.baseUrl}/api/workspace/${WS}${path}`);
118
+
119
+ it('GET /file: readable → 200, denied → 403', async () => {
120
+ h = await makeHarness();
121
+ // Paths are workspace-relative (kbDir-prefixed), as the file tree produces
122
+ // and `readFile` resolves them.
123
+ const ok = await get(`/file?path=${encodeURIComponent(`${KB}/Knowledge/Open/a.md`)}`);
124
+ expect(ok.status).toBe(200);
125
+ expect((await ok.json()).content).toBe('CONTENT');
126
+
127
+ const denied = await get(`/file?path=${encodeURIComponent(`${KB}/Knowledge/Secret/x.md`)}`);
128
+ expect(denied.status).toBe(403);
129
+ // canRead consulted with the KB-repo-relative path (kbDir prefix stripped).
130
+ expect(h.canRead).toHaveBeenCalledWith(WS, USER.email, 'Knowledge/Secret/x.md');
131
+ });
132
+
133
+ it('GET /file: a non-KB path (reserved workspace file) is not read-gated', async () => {
134
+ h = await makeHarness();
135
+ // Outside `kbDirName` → carries no read rules → served without a canRead
136
+ // check (matches the agent tools and diff routes). Previously this 403'd
137
+ // because the full path was passed to default-deny canRead.
138
+ const res = await get(`/file?path=${encodeURIComponent('reserved-config.json')}`);
139
+ expect(res.status).toBe(200);
140
+ expect(h.canRead).not.toHaveBeenCalled();
141
+ });
142
+
143
+ it('GET /file/raw (inline, no download) is read-gated', async () => {
144
+ h = await makeHarness();
145
+ const denied = await get(`/file/raw?path=${encodeURIComponent(`${KB}/Secret/x.png`)}`);
146
+ expect(denied.status).toBe(403);
147
+ const ok = await get(`/file/raw?path=${encodeURIComponent(`${KB}/Open/a.png`)}`);
148
+ expect(ok.status).toBe(200);
149
+ });
150
+
151
+ // NOTE (core split): the `GET /resolve-id` and `GET /graph` read-gate tests
152
+ // moved with the kb-graph module (enterprise) — those routes are not part of
153
+ // the core workspace surface.
154
+
155
+ it('GET /kb-files drops unreadable nodes from the graph payload', async () => {
156
+ h = await makeHarness({ kbFiles: { 'Knowledge/Open/a.md': 'A', 'Knowledge/Secret/x.md': 'S' } });
157
+ const res = await get('/kb-files');
158
+ expect(res.status).toBe(200);
159
+ const { files } = await res.json();
160
+ expect(files['Knowledge/Open/a.md']).toBe('A');
161
+ expect(files['Knowledge/Secret/x.md']).toBeUndefined();
162
+ });
163
+
164
+ it('GET /files passes a working shallow read filter to listFiles', async () => {
165
+ h = await makeHarness();
166
+ expect((await get('/files')).status).toBe(200);
167
+ const filter = h.listFilesFilter();
168
+ expect(typeof filter).toBe('function');
169
+ // The filter maps workspace-relative paths (kbDir-prefixed) → verdicts,
170
+ // stripping the kbDir and consulting the full canReadBatch.
171
+ const verdict = await filter!([
172
+ `${KB}/Knowledge/Open/a.md`,
173
+ `${KB}/Knowledge/Secret/x.md`,
174
+ ]);
175
+ expect(verdict.get(`${KB}/Knowledge/Open/a.md`)).toBe(true);
176
+ expect(verdict.get(`${KB}/Knowledge/Secret/x.md`)).toBe(false);
177
+ });
178
+
179
+ it('keeps the structural root folders visible even when nothing is readable', async () => {
180
+ // A user with no read grants: the access batch denies everything. The
181
+ // structural top-level folders (KnowledgeBase, Data, Agents, Pipelines,
182
+ // Skills, Tools) must still resolve readable (so the explorer renders its
183
+ // section view), while their contents stay gated.
184
+ h = await makeHarness({ allowFn: () => false });
185
+ expect((await get('/files')).status).toBe(200);
186
+ const filter = h.listFilesFilter();
187
+ const verdict = await filter!([
188
+ `${KB}/KnowledgeBase`,
189
+ `${KB}/Data`,
190
+ `${KB}/Agents`,
191
+ `${KB}/Pipelines`,
192
+ `${KB}/Skills`,
193
+ `${KB}/Tools`,
194
+ `${KB}/KnowledgeBase/Open/a.md`,
195
+ `${KB}/Data/Ops/Knowledge/item.md`,
196
+ `${KB}/Skills/rfi/SKILL.md`,
197
+ ]);
198
+ // Folders themselves: forced visible.
199
+ expect(verdict.get(`${KB}/KnowledgeBase`)).toBe(true);
200
+ expect(verdict.get(`${KB}/Data`)).toBe(true);
201
+ expect(verdict.get(`${KB}/Agents`)).toBe(true);
202
+ expect(verdict.get(`${KB}/Pipelines`)).toBe(true);
203
+ expect(verdict.get(`${KB}/Skills`)).toBe(true);
204
+ expect(verdict.get(`${KB}/Tools`)).toBe(true);
205
+ // Their contents: still gated (the override matches only the exact folder).
206
+ expect(verdict.get(`${KB}/KnowledgeBase/Open/a.md`)).toBe(false);
207
+ expect(verdict.get(`${KB}/Data/Ops/Knowledge/item.md`)).toBe(false);
208
+ expect(verdict.get(`${KB}/Skills/rfi/SKILL.md`)).toBe(false);
209
+ });
210
+
211
+ it('resolves every entry with the FULL read batch (frontmatter grants AND denies honoured)', async () => {
212
+ // The tree filter consults the full canReadBatch for ALL paths in one
213
+ // call — a per-file frontmatter grant (e.g. the creator read grant a
214
+ // loose root-level .md gets on creation) makes the file visible, and a
215
+ // frontmatter deny hides it, exactly matching what the content route
216
+ // will enforce on open.
217
+ h = await makeHarness({ allowFn: () => false });
218
+ const full = h.canReadBatchFull;
219
+ full.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
220
+ new Map(paths.map((p) => [p, p === 'KnowledgeBase/mine.md'])));
221
+ expect((await get('/files')).status).toBe(200);
222
+ const filter = h.listFilesFilter();
223
+ const verdict = await filter!([
224
+ `${KB}/KnowledgeBase/mine.md`, // frontmatter grant → visible
225
+ `${KB}/KnowledgeBase/other.md`, // no grant → hidden
226
+ `${KB}/KnowledgeBase/Secret/deep.md`, // no grant → hidden
227
+ ]);
228
+ expect(verdict.get(`${KB}/KnowledgeBase/mine.md`)).toBe(true);
229
+ expect(verdict.get(`${KB}/KnowledgeBase/other.md`)).toBe(false);
230
+ expect(verdict.get(`${KB}/KnowledgeBase/Secret/deep.md`)).toBe(false);
231
+ // ONE batched call covers every path — no per-file fan-out.
232
+ expect(full).toHaveBeenCalledTimes(1);
233
+ expect(full.mock.calls[0][2]).toEqual([
234
+ 'KnowledgeBase/mine.md',
235
+ 'KnowledgeBase/other.md',
236
+ 'KnowledgeBase/Secret/deep.md',
237
+ ]);
238
+ });
239
+
240
+ it('legacy layout: root-level Knowledge/ is forced visible and its loose files get the full check', async () => {
241
+ // Clones that predate the KnowledgeBase/Skills split keep their knowledge
242
+ // root at `Knowledge/` (repo root). It must get the same structural-root
243
+ // treatment: the folder itself stays visible, and a loose file directly
244
+ // under it (whose creator grant lives in frontmatter) is re-checked with
245
+ // the FULL batch.
246
+ h = await makeHarness({ allowFn: () => false });
247
+ h.canReadBatchFull.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
248
+ new Map(paths.map((p) => [p, p === 'Knowledge/mine.md'])));
249
+ expect((await get('/files')).status).toBe(200);
250
+ const filter = h.listFilesFilter();
251
+ const verdict = await filter!([
252
+ `${KB}/Knowledge`,
253
+ `${KB}/Knowledge/mine.md`,
254
+ `${KB}/Knowledge/other.md`,
255
+ `${KB}/Knowledge/Secret/deep.md`,
256
+ ]);
257
+ expect(verdict.get(`${KB}/Knowledge`)).toBe(true);
258
+ expect(verdict.get(`${KB}/Knowledge/mine.md`)).toBe(true);
259
+ expect(verdict.get(`${KB}/Knowledge/other.md`)).toBe(false);
260
+ expect(verdict.get(`${KB}/Knowledge/Secret/deep.md`)).toBe(false);
261
+ });
262
+
263
+ it('GET /workspace bootstrap also returns a read-filtered file tree', async () => {
264
+ h = await makeHarness();
265
+ const res = await fetch(`${h.baseUrl}/api/workspace?branch=${encodeURIComponent(WS)}`);
266
+ expect(res.status).toBe(200);
267
+ const filter = h.listFilesFilter();
268
+ expect(typeof filter).toBe('function');
269
+ const verdict = await filter!([
270
+ `${KB}/Knowledge/Open/a.md`,
271
+ `${KB}/Knowledge/Secret/x.md`,
272
+ ]);
273
+ expect(verdict.get(`${KB}/Knowledge/Open/a.md`)).toBe(true);
274
+ expect(verdict.get(`${KB}/Knowledge/Secret/x.md`)).toBe(false);
275
+ });
276
+ });