@bevel-software/platform-core-backend 0.33.0 → 0.35.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 (602) hide show
  1. package/agent-guide/access-control.md +146 -289
  2. package/agent-guide/conventions.md +23 -25
  3. package/agent-guide/directory-structure.md +72 -120
  4. package/agent-guide/drafts-and-change-requests.md +92 -0
  5. package/agent-guide/for-git-users.md +47 -0
  6. package/agent-guide/glossary.md +36 -0
  7. package/agent-guide/how-agents-find-things.md +49 -0
  8. package/agent-guide/html-views.md +239 -8
  9. package/agent-guide/new-knowledge-base.md +9 -8
  10. package/agent-guide/skills.md +67 -42
  11. package/agent-guide/start-here.md +65 -0
  12. package/agent-guide/tool-manuals.md +133 -155
  13. package/agent-guide/tools.md +67 -0
  14. package/agent-guide/where-a-new-file-goes.md +45 -28
  15. package/dist/core/catalog-revision.d.ts +12 -1
  16. package/dist/core/catalog-revision.d.ts.map +1 -1
  17. package/dist/core/catalog-revision.js +23 -3
  18. package/dist/core/catalog-revision.js.map +1 -1
  19. package/dist/core/core-ports.d.ts +41 -0
  20. package/dist/core/core-ports.d.ts.map +1 -1
  21. package/dist/core/core-ports.js.map +1 -1
  22. package/dist/core/create-core-server.d.ts.map +1 -1
  23. package/dist/core/create-core-server.js +15 -8
  24. package/dist/core/create-core-server.js.map +1 -1
  25. package/dist/core/create-core-services.d.ts +22 -5
  26. package/dist/core/create-core-services.d.ts.map +1 -1
  27. package/dist/core/create-core-services.js +44 -16
  28. package/dist/core/create-core-services.js.map +1 -1
  29. package/dist/core/lifecycle.d.ts +16 -0
  30. package/dist/core/lifecycle.d.ts.map +1 -1
  31. package/dist/core/lifecycle.js +12 -2
  32. package/dist/core/lifecycle.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/modules/access/access-control.interface.d.ts +39 -9
  37. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  38. package/dist/modules/access/access-control.service.d.ts +15 -3
  39. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.js +239 -75
  41. package/dist/modules/access/access-control.service.js.map +1 -1
  42. package/dist/modules/access/access-mutation.service.d.ts +22 -24
  43. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  44. package/dist/modules/access/access-mutation.service.js +48 -51
  45. package/dist/modules/access/access-mutation.service.js.map +1 -1
  46. package/dist/modules/access/access-view.d.ts.map +1 -1
  47. package/dist/modules/access/access-view.js +12 -11
  48. package/dist/modules/access/access-view.js.map +1 -1
  49. package/dist/modules/access/access.routes.d.ts +3 -0
  50. package/dist/modules/access/access.routes.d.ts.map +1 -1
  51. package/dist/modules/access/access.routes.js +58 -33
  52. package/dist/modules/access/access.routes.js.map +1 -1
  53. package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
  54. package/dist/modules/access/groups-admin.routes.js +4 -2
  55. package/dist/modules/access/groups-admin.routes.js.map +1 -1
  56. package/dist/modules/access/groups-admin.service.d.ts +44 -13
  57. package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
  58. package/dist/modules/access/groups-admin.service.js +80 -38
  59. package/dist/modules/access/groups-admin.service.js.map +1 -1
  60. package/dist/modules/access/groups-edit.d.ts +1 -1
  61. package/dist/modules/access/groups-edit.d.ts.map +1 -1
  62. package/dist/modules/access/groups-edit.js +1 -1
  63. package/dist/modules/access/groups-edit.js.map +1 -1
  64. package/dist/modules/access/index.d.ts +1 -1
  65. package/dist/modules/access/index.d.ts.map +1 -1
  66. package/dist/modules/access/reference-scan.d.ts +116 -24
  67. package/dist/modules/access/reference-scan.d.ts.map +1 -1
  68. package/dist/modules/access/reference-scan.js +305 -44
  69. package/dist/modules/access/reference-scan.js.map +1 -1
  70. package/dist/modules/access/roles-admin.service.d.ts +17 -8
  71. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  72. package/dist/modules/access/roles-admin.service.js +32 -23
  73. package/dist/modules/access/roles-admin.service.js.map +1 -1
  74. package/dist/modules/access-model/access-grammar.d.ts +35 -10
  75. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  76. package/dist/modules/access-model/access-grammar.js +60 -20
  77. package/dist/modules/access-model/access-grammar.js.map +1 -1
  78. package/dist/modules/access-model/access-splice.d.ts +22 -18
  79. package/dist/modules/access-model/access-splice.d.ts.map +1 -1
  80. package/dist/modules/access-model/access-splice.js +20 -14
  81. package/dist/modules/access-model/access-splice.js.map +1 -1
  82. package/dist/modules/access-model/group-files.d.ts +1 -0
  83. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  84. package/dist/modules/access-model/group-files.js +5 -1
  85. package/dist/modules/access-model/group-files.js.map +1 -1
  86. package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
  87. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  88. package/dist/modules/agent-guide/agent-guide.js +103 -13
  89. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  90. package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
  91. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
  92. package/dist/modules/agent-guide/agent-guide.tools.js +46 -22
  93. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
  94. package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
  95. package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
  96. package/dist/modules/agent-guide/guide-budget.js +67 -0
  97. package/dist/modules/agent-guide/guide-budget.js.map +1 -0
  98. package/dist/modules/agent-guide/index.d.ts +2 -1
  99. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  100. package/dist/modules/agent-guide/index.js +2 -1
  101. package/dist/modules/agent-guide/index.js.map +1 -1
  102. package/dist/modules/agent-instructions/compose.d.ts +3 -1
  103. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  104. package/dist/modules/agent-instructions/compose.js +7 -1
  105. package/dist/modules/agent-instructions/compose.js.map +1 -1
  106. package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
  107. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  108. package/dist/modules/agent-instructions/shared-file-rules.js +26 -9
  109. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  110. package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
  111. package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
  112. package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
  113. package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
  114. package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
  115. package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
  116. package/dist/modules/agent-scope/connection-scope.js +165 -0
  117. package/dist/modules/agent-scope/connection-scope.js.map +1 -0
  118. package/dist/modules/agent-scope/index.d.ts +3 -0
  119. package/dist/modules/agent-scope/index.d.ts.map +1 -0
  120. package/dist/modules/agent-scope/index.js +3 -0
  121. package/dist/modules/agent-scope/index.js.map +1 -0
  122. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  123. package/dist/modules/audit/agent-audit.service.js +2 -0
  124. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  125. package/dist/modules/audit/audit.contract.d.ts +6 -0
  126. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  127. package/dist/modules/audit/audit.contract.js.map +1 -1
  128. package/dist/modules/auth/account-admission.d.ts +12 -0
  129. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  130. package/dist/modules/auth/account-admission.js +15 -0
  131. package/dist/modules/auth/account-admission.js.map +1 -1
  132. package/dist/modules/auth/auth.middleware.d.ts +27 -1
  133. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  134. package/dist/modules/auth/auth.middleware.js +42 -4
  135. package/dist/modules/auth/auth.middleware.js.map +1 -1
  136. package/dist/modules/auth/auth.routes.d.ts +13 -9
  137. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  138. package/dist/modules/auth/auth.routes.js +35 -29
  139. package/dist/modules/auth/auth.routes.js.map +1 -1
  140. package/dist/modules/auth/auth.service.d.ts +55 -6
  141. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  142. package/dist/modules/auth/auth.service.js +128 -34
  143. package/dist/modules/auth/auth.service.js.map +1 -1
  144. package/dist/modules/auth/index.d.ts +1 -1
  145. package/dist/modules/auth/index.d.ts.map +1 -1
  146. package/dist/modules/auth/index.js +1 -1
  147. package/dist/modules/auth/index.js.map +1 -1
  148. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  149. package/dist/modules/auth/oidc-auth-provider.js +2 -9
  150. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  151. package/dist/modules/code-mode/code-mode.tool.js +2 -2
  152. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  153. package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
  154. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  155. package/dist/modules/connection-probe/connection-probe.service.js +1 -38
  156. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  157. package/dist/modules/database/core-schema.d.ts +68 -0
  158. package/dist/modules/database/core-schema.d.ts.map +1 -1
  159. package/dist/modules/database/core-schema.js +33 -0
  160. package/dist/modules/database/core-schema.js.map +1 -1
  161. package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
  162. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  163. package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
  164. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  165. package/dist/modules/embed/embed.tools.d.ts.map +1 -1
  166. package/dist/modules/embed/embed.tools.js +5 -0
  167. package/dist/modules/embed/embed.tools.js.map +1 -1
  168. package/dist/modules/kb-fs/index.d.ts +2 -0
  169. package/dist/modules/kb-fs/index.d.ts.map +1 -1
  170. package/dist/modules/kb-fs/index.js +1 -0
  171. package/dist/modules/kb-fs/index.js.map +1 -1
  172. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  173. package/dist/modules/kb-fs/locking-filesystem.js +7 -1
  174. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  175. package/dist/modules/kb-fs/repo-path.d.ts +26 -6
  176. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  177. package/dist/modules/kb-fs/repo-path.js +22 -7
  178. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  179. package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
  180. package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
  181. package/dist/modules/kb-fs/tmp-ref.js +76 -0
  182. package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
  183. package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
  184. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  185. package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
  186. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  187. package/dist/modules/mcp/mcp.routes.d.ts +7 -1
  188. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  189. package/dist/modules/mcp/mcp.routes.js +39 -4
  190. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  191. package/dist/modules/mcp/mcp.service.d.ts +11 -0
  192. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  193. package/dist/modules/mcp/mcp.service.js +74 -48
  194. package/dist/modules/mcp/mcp.service.js.map +1 -1
  195. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
  196. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  197. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
  198. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  199. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
  200. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  201. package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
  202. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  203. package/dist/modules/plugins/index.d.ts +1 -1
  204. package/dist/modules/plugins/index.d.ts.map +1 -1
  205. package/dist/modules/plugins/index.js.map +1 -1
  206. package/dist/modules/plugins/plugin-links.d.ts +18 -2
  207. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  208. package/dist/modules/plugins/plugin-links.js.map +1 -1
  209. package/dist/modules/plugins/plugins.routes.d.ts +5 -1
  210. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  211. package/dist/modules/plugins/plugins.routes.js +32 -5
  212. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  213. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
  214. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  215. package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
  216. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  217. package/dist/modules/secrets-vault/index.d.ts +1 -1
  218. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  219. package/dist/modules/secrets-vault/index.js +1 -1
  220. package/dist/modules/secrets-vault/index.js.map +1 -1
  221. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
  222. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  223. package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
  224. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  225. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
  226. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  227. package/dist/modules/secrets-vault/secrets-vault.routes.js +100 -42
  228. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  229. package/dist/modules/settings/oidc-check.d.ts.map +1 -1
  230. package/dist/modules/settings/oidc-check.js +5 -17
  231. package/dist/modules/settings/oidc-check.js.map +1 -1
  232. package/dist/modules/skills/index.d.ts +3 -3
  233. package/dist/modules/skills/index.d.ts.map +1 -1
  234. package/dist/modules/skills/index.js +0 -2
  235. package/dist/modules/skills/index.js.map +1 -1
  236. package/dist/modules/skills/skills.contract.d.ts +16 -0
  237. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  238. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  239. package/dist/modules/skills/skills.service.js +6 -1
  240. package/dist/modules/skills/skills.service.js.map +1 -1
  241. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  242. package/dist/modules/skills/skills.tools.js +25 -13
  243. package/dist/modules/skills/skills.tools.js.map +1 -1
  244. package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
  245. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  246. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  247. package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
  248. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  249. package/dist/modules/tool-auth/external-api-key.service.js +4 -1
  250. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  251. package/dist/modules/tool-auth/index.d.ts +1 -1
  252. package/dist/modules/tool-auth/index.d.ts.map +1 -1
  253. package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
  254. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  255. package/dist/modules/tool-auth/internal-token.service.js +1 -0
  256. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  257. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
  258. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  259. package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
  260. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  261. package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
  262. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  263. package/dist/modules/tool-helpers/tool-context.js +3 -0
  264. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  265. package/dist/modules/tool-helpers/tool-def.js +1 -1
  266. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  267. package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
  268. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  269. package/dist/modules/tool-helpers/tool-handler.js +19 -0
  270. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  271. package/dist/modules/tool-helpers/tool.contract.d.ts +10 -0
  272. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  273. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  274. package/dist/modules/tool-helpers/validate-token.d.ts +3 -1
  275. package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
  276. package/dist/modules/tool-helpers/validate-token.js +1 -1
  277. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  278. package/dist/modules/tool-manuals/index.d.ts +3 -3
  279. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  280. package/dist/modules/tool-manuals/index.js +0 -2
  281. package/dist/modules/tool-manuals/index.js.map +1 -1
  282. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
  283. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  284. package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
  285. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  286. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +46 -6
  287. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  288. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
  289. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  290. package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
  291. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  292. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +24 -13
  293. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  294. package/dist/modules/tool-manuals/tool-manuals.service.js +138 -59
  295. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  296. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
  297. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  298. package/dist/modules/tool-manuals/tool-manuals.tools.js +56 -20
  299. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  300. package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
  301. package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
  302. package/dist/modules/tool-registry/manual.routes.js +27 -7
  303. package/dist/modules/tool-registry/manual.routes.js.map +1 -1
  304. package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
  305. package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
  306. package/dist/modules/tool-registry/tool.contract.js.map +1 -1
  307. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  308. package/dist/modules/workflow/agent-tools/workflow.tools.js +36 -27
  309. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  310. package/dist/modules/workflow/git/git.service.d.ts +2 -2
  311. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  312. package/dist/modules/workflow/git/git.service.js +20 -35
  313. package/dist/modules/workflow/git/git.service.js.map +1 -1
  314. package/dist/modules/workflow/workflow.service.d.ts +2 -1
  315. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  316. package/dist/modules/workflow/workflow.service.js +3 -0
  317. package/dist/modules/workflow/workflow.service.js.map +1 -1
  318. package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
  319. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
  320. package/dist/modules/workspace/agent-access.gate.js +1 -1
  321. package/dist/modules/workspace/agent-access.gate.js.map +1 -1
  322. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
  323. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  324. package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
  325. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  326. package/dist/modules/workspace/index.d.ts +8 -0
  327. package/dist/modules/workspace/index.d.ts.map +1 -1
  328. package/dist/modules/workspace/index.js +8 -0
  329. package/dist/modules/workspace/index.js.map +1 -1
  330. package/dist/modules/workspace/nul-scan.d.ts +19 -0
  331. package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
  332. package/dist/modules/workspace/nul-scan.js +55 -0
  333. package/dist/modules/workspace/nul-scan.js.map +1 -0
  334. package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
  335. package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
  336. package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
  337. package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
  338. package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
  339. package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
  340. package/dist/modules/workspace/ripgrep-search.js +657 -0
  341. package/dist/modules/workspace/ripgrep-search.js.map +1 -0
  342. package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
  343. package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
  344. package/dist/modules/workspace/ripgrep-version.js +31 -0
  345. package/dist/modules/workspace/ripgrep-version.js.map +1 -0
  346. package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
  347. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
  348. package/dist/modules/workspace/startup/beside-checkout.js +14 -5
  349. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
  350. package/dist/modules/workspace/worker-pool.d.ts +3 -4
  351. package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
  352. package/dist/modules/workspace/worker-pool.js.map +1 -1
  353. package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
  354. package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
  355. package/dist/modules/workspace/workspace-tmp.js +435 -0
  356. package/dist/modules/workspace/workspace-tmp.js.map +1 -0
  357. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  358. package/dist/modules/workspace/workspace.routes.js +5 -26
  359. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  360. package/dist/modules/workspace/workspace.service.d.ts +47 -6
  361. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  362. package/dist/modules/workspace/workspace.service.js +86 -6
  363. package/dist/modules/workspace/workspace.service.js.map +1 -1
  364. package/dist/modules/workspace/workspace.tools.d.ts +3 -2
  365. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  366. package/dist/modules/workspace/workspace.tools.js +531 -137
  367. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  368. package/dist/shared/oauth-token-error.d.ts +23 -0
  369. package/dist/shared/oauth-token-error.d.ts.map +1 -0
  370. package/dist/shared/oauth-token-error.js +47 -0
  371. package/dist/shared/oauth-token-error.js.map +1 -0
  372. package/dist/shared/path-order.d.ts +28 -8
  373. package/dist/shared/path-order.d.ts.map +1 -1
  374. package/dist/shared/path-order.js +37 -8
  375. package/dist/shared/path-order.js.map +1 -1
  376. package/dist/shared/read-capped.d.ts +19 -0
  377. package/dist/shared/read-capped.d.ts.map +1 -0
  378. package/dist/shared/read-capped.js +45 -0
  379. package/dist/shared/read-capped.js.map +1 -0
  380. package/migrations/0022_session_version.sql +6 -0
  381. package/migrations/0023_agent_path.sql +3 -0
  382. package/migrations/meta/0022_snapshot.json +2360 -0
  383. package/migrations/meta/0023_snapshot.json +2378 -0
  384. package/migrations/meta/_journal.json +14 -0
  385. package/package.json +4 -3
  386. package/src/__tests__/hexis-catalog.ts +144 -0
  387. package/src/__tests__/pg-scratch.test.ts +69 -0
  388. package/src/__tests__/pg-scratch.ts +31 -0
  389. package/src/__tests__/real-disk.ts +21 -0
  390. package/src/__tests__/slow-ripgrep.ts +65 -0
  391. package/src/__tests__/tokens.ts +19 -0
  392. package/src/__tests__/tool-manuals.ts +32 -0
  393. package/src/__tests__/tool-wiring.ts +25 -0
  394. package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
  395. package/src/core/__tests__/catalog-revision.test.ts +89 -0
  396. package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
  397. package/src/core/__tests__/gated-boot.test.ts +8 -1
  398. package/src/core/__tests__/lifecycle.test.ts +46 -13
  399. package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
  400. package/src/core/catalog-revision.ts +24 -2
  401. package/src/core/core-ports.ts +42 -0
  402. package/src/core/create-core-server.ts +14 -1
  403. package/src/core/create-core-services.ts +62 -15
  404. package/src/core/lifecycle.ts +26 -2
  405. package/src/index.ts +1 -1
  406. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
  407. package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
  408. package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
  409. package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
  410. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
  411. package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
  412. package/src/modules/access/__tests__/access-groups.test.ts +55 -22
  413. package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
  414. package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
  415. package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
  416. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
  417. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
  418. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
  419. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  420. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
  421. package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -10
  422. package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
  423. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
  424. package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
  425. package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
  426. package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
  427. package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
  428. package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
  429. package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
  430. package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
  431. package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
  432. package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
  433. package/src/modules/access/access-control.interface.ts +43 -10
  434. package/src/modules/access/access-control.service.ts +250 -72
  435. package/src/modules/access/access-mutation.service.ts +48 -53
  436. package/src/modules/access/access-view.ts +12 -10
  437. package/src/modules/access/access.routes.ts +66 -37
  438. package/src/modules/access/groups-admin.routes.ts +4 -2
  439. package/src/modules/access/groups-admin.service.ts +114 -44
  440. package/src/modules/access/groups-edit.ts +1 -1
  441. package/src/modules/access/index.ts +1 -1
  442. package/src/modules/access/reference-scan.ts +319 -46
  443. package/src/modules/access/roles-admin.service.ts +43 -26
  444. package/src/modules/access/synced-groups-writer.ts +1 -1
  445. package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
  446. package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
  447. package/src/modules/access-model/access-grammar.ts +65 -20
  448. package/src/modules/access-model/access-splice.ts +31 -23
  449. package/src/modules/access-model/group-files.ts +5 -0
  450. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +110 -25
  451. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
  452. package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
  453. package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
  454. package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
  455. package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
  456. package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
  457. package/src/modules/agent-guide/agent-guide.tools.ts +51 -22
  458. package/src/modules/agent-guide/agent-guide.ts +120 -13
  459. package/src/modules/agent-guide/guide-budget.ts +73 -0
  460. package/src/modules/agent-guide/index.ts +12 -0
  461. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +5 -4
  462. package/src/modules/agent-instructions/compose.ts +7 -1
  463. package/src/modules/agent-instructions/shared-file-rules.ts +27 -9
  464. package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
  465. package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
  466. package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
  467. package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
  468. package/src/modules/agent-scope/connection-scope.ts +199 -0
  469. package/src/modules/agent-scope/index.ts +15 -0
  470. package/src/modules/audit/agent-audit.service.ts +2 -0
  471. package/src/modules/audit/audit.contract.ts +6 -0
  472. package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
  473. package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
  474. package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
  475. package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
  476. package/src/modules/auth/account-admission.ts +19 -0
  477. package/src/modules/auth/auth.middleware.ts +45 -4
  478. package/src/modules/auth/auth.routes.ts +46 -29
  479. package/src/modules/auth/auth.service.ts +135 -34
  480. package/src/modules/auth/index.ts +2 -0
  481. package/src/modules/auth/oidc-auth-provider.ts +2 -9
  482. package/src/modules/code-mode/code-mode.tool.ts +2 -2
  483. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
  484. package/src/modules/connection-probe/connection-probe.service.ts +1 -34
  485. package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
  486. package/src/modules/database/core-schema.ts +33 -0
  487. package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
  488. package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
  489. package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
  490. package/src/modules/embed/embed.tools.ts +5 -0
  491. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
  492. package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
  493. package/src/modules/kb-fs/index.ts +2 -0
  494. package/src/modules/kb-fs/locking-filesystem.ts +7 -1
  495. package/src/modules/kb-fs/repo-path.ts +33 -7
  496. package/src/modules/kb-fs/tmp-ref.ts +81 -0
  497. package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
  498. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
  499. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
  500. package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
  501. package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
  502. package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
  503. package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
  504. package/src/modules/mcp/mcp.routes.ts +39 -3
  505. package/src/modules/mcp/mcp.service.ts +112 -52
  506. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
  507. package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
  508. package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
  509. package/src/modules/plugins/index.ts +1 -1
  510. package/src/modules/plugins/plugin-links.ts +19 -2
  511. package/src/modules/plugins/plugins.routes.ts +35 -4
  512. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
  513. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
  514. package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
  515. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
  516. package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
  517. package/src/modules/secrets-vault/index.ts +1 -1
  518. package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
  519. package/src/modules/secrets-vault/secrets-vault.routes.ts +114 -42
  520. package/src/modules/settings/oidc-check.ts +5 -18
  521. package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
  522. package/src/modules/skills/index.ts +7 -2
  523. package/src/modules/skills/skills.contract.ts +17 -0
  524. package/src/modules/skills/skills.service.ts +5 -1
  525. package/src/modules/skills/skills.tools.ts +45 -14
  526. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
  527. package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
  528. package/src/modules/tool-auth/external-api-key.service.ts +5 -2
  529. package/src/modules/tool-auth/index.ts +1 -1
  530. package/src/modules/tool-auth/internal-token.service.ts +10 -0
  531. package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
  532. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
  533. package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
  534. package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
  535. package/src/modules/tool-helpers/tool-context.ts +10 -0
  536. package/src/modules/tool-helpers/tool-def.ts +1 -1
  537. package/src/modules/tool-helpers/tool-handler.ts +34 -0
  538. package/src/modules/tool-helpers/tool.contract.ts +10 -0
  539. package/src/modules/tool-helpers/validate-token.ts +4 -2
  540. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
  541. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
  542. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
  543. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +99 -8
  544. package/src/modules/tool-manuals/index.ts +6 -2
  545. package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
  546. package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -6
  547. package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
  548. package/src/modules/tool-manuals/tool-manuals.service.ts +169 -61
  549. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -25
  550. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
  551. package/src/modules/tool-registry/manual.routes.ts +26 -5
  552. package/src/modules/tool-registry/tool.contract.ts +7 -0
  553. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
  554. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
  555. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
  556. package/src/modules/workflow/agent-tools/workflow.tools.ts +36 -27
  557. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
  558. package/src/modules/workflow/git/git.service.ts +29 -48
  559. package/src/modules/workflow/workflow.service.ts +5 -0
  560. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
  561. package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
  562. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
  563. package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
  564. package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
  565. package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
  566. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
  567. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
  568. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -12
  569. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
  570. package/src/modules/workspace/__tests__/workspace.tools.test.ts +549 -162
  571. package/src/modules/workspace/agent-access.gate.ts +1 -1
  572. package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
  573. package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
  574. package/src/modules/workspace/index.ts +20 -0
  575. package/src/modules/workspace/nul-scan.ts +50 -0
  576. package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
  577. package/src/modules/workspace/ripgrep-search.ts +715 -0
  578. package/src/modules/workspace/ripgrep-version.ts +35 -0
  579. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
  580. package/src/modules/workspace/startup/beside-checkout.ts +13 -5
  581. package/src/modules/workspace/worker-pool.ts +3 -4
  582. package/src/modules/workspace/workspace-tmp.ts +457 -0
  583. package/src/modules/workspace/workspace.routes.ts +6 -27
  584. package/src/modules/workspace/workspace.service.ts +92 -15
  585. package/src/modules/workspace/workspace.tools.ts +574 -176
  586. package/src/shared/__tests__/read-capped.test.ts +68 -0
  587. package/src/shared/oauth-token-error.ts +46 -0
  588. package/src/shared/path-order.ts +37 -8
  589. package/src/shared/read-capped.ts +40 -0
  590. package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
  591. package/starter-packs/general/Plugins/general-starter/access.md +1 -1
  592. package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
  593. package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
  594. package/starter-packs/support/Plugins/support-starter/access.md +1 -1
  595. package/agent-guide/finding-things.md +0 -7
  596. package/agent-guide/introduction.md +0 -27
  597. package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
  598. package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
  599. package/dist/modules/workspace/bounded-matcher.js +0 -200
  600. package/dist/modules/workspace/bounded-matcher.js.map +0 -1
  601. package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
  602. package/src/modules/workspace/bounded-matcher.ts +0 -271
@@ -63,32 +63,38 @@ function parseDomainList(raw: string): string[] {
63
63
  .filter((d) => d.length > 0);
64
64
  }
65
65
  import { SpillStore } from '../modules/workspace/spill-store.js';
66
+ import { TmpSweeper } from '../modules/workspace/workspace-tmp.js';
66
67
  import { AgentUploadStore, assertUploadsRootOutsideWorkspaces } from '../modules/workspace/agent-upload.store.js';
67
68
  import { AgentDownloadStore, type IAgentDownloadStore } from '../modules/workspace/agent-download.store.js';
68
69
  import { createDownloadFetcherIdentifier } from '../modules/workspace/agent-download.routes.js';
69
- import { WorkerPatternMatcher } from '../modules/workspace/bounded-matcher.js';
70
- import type { IPatternMatcher } from '../modules/workspace/pattern-matcher.contract.js';
71
70
  import type { Request } from 'express';
72
71
  import {
72
+ assertRipgrep,
73
73
  DocExtractService,
74
+ RipgrepPatternMatcher,
74
75
  WorkerDocumentExtractor,
75
76
  type IDocumentExtractor,
77
+ type IPatternMatcher,
76
78
  } from '../modules/workspace/index.js';
77
79
  import { UuidSessionSink, type ISessionSink } from '../modules/workspace/session-sink.js';
78
80
  import { AuthService } from '../modules/auth/auth.service.js';
79
81
  import { AccountHooks, type IAccountHooks } from '../modules/auth/account-hooks.js';
80
82
  import { AccountErasureService } from '../modules/auth/account-erasure.service.js';
81
83
  import { OidcAuthProvider, oidcSettingsFrom } from '../modules/auth/oidc-auth-provider.js';
82
- import { createAuthMiddleware, readAuthCookie } from '../modules/auth/auth.middleware.js';
84
+ import { authCookieSecure, createAuthMiddleware, readAuthCookie } from '../modules/auth/auth.middleware.js';
83
85
  import { AccessControlService, loadActiveGroups } from '../modules/access/access-control.service.js';
84
86
  import { AccessMutationService } from '../modules/access/access-mutation.service.js';
85
87
  import { CreatorAccessService } from '../modules/access/creator-access.js';
86
88
  import { ChangeReadGate } from '../modules/access/change-read-gate.js';
87
89
  import { GroupsAdminService } from '../modules/access/groups-admin.service.js';
88
90
  import { UserAccessRemovalService } from '../modules/access/user-access-removal.service.js';
89
- import { makeRolesYamlWriteValidator } from '../modules/access-model/roles-yaml-guard.js';
90
- import { PendingSkillsService, SkillService } from '../modules/skills/index.js';
91
- import { PendingToolsService, ToolManualService } from '../modules/tool-manuals/index.js';
91
+ import { makeRolesYamlWriteValidator } from '../modules/access-model/index.js';
92
+ // The implementations, from their files: the module entries offer them as
93
+ // types only, so nothing that imports an entry loads a service.
94
+ import { SkillService } from '../modules/skills/skills.service.js';
95
+ import { PendingSkillsService } from '../modules/skills/pending-skills.service.js';
96
+ import { ToolManualService } from '../modules/tool-manuals/tool-manuals.service.js';
97
+ import { PendingToolsService } from '../modules/tool-manuals/pending-tools.service.js';
92
98
  import { McpServerEditService } from '../modules/tool-manuals/mcp-server-edit.service.js';
93
99
  import { ToolDeleteService } from '../modules/tool-manuals/tool-delete.service.js';
94
100
  import {
@@ -105,6 +111,7 @@ import {
105
111
  MarketplaceCompilerService,
106
112
  KbPluginSource,
107
113
  } from '../modules/plugins/index.js';
114
+ import { noAgentConfigurations, type IAgentScopeResolver } from '../modules/agent-scope/index.js';
108
115
  import { MarketplaceRepoService } from '../modules/marketplace/index.js';
109
116
  import {
110
117
  GitHubFacadeCredentialsService,
@@ -164,6 +171,7 @@ import { AgentAuditService, retentionDaysFrom } from '../modules/audit/agent-aud
164
171
  import { readAgentPreamble, type AgentPreambleReader } from '../modules/agent-instructions/index.js';
165
172
  import {
166
173
  agentGuideSections,
174
+ guideStartText,
167
175
  joinGuideSections,
168
176
  type AgentGuideReader,
169
177
  type AgentGuideSectionsReader,
@@ -255,7 +263,9 @@ export interface CoreServices {
255
263
  agentUploadStore: AgentUploadStore;
256
264
  /** The bytes `request_file_download` captured, held until their one-time link is fetched or expires. */
257
265
  agentDownloadStore: IAgentDownloadStore;
258
- /** Runs a caller's `grep` pattern off the request thread under a budget; its workers end when the graph stops. */
266
+ /** Sweeps every workspace's `tmp/` folder of entries older than seven days, hourly; started and stopped with the graph. */
267
+ tmpSweeper: TmpSweeper;
268
+ /** Runs a caller's `grep` pattern in ripgrep under a budget; its running searches end when the graph stops. */
259
269
  patternMatcher: IPatternMatcher;
260
270
  /**
261
271
  * Every user a download fetch identifies itself as, by its bearer and its
@@ -287,6 +297,12 @@ export interface CoreServices {
287
297
  * `read_file` of the guide's name answer. See modules/agent-guide.
288
298
  */
289
299
  agentGuide: AgentGuideReader;
300
+ /**
301
+ * The start of that guide and the list of its sections: what a `read_file`
302
+ * of the guide's name answers, the same as `get_agent_guide` with no
303
+ * `section`. See `guideStartText`.
304
+ */
305
+ agentGuideStart: AgentGuideReader;
290
306
  /** The same guide as its sections, each with its title — what `get_agent_guide` lists and serves one of. */
291
307
  agentGuideSections: AgentGuideSectionsReader;
292
308
  mcpServerEditService: McpServerEditService;
@@ -309,6 +325,12 @@ export interface CoreServices {
309
325
  pluginJoinRequestJobs: PluginJoinRequestJobs;
310
326
  /** Which plugins hold which skills (inline or linked) — see `PluginLinkIndex`. */
311
327
  pluginLinkIndex: PluginLinkIndex;
328
+ /**
329
+ * Connecting as an agent configuration: the choices offered at consent and key
330
+ * mint, and the scope every agent surface narrows a connection to — see
331
+ * `modules/agent-scope`.
332
+ */
333
+ agentScopeService: IAgentScopeResolver;
312
334
  /** Link / unlink / repair shared skills into plugins. */
313
335
  pluginLinksService: PluginLinksService;
314
336
  pluginRenameService: PluginRenameService;
@@ -432,6 +454,9 @@ export async function createCoreServices(
432
454
  // (see `git-version.ts`): every clone, fetch and refresh below depends on it,
433
455
  // so an unsupported binary is better surfaced here than at the first merge.
434
456
  await assertGitVersion();
457
+ // The same for ripgrep, which the grep tool searches with and has no
458
+ // fallback for (see `ripgrep-version.ts`).
459
+ await assertRipgrep();
435
460
  // What tells this knowledge base's locks, migration ledger and secrets
436
461
  // loader apart from another's in the same process and database. Empty for
437
462
  // the default schema, so a single-tenant deployment keeps the lock ids and
@@ -767,6 +792,10 @@ export async function createCoreServices(
767
792
  // Shared, workspace-independent store for oversized `call_tool_chain` results,
768
793
  // read back via `read_file`. Sibling of `workspacesRoot`, never committed.
769
794
  const spillStore = new SpillStore(config.spillRoot);
795
+ // What the chat, the SharePoint reader and tool chains stage in a
796
+ // workspace's `tmp/` folder lives seven days past its last change; the file
797
+ // tools read it through the `__tmp__/` ref meanwhile (see workspace-tmp.ts).
798
+ const tmpSweeper = new TmpSweeper((signal) => workspaceService.sweepTmp(undefined, undefined, signal));
770
799
  // The upload route an agent lands files by, so their content never passes
771
800
  // through the model. Bytes live BESIDE the workspaces root (never inside
772
801
  // one) until the apply commits them or the token expires — checked here
@@ -795,7 +824,7 @@ export async function createCoreServices(
795
824
  'captured download bytes wait there for their one-time links, so a root inside a workspace would let the ' +
796
825
  'file tools read them. Point AGENT_UPLOADS_ROOT at a directory whose parent is outside WORKSPACES_ROOT.',
797
826
  });
798
- const patternMatcher = new WorkerPatternMatcher();
827
+ const patternMatcher = new RipgrepPatternMatcher();
799
828
  const agentDownloadStore = new AgentDownloadStore({
800
829
  root: agentDownloadsRoot,
801
830
  publicBaseUrl: config.publicBackendUrl,
@@ -851,6 +880,17 @@ export async function createCoreServices(
851
880
  // The link index resolves manifests against the released catalog, and the
852
881
  // plugin index counts through it (inline + linked), so it comes first.
853
882
  const pluginLinkIndex = new PluginLinkIndex(workspaceService, skillService, accessControl, kb, pluginSource);
883
+ // What "Connect as" offers and what a connection made as an agent loads:
884
+ // the distribution's resolver (`CorePorts.agentScope`), made HERE with the
885
+ // core services it reads through — its configurations behind the person's
886
+ // read gate, `plugins:` expanded through the link index above — or core's
887
+ // default, which offers no agent and loads nothing for a path it cannot
888
+ // read. Made once, before anything consults it: the tool-context resolver
889
+ // below captures it, as the routes the server mounts do, so there is one
890
+ // resolver in effect and no later assignment to miss. Core owns the
891
+ // mechanism and no configuration (see `modules/agent-scope`).
892
+ const agentScopeService: IAgentScopeResolver =
893
+ ports.agentScope?.({ workspaceService, accessControl, kb, disk, pluginLinkIndex }) ?? noAgentConfigurations();
854
894
  const pluginIndexService = new PluginIndexService(
855
895
  workspaceService,
856
896
  accessControl,
@@ -1343,11 +1383,13 @@ export async function createCoreServices(
1343
1383
  // serves the same composition to the local bridge and the frontend card.
1344
1384
  const readPreamble: AgentPreambleReader = () => readAgentPreamble(workspaceService, kb, disk);
1345
1385
  // The guide every agent is told to read first, composed when asked for —
1346
- // the layout is read per call, so a name the setup save applies lands
1347
- // without a restart, and the distribution's hook sees every composition.
1386
+ // the layout and the default branch are read per call, so a name the setup
1387
+ // save applies lands without a restart, and the distribution's hook sees
1388
+ // every composition.
1348
1389
  const agentGuideSectionsReader: AgentGuideSectionsReader = () =>
1349
- agentGuideSections(kb.layout, ports.agentGuide, { kbDirName });
1390
+ agentGuideSections(kb.layout, ports.agentGuide, { kbDirName, defaultBranch: kb.defaultBranch });
1350
1391
  const agentGuide: AgentGuideReader = async () => joinGuideSections(await agentGuideSectionsReader());
1392
+ const agentGuideStart: AgentGuideReader = async () => guideStartText(await agentGuideSectionsReader());
1351
1393
  // The Audit log. Records through the proxy below (every call an external
1352
1394
  // agent makes), reads keys through the key service so their shape is
1353
1395
  // defined once, and prunes past the retention setting — read per sweep, so
@@ -1487,6 +1529,7 @@ export async function createCoreServices(
1487
1529
  kbDirName: kbDirName,
1488
1530
  creatorAccess,
1489
1531
  loadActiveGroups,
1532
+ agentScope: agentScopeService,
1490
1533
  });
1491
1534
  const writeAccess = ports.writeAccess ?? alwaysWritable;
1492
1535
  // The branch every tool call runs on is resolved by the handler, before the
@@ -1496,8 +1539,8 @@ export async function createCoreServices(
1496
1539
  defaultBranch: () => kb.defaultBranch,
1497
1540
  isMissing: (branch) => workspaceService.isBranchMissing(branch),
1498
1541
  });
1499
- const toolAuthMiddleware = createToolAuthMiddleware(externalApiKeyService, internalTokenService, authService);
1500
- const verifyToolToken = createTokenVerifier(externalApiKeyService, internalTokenService, authService);
1542
+ const toolAuthMiddleware = createToolAuthMiddleware(externalApiKeyService, internalTokenService, authService, mcpOAuthProvider);
1543
+ const verifyToolToken = createTokenVerifier(externalApiKeyService, internalTokenService, authService, mcpOAuthProvider);
1501
1544
  // Every credential kind this server issues — connection keys, internal
1502
1545
  // tokens and app sessions (bearer or cookie) — so a fetch carrying another
1503
1546
  // user's identity of ANY kind is refused the link.
@@ -1508,7 +1551,7 @@ export async function createCoreServices(
1508
1551
  // Read-only manual endpoints accept the above PLUS a browser JWT, so a
1509
1552
  // logged-in user can browse the catalog with their session. Execution routes
1510
1553
  // keep `toolAuthMiddleware` (no JWT), so a session can read but not invoke.
1511
- const manualAuthMiddleware = createManualAuthMiddleware(externalApiKeyService, internalTokenService, authService);
1554
+ const manualAuthMiddleware = createManualAuthMiddleware(externalApiKeyService, internalTokenService, authService, mcpOAuthProvider);
1512
1555
 
1513
1556
  // Recovery-bot identity: a synthetic `recovery-bot@bevel.local` user
1514
1557
  // backing (a) the recovery agent's commit authorship and (b) the
@@ -1575,7 +1618,7 @@ export async function createCoreServices(
1575
1618
  settings: () => oidcSettingsFrom(settings),
1576
1619
  publicBackendUrl: config.publicBackendUrl,
1577
1620
  publicFrontendUrl: config.publicFrontendUrl,
1578
- cookieSecure: config.publicBackendUrl.startsWith('https'),
1621
+ cookieSecure: authCookieSecure(config.publicBackendUrl),
1579
1622
  // A real sign-in proves the values that sign-in used. Recorded against
1580
1623
  // those — under their own key — so it says nothing about any saved
1581
1624
  // since, and cannot overwrite what was recorded about them.
@@ -1619,6 +1662,7 @@ export async function createCoreServices(
1619
1662
  workspaceService,
1620
1663
  workflowService,
1621
1664
  accessControl,
1665
+ disk,
1622
1666
  kbDirName,
1623
1667
  () => kb.defaultBranch,
1624
1668
  eventBus,
@@ -1673,6 +1717,7 @@ export async function createCoreServices(
1673
1717
  spillStore,
1674
1718
  agentUploadStore,
1675
1719
  agentDownloadStore,
1720
+ tmpSweeper,
1676
1721
  patternMatcher,
1677
1722
  agentDownloadFetcher,
1678
1723
  documentExtractor,
@@ -1687,6 +1732,7 @@ export async function createCoreServices(
1687
1732
  pendingToolsService,
1688
1733
  readAgentPreamble: readPreamble,
1689
1734
  agentGuide,
1735
+ agentGuideStart,
1690
1736
  agentGuideSections: agentGuideSectionsReader,
1691
1737
  pluginIndexService,
1692
1738
  pluginProvisionService,
@@ -1694,6 +1740,7 @@ export async function createCoreServices(
1694
1740
  joinRequestsService,
1695
1741
  pluginJoinRequestJobs,
1696
1742
  pluginLinkIndex,
1743
+ agentScopeService,
1697
1744
  pluginLinksService,
1698
1745
  pluginRenameService,
1699
1746
  marketplaceCompiler,
@@ -6,6 +6,7 @@ import { noteBesideCheckout } from '../modules/workspace/startup/beside-checkout
6
6
  import { unregisterBevelSecretsVariableLoader } from '../modules/secrets-vault/secrets-variable-loader.js';
7
7
  import type { WorkflowService } from '../modules/workflow/workflow.service.js';
8
8
  import type { PluginJoinRequestJobs } from '../modules/plugins/join-request-jobs.service.js';
9
+ import type { TmpSweeper } from '../modules/workspace/workspace-tmp.js';
9
10
  import { printable } from '../shared/printable.js';
10
11
  import { logger } from '../shared/logging.js';
11
12
 
@@ -293,6 +294,8 @@ export interface BootableCore {
293
294
  kbStartupRunner: Pick<KbStartupRunner, 'runAll' | 'retryUntilMaintained'>;
294
295
  workflowService: Pick<WorkflowService, 'closeChangeRequestsWithDeletedBranches' | 'tidyAfterSweep'>;
295
296
  pluginJoinRequestJobs: Pick<PluginJoinRequestJobs, 'startSweeping'>;
297
+ /** The periodic sweep of every workspace's `tmp/` folder, started here and stopped by {@link stopCore}. */
298
+ tmpSweeper: Pick<TmpSweeper, 'startSweeping'>;
296
299
  /**
297
300
  * The startup phase's retry, when the boot survived an unreachable remote
298
301
  * and is asking again on a timer; null otherwise. Set by {@link startCore},
@@ -336,6 +339,13 @@ export async function startCore<C extends BootableCore>(
336
339
  });
337
340
  }
338
341
 
342
+ // The seven-day sweep of every workspace's `tmp/` folder (see
343
+ // `workspace-tmp.ts`), now and hourly. Needs nothing the startup phase
344
+ // provides — it reads the workspaces root and nothing else — so it is not
345
+ // held behind it, and a remote that is down does not leave staged files to
346
+ // pile up. Stopped by `stopCore`.
347
+ core.tmpSweeper.startSweeping();
348
+
339
349
  // The KB startup phase — AFTER the distribution's onBoot, because a FATAL
340
350
  // template finding raised there must stop the boot before anything seeds
341
351
  // from that template; the runner then brings every branch up to this build
@@ -448,6 +458,16 @@ export interface CoreStopDeps {
448
458
  agentUploadStore?: { stopSweeping(): void; drainSweep(): Promise<void> };
449
459
  /** The agent download store, whose sweep deletes captured bytes nobody fetched — stopped for the same reason. */
450
460
  agentDownloadStore?: { stopSweeping(): void; drainSweep(): Promise<void> };
461
+ /**
462
+ * The sweep of the workspaces' `tmp/` folders: stopped with this graph, so
463
+ * an evicted tenant's graph leaves no timer behind, and its sweep in flight
464
+ * cancelled (it removes nothing more) and then awaited within the stop's
465
+ * deadline, so the next graph over the same root does not meet it still
466
+ * walking there — unless one filesystem call outlasts that deadline, after
467
+ * which the cancelled sweep still removes nothing. Optional: a caller that
468
+ * has not built one passes nothing.
469
+ */
470
+ tmpSweeper?: { stopSweeping(): void; drainSweep(): Promise<void> };
451
471
  /**
452
472
  * The grep pattern matcher, whose worker threads are this graph's own: ended
453
473
  * here so a graph a tenant host evicts leaves no worker behind. Optional: a
@@ -555,6 +575,7 @@ async function releaseCore(deps: CoreStopDeps, remaining: () => number, log: (m:
555
575
  // running to stop deleting.
556
576
  deps.agentUploadStore?.stopSweeping();
557
577
  deps.agentDownloadStore?.stopSweeping();
578
+ deps.tmpSweeper?.stopSweeping();
558
579
  // A startup phase still asking for an unreachable remote stops asking:
559
580
  // synchronous, and nothing after it must be able to clone into a
560
581
  // workspaces folder that is about to belong to nobody.
@@ -587,15 +608,18 @@ async function releaseCore(deps: CoreStopDeps, remaining: () => number, log: (m:
587
608
  if (deps.agentDownloadStore) {
588
609
  await bounded(deps.agentDownloadStore.drainSweep(), remaining(), 'finishing the download sweep', log);
589
610
  }
611
+ if (deps.tmpSweeper) {
612
+ await bounded(deps.tmpSweeper.drainSweep(), remaining(), "finishing the workspaces' tmp sweep", log);
613
+ }
590
614
  // The tidy-up in flight fetches and deletes branches through the pool and
591
615
  // the default branch's checkout: awaited, so the next graph for this
592
616
  // tenant cannot find a branch deletion still running under it.
593
617
  if (deps.workflowService) {
594
618
  await bounded(deps.workflowService.drainTidy(), remaining(), 'finishing the branch tidy-up', log);
595
619
  }
596
- // Its workers end with the graph; a search still running is answered busy.
620
+ // Its ripgrep runs end with the graph; a search still running is answered busy or stopped.
597
621
  if (deps.patternMatcher) {
598
- await bounded(deps.patternMatcher.close(), remaining(), 'ending the search workers', log);
622
+ await bounded(deps.patternMatcher.close(), remaining(), 'ending the running searches', log);
599
623
  }
600
624
  // Likewise: an extraction still running is answered busy, and nothing about its file is remembered.
601
625
  if (deps.documentExtractor) {
package/src/index.ts CHANGED
@@ -26,7 +26,7 @@ export {
26
26
  type ServerExtensions,
27
27
  type ToolSurfaceCtx,
28
28
  } from './core/create-core-server.js';
29
- export { noopRecoveryAgent, type CorePorts } from './core/core-ports.js';
29
+ export { noopRecoveryAgent, type AgentScopeCollaborators, type CorePorts } from './core/core-ports.js';
30
30
  // Logging: the contract, the slot a shell installs its sink into, and the
31
31
  // tagged-logger lookup every module logs through. See shared/logging.ts.
32
32
  export { setLogger, logger, createConsoleLogger } from './shared/logging.js';
@@ -242,6 +242,17 @@ describe('Admin write floor at the repository root', () => {
242
242
  expect(await fs.readFile(path.join(repo, 'access.md'), 'utf-8')).toContain('write:\n - Admin');
243
243
  });
244
244
 
245
+ it('lets the root drop a GROUP named Admin — group/Admin is never the role', async () => {
246
+ await write(repo, 'access.md', '---\n---\nwrite:\n - Admin\n - group/Admin\n');
247
+ const group = { kind: 'role' as const, role: 'group/Admin' };
248
+ await expect(mutation.revoke(WS, 'folder', '', group, ADMIN, 'write')).resolves.toMatchObject({
249
+ changed: true,
250
+ });
251
+ const after = await fs.readFile(path.join(repo, 'access.md'), 'utf-8');
252
+ expect(after).not.toContain('group/Admin');
253
+ expect(after).toContain('write:\n - Admin'); // the role's line is untouched
254
+ });
255
+
245
256
  it('refuses deny-here on a PERSON who is an admin at the root, with the reason, and writes nothing', async () => {
246
257
  const before = await fs.readFile(path.join(repo, 'access.md'), 'utf-8');
247
258
  for (const email of [ADMIN, OWNER]) {
@@ -310,6 +321,7 @@ describe('Admin write floor at the repository root', () => {
310
321
  { emit: () => {} } as unknown as WorkflowEventBus,
311
322
  usersDbDouble(),
312
323
  testKbContext({ kbDirName: KB }),
324
+ new NodeFs(),
313
325
  ),
314
326
  );
315
327
  server = await new Promise<Server>((resolve) => {
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The case table for a name shared by a role and a group (the moved property
3
+ * of decision #315, "a bare name means the role"). Before this change a bare
4
+ * name meant the GROUP when both existed; it now means the ROLE. Exactly the
5
+ * colliding bare spellings change meaning, in grants AND in denials (decided
6
+ * by Razvan on 2026-10-23: a bare denial means the role exactly as a bare
7
+ * grant does; a group denial is written `deny group/<Name>`):
8
+ *
9
+ * rule | role-only member | group-only member
10
+ * -----------------------|-----------------------|----------------------
11
+ * read: Reviewer | was denied → granted | was granted → denied
12
+ * deny Reviewer | was allowed → denied | was denied → allowed
13
+ * read: group/Reviewer | denied (unchanged) | granted (new token)
14
+ * deny group/Reviewer | allowed (unchanged) | denied (new token)
15
+ * read: role/Reviewer | granted (unchanged) | denied (unchanged)
16
+ * deny role/Reviewer | denied (unchanged) | allowed (unchanged)
17
+ *
18
+ * A bare denial never touches the same-named group, so `read: group/Reviewer`
19
+ * beside `deny Reviewer` lets the group in. A name held by only one kind
20
+ * resolves as before. Every file with a colliding bare entry is listed by
21
+ * the groups roster (`bareNameResolvesToRole`) so an Admin can rewrite it as
22
+ * `group/<Name>` or `role/<Name>`.
23
+ */
24
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
25
+ import fs from 'node:fs/promises';
26
+ import path from 'node:path';
27
+ import os from 'node:os';
28
+
29
+ import { NodeFs } from '../../kb-fs/node-fs.js';
30
+ import type { WorkspaceService } from '../../workspace/index.js';
31
+ import { AccessControlService } from '../access-control.service.js';
32
+
33
+ const KB_DIR = 'knowledge-base';
34
+ const WS = 'ws-colliding-name';
35
+
36
+ const ADMIN = 'admin@x.io';
37
+ const ROLE_MEMBER = 'role-member@x.io';
38
+ const GROUP_MEMBER = 'group-member@x.io';
39
+ const DOC = 'Docs/Plan.md';
40
+
41
+ const ROOT_FILES: Record<string, string> = {
42
+ 'roles.yaml': `roles:\n Admin:\n - ${ADMIN}\n Reviewer:\n - ${ROLE_MEMBER}\n`,
43
+ 'groups.yaml': `groups:\n Reviewer:\n - ${GROUP_MEMBER}\n`,
44
+ 'access.md': '---\nwrite:\n - Admin\n---\nwrite:\n - Admin\n',
45
+ [DOC]: '# Plan\n',
46
+ };
47
+
48
+ describe('a name shared by a role and a group: grant and deny case table', () => {
49
+ let root: string;
50
+ let repo: string;
51
+ let svc: AccessControlService;
52
+
53
+ beforeEach(async () => {
54
+ root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-colliding-name-'));
55
+ const workspaceDir = path.join(root, WS);
56
+ repo = path.join(workspaceDir, KB_DIR);
57
+ for (const [rel, text] of Object.entries(ROOT_FILES)) {
58
+ await fs.mkdir(path.dirname(path.join(repo, rel)), { recursive: true });
59
+ await fs.writeFile(path.join(repo, rel), text);
60
+ }
61
+ const workspaceService = {
62
+ getWorkspacePath: async () => workspaceDir,
63
+ ensureRemotesFetched: async () => undefined,
64
+ } as unknown as WorkspaceService;
65
+ svc = new AccessControlService(workspaceService, KB_DIR, new NodeFs());
66
+ });
67
+
68
+ afterEach(async () => {
69
+ await fs.rm(root, { recursive: true, force: true });
70
+ });
71
+
72
+ /** Who may read DOC when the Docs folder's read list is `entries`. */
73
+ async function readers(entries: string[]): Promise<{ role: boolean; group: boolean }> {
74
+ const body = `read:\n${entries.map((e) => ` - ${e}\n`).join('')}`;
75
+ await fs.writeFile(path.join(repo, 'Docs/access.md'), `---\nwrite:\n - Admin\n---\n${body}`);
76
+ svc.invalidate(WS);
77
+ return {
78
+ role: await svc.canRead(WS, ROLE_MEMBER, DOC),
79
+ group: await svc.canRead(WS, GROUP_MEMBER, DOC),
80
+ };
81
+ }
82
+
83
+ it('a bare grant reaches the role only', async () => {
84
+ expect(await readers(['Reviewer'])).toEqual({ role: true, group: false });
85
+ });
86
+
87
+ it('a bare denial reaches the role only: the members of the same-named group are untouched', async () => {
88
+ expect(await readers(['everyone', 'deny Reviewer'])).toEqual({ role: false, group: true });
89
+ });
90
+
91
+ it('a group grant beside a bare denial lets the group in', async () => {
92
+ expect(await readers(['group/Reviewer', 'deny Reviewer'])).toEqual({ role: false, group: true });
93
+ });
94
+
95
+ it('group/<Name> grants and denies the group only', async () => {
96
+ expect(await readers(['group/Reviewer'])).toEqual({ role: false, group: true });
97
+ expect(await readers(['everyone', 'deny group/Reviewer'])).toEqual({ role: true, group: false });
98
+ });
99
+
100
+ it('role/<Name> grants and denies the role only', async () => {
101
+ expect(await readers(['role/Reviewer'])).toEqual({ role: true, group: false });
102
+ expect(await readers(['everyone', 'deny role/Reviewer'])).toEqual({ role: false, group: true });
103
+ });
104
+
105
+ it('a bare name held only by a group still means the group, in grants and denials', async () => {
106
+ await fs.writeFile(
107
+ path.join(repo, 'roles.yaml'),
108
+ `roles:\n Admin:\n - ${ADMIN}\n`,
109
+ );
110
+ expect(await readers(['Reviewer'])).toEqual({ role: false, group: true });
111
+ expect(await readers(['everyone', 'deny Reviewer'])).toEqual({ role: true, group: false });
112
+ });
113
+ });
@@ -0,0 +1,117 @@
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 { NodeFs } from '../../kb-fs/node-fs.js';
6
+ import type { WorkspaceService } from '../../workspace/workspace.service.js';
7
+ import { AccessControlService } from '../access-control.service.js';
8
+
9
+ const KB = 'knowledge-base';
10
+ const RULE = 'read:\n - deny everyone\n - Product Manager\n';
11
+ const ali = 'ali@bevel.software'; // no role
12
+ const felix = 'felix@example.com'; // Product Manager
13
+
14
+ /**
15
+ * A file's own access rules live in its frontmatter, so the read checks read
16
+ * a file only as far as its frontmatter reaches — a line, for a file with
17
+ * none. That is what keeps a batch over a tree of large transcripts (grep's,
18
+ * the explorer's) from reading the whole tree, and these pin that the head
19
+ * answers exactly as the whole file would, fence rule and all.
20
+ */
21
+ describe('AccessControlService — a file is read only as far as its frontmatter', () => {
22
+ let root: string;
23
+ let repo: string;
24
+ let svc: AccessControlService;
25
+ const workspaceId = 'ws-frontmatter-head';
26
+
27
+ beforeEach(async () => {
28
+ root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-fm-head-'));
29
+ const workspaceDir = path.join(root, workspaceId);
30
+ repo = path.join(workspaceDir, KB);
31
+ await fs.mkdir(path.join(repo, 'Open'), { recursive: true });
32
+ await fs.writeFile(path.join(repo, 'roles.yaml'), 'roles:\n Admin:\n - razvan@bevel.software\n Product Manager:\n - felix@example.com\n');
33
+ await fs.writeFile(path.join(repo, 'Open', 'access.md'), '---\nread:\n - everyone\n---\n');
34
+ svc = new AccessControlService(
35
+ { getWorkspacePath: async () => workspaceDir } as unknown as WorkspaceService,
36
+ KB,
37
+ new NodeFs(),
38
+ );
39
+ });
40
+ afterEach(async () => {
41
+ await fs.rm(root, { recursive: true, force: true });
42
+ });
43
+
44
+ const put = (name: string, content: string | Buffer) => fs.writeFile(path.join(repo, 'Open', name), content);
45
+ const verdicts = async (name: string) => {
46
+ const rel = `Open/${name}`;
47
+ const batch = await svc.canReadBatch(workspaceId, ali, [rel]);
48
+ const felixBatch = await svc.canReadBatch(workspaceId, felix, [rel]);
49
+ return {
50
+ ali: batch.get(rel),
51
+ felix: felixBatch.get(rel),
52
+ single: await svc.canRead(workspaceId, ali, rel),
53
+ };
54
+ };
55
+
56
+ it('honours the rules of a file far too large to read whole, at once', async () => {
57
+ await put('huge.md', `---\n${RULE}---\n`);
58
+ // 600 MB, sparse: more text than one string can hold, so a read of the
59
+ // whole file could not even be decoded, and its rule would be missed.
60
+ await fs.truncate(path.join(repo, 'Open', 'huge.md'), 600 * 1024 * 1024);
61
+ expect(await verdicts('huge.md')).toEqual({ ali: false, felix: true, single: false });
62
+ });
63
+
64
+ it('reads a frontmatter longer than any one read through to its closing fence', async () => {
65
+ await put('long-fm.md', `---\nsummary: "${'x'.repeat(200_000)}"\n${RULE}---\nbody\n`);
66
+ expect(await verdicts('long-fm.md')).toEqual({ ali: false, felix: true, single: false });
67
+ });
68
+
69
+ it('scans a frontmatter line far longer than one read once, not again with every read', async () => {
70
+ // A long line inside the frontmatter, then the rule; and a closing fence
71
+ // padded with 3 MB of spaces, which stays a fence however long it runs.
72
+ const longValue = (chars: number) =>
73
+ `---\nsummary: "${'x'.repeat(chars)}"\n${RULE}${' '.repeat(3_000_000)}---${' '.repeat(3_000_000)}\nbody\n`;
74
+ await put('long-value.md', longValue(32_000_000));
75
+ expect(await verdicts('long-value.md')).toEqual({ ali: false, felix: true, single: false });
76
+ // How the cost grows, not how long it takes on this machine: a line 16
77
+ // times longer costs about 16 times as much scanned once, and far more
78
+ // (about 60 times, measured) scanned again with every read. The slowest
79
+ // short check against the fastest long one, so noise only helps a scan
80
+ // that is linear pass.
81
+ const timed = async (name: string, chars: number): Promise<number> => {
82
+ await put(name, `---\nsummary: "${'x'.repeat(chars)}"\n${RULE}---\n`);
83
+ const started = performance.now();
84
+ await svc.canReadBatch(workspaceId, ali, [`Open/${name}`]);
85
+ return performance.now() - started;
86
+ };
87
+ const short: number[] = [];
88
+ const long: number[] = [];
89
+ for (let i = 0; i < 3; i++) {
90
+ short.push(await timed(`short-${i}.md`, 2_000_000));
91
+ long.push(await timed(`long-${i}.md`, 32_000_000));
92
+ }
93
+ expect(Math.min(...long) / Math.max(...short)).toBeLessThan(32);
94
+ }, 60_000);
95
+
96
+ it('applies the fence rule as the whole-file scan does: CRLF, a BOM, padding around the fence', async () => {
97
+ await put('crlf.md', `---\r\n${RULE.replace(/\n/g, '\r\n')}---\r\nbody\r\n`);
98
+ await put('bom.md', `\uFEFF---\n${RULE}---\n`);
99
+ await put('padded.md', `${' '.repeat(5_000)}--- \n${RULE} ---\n`);
100
+ for (const name of ['crlf.md', 'bom.md', 'padded.md']) {
101
+ expect(await verdicts(name), name).toEqual({ ali: false, felix: true, single: false });
102
+ }
103
+ });
104
+
105
+ it('finds no rules where the whole-file scan finds none', async () => {
106
+ // A fenced block that is not at the top is body, not frontmatter.
107
+ await put('later.md', `# title\n---\n${RULE}---\n`);
108
+ // An opening fence never closed is no frontmatter either.
109
+ await put('unclosed.md', `---\n${RULE}`);
110
+ // A first line that is long, and no fence, ends the read at once.
111
+ await put('long-line.md', `${'x'.repeat(3_000_000)}\n---\n${RULE}---\n`);
112
+ await put('empty.md', '');
113
+ for (const name of ['later.md', 'unclosed.md', 'long-line.md', 'empty.md']) {
114
+ expect(await verdicts(name), name).toEqual({ ali: true, felix: true, single: true });
115
+ }
116
+ });
117
+ });