@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
@@ -0,0 +1,269 @@
1
+ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
2
+ import express from 'express';
3
+ import type { Server } from 'node:http';
4
+ import { eq } from 'drizzle-orm';
5
+ import { users, type Database } from '../../database/index.js';
6
+ import { PG_ADMIN_URL, PG_TEST_TIMEOUT, dropScratchDatabases, openMigratedScratchDb } from '../../../__tests__/pg-scratch.js';
7
+ import { AuthService } from '../auth.service.js';
8
+ import { SessionEndedError } from '../account-admission.js';
9
+ import { createAuthRoutes } from '../auth.routes.js';
10
+ import { AUTH_COOKIE_NAME, createAuthMiddleware } from '../auth.middleware.js';
11
+ import { verifyPassword } from '../password-hash.js';
12
+
13
+ /**
14
+ * Something to run once, the next time a password is hashed: a change hashes
15
+ * its new password after reading the row and proving the current one, and
16
+ * before writing, so this is where "another session changed it first" lands.
17
+ */
18
+ const beforeNextHash = vi.hoisted(() => ({ run: null as null | (() => Promise<void>) }));
19
+ vi.mock('../password-hash.js', async (importOriginal) => {
20
+ const original = await importOriginal<typeof import('../password-hash.js')>();
21
+ return {
22
+ ...original,
23
+ hashPassword: async (password: string) => {
24
+ const run = beforeNextHash.run;
25
+ beforeNextHash.run = null;
26
+ if (run) await run();
27
+ return original.hashPassword(password);
28
+ },
29
+ };
30
+ });
31
+
32
+ /**
33
+ * Changing your password ends the sessions opened with the old one, and a
34
+ * change whose current password stopped being current is refused. Both are
35
+ * a row's session generation and a conditional write on it, which no stub
36
+ * can stand in for, so this crosses a real Postgres (see `pg-scratch.ts`)
37
+ * through the real routes and the real auth middleware.
38
+ */
39
+ const JWT_SECRET = 'test-jwt-secret';
40
+
41
+ describe.skipIf(!PG_ADMIN_URL)('a password change and the sessions opened before it, on a real Postgres', () => {
42
+ let db: Database;
43
+ let closeDb: () => Promise<void>;
44
+ let auth: AuthService;
45
+ let server: Server;
46
+ let base: string;
47
+
48
+ beforeAll(async () => {
49
+ ({ db, close: closeDb } = await openMigratedScratchDb('hexis_pwchange'));
50
+ auth = new AuthService(db, { jwtSecret: JWT_SECRET, adminEmail: '', adminPassword: '', allowedEmailDomains: [] });
51
+ const app = express();
52
+ app.use(express.json());
53
+ app.use('/api', createAuthRoutes(auth, createAuthMiddleware(auth)));
54
+ await new Promise<void>((resolve) => {
55
+ server = app.listen(0, resolve);
56
+ });
57
+ const address = server.address();
58
+ if (typeof address === 'string' || !address) throw new Error('no port');
59
+ base = `http://127.0.0.1:${address.port}`;
60
+ }, PG_TEST_TIMEOUT);
61
+
62
+ afterAll(async () => {
63
+ await new Promise<void>((resolve) => {
64
+ if (server) server.close(() => resolve());
65
+ else resolve();
66
+ });
67
+ if (db) await closeDb();
68
+ await dropScratchDatabases();
69
+ }, PG_TEST_TIMEOUT);
70
+
71
+ /** A sign-in: its bearer token, and the auth cookie a browser would keep. */
72
+ async function signIn(email: string, password: string): Promise<{ token: string; cookie: string }> {
73
+ const res = await fetch(`${base}/api/auth/login`, {
74
+ method: 'POST',
75
+ headers: { 'Content-Type': 'application/json' },
76
+ body: JSON.stringify({ email, password }),
77
+ });
78
+ expect(res.status).toBe(200);
79
+ return { token: ((await res.json()) as { token: string }).token, cookie: authCookie(res) };
80
+ }
81
+
82
+ function authCookie(res: Response): string {
83
+ const set = res.headers.getSetCookie().find((c) => c.startsWith(`${AUTH_COOKIE_NAME}=`));
84
+ if (!set) throw new Error('no auth cookie set');
85
+ return set.split(';')[0];
86
+ }
87
+
88
+ function changePassword(token: string, currentPassword: string, newPassword: string) {
89
+ return fetch(`${base}/api/auth/change-password`, {
90
+ method: 'POST',
91
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
92
+ body: JSON.stringify({ currentPassword, newPassword }),
93
+ });
94
+ }
95
+
96
+ const meWithBearer = (token: string) =>
97
+ fetch(`${base}/api/auth/me`, { headers: { Authorization: `Bearer ${token}` } }).then((r) => r.status);
98
+ const meWithCookie = (cookie: string) =>
99
+ fetch(`${base}/api/auth/me`, { headers: { Cookie: cookie } }).then((r) => r.status);
100
+
101
+ async function storedRow(email: string) {
102
+ const [row] = await db.select().from(users).where(eq(users.emailBidx, email)).limit(1);
103
+ return row;
104
+ }
105
+
106
+ it(
107
+ 'ends every token and browser session issued before the change, except the one that made it',
108
+ async () => {
109
+ await auth.createAccount('kim@acme.test', 'Kim', 'old-password-1');
110
+ const changer = await signIn('kim@acme.test', 'old-password-1');
111
+ const laptop = await signIn('kim@acme.test', 'old-password-1');
112
+ const browser = await signIn('kim@acme.test', 'old-password-1');
113
+ // All three are live, and the cached answer the middleware now holds
114
+ // is exactly what the change has to get past.
115
+ expect(await meWithBearer(changer.token)).toBe(200);
116
+ expect(await meWithBearer(laptop.token)).toBe(200);
117
+ expect(await meWithCookie(browser.cookie)).toBe(200);
118
+
119
+ const res = await changePassword(changer.token, 'old-password-1', 'new-password-1');
120
+ expect(res.status).toBe(200);
121
+ const body = (await res.json()) as { ok: boolean; token: string };
122
+ expect(body.ok).toBe(true);
123
+
124
+ // Every session opened before the change answers 401 on its next request...
125
+ expect(await meWithBearer(laptop.token)).toBe(401);
126
+ expect(await meWithCookie(browser.cookie)).toBe(401);
127
+ expect(await meWithBearer(browser.token)).toBe(401);
128
+ expect(await meWithCookie(laptop.cookie)).toBe(401);
129
+ // ...the changer's old credentials included: what it carries on with is
130
+ // the fresh token and cookie the change handed back.
131
+ expect(await meWithBearer(changer.token)).toBe(401);
132
+ expect(await meWithCookie(changer.cookie)).toBe(401);
133
+ expect(await meWithBearer(body.token)).toBe(200);
134
+ expect(await meWithCookie(authCookie(res))).toBe(200);
135
+
136
+ // The old password signs nobody in; the new one does, and its session lives.
137
+ const old = await fetch(`${base}/api/auth/login`, {
138
+ method: 'POST',
139
+ headers: { 'Content-Type': 'application/json' },
140
+ body: JSON.stringify({ email: 'kim@acme.test', password: 'old-password-1' }),
141
+ });
142
+ expect(old.status).toBe(401);
143
+ const again = await signIn('kim@acme.test', 'new-password-1');
144
+ expect(await meWithBearer(again.token)).toBe(200);
145
+ // ...and so does the changer's, alongside it.
146
+ expect(await meWithBearer(body.token)).toBe(200);
147
+ },
148
+ PG_TEST_TIMEOUT,
149
+ );
150
+
151
+ it(
152
+ 'refuses, in words, a change whose current password another session changed first, and writes nothing',
153
+ async () => {
154
+ await auth.createAccount('lena@acme.test', 'Lena', 'old-password-1');
155
+ const first = await signIn('lena@acme.test', 'old-password-1');
156
+ const second = await signIn('lena@acme.test', 'old-password-1');
157
+
158
+ // The second session's change has read the row and proven the old
159
+ // password when the first session's change lands.
160
+ let firstStatus = 0;
161
+ beforeNextHash.run = async () => {
162
+ firstStatus = (await changePassword(first.token, 'old-password-1', 'first-new-password')).status;
163
+ };
164
+ const raced = await changePassword(second.token, 'old-password-1', 'second-new-password');
165
+ expect(firstStatus).toBe(200);
166
+ expect(raced.status).toBe(409);
167
+ expect(((await raced.json()) as { error: string }).error).toMatch(
168
+ /changed from another session.*nothing was changed/,
169
+ );
170
+
171
+ // Nothing of the refused change was written: the first change's
172
+ // password is the account's, and the generation moved on once.
173
+ const afterRace = await storedRow('lena@acme.test');
174
+ expect(await verifyPassword('first-new-password', afterRace.passwordHash!)).toBe(true);
175
+ expect(await verifyPassword('second-new-password', afterRace.passwordHash!)).toBe(false);
176
+ expect(afterRace.sessionVersion).toBe(1);
177
+
178
+ // A session opened since, typing the password that is no longer
179
+ // current, is refused in words and changes nothing either.
180
+ const third = await signIn('lena@acme.test', 'first-new-password');
181
+ const stale = await changePassword(third.token, 'old-password-1', 'third-new-password');
182
+ expect(stale.status).toBe(400);
183
+ expect(((await stale.json()) as { error: string }).error).toBe('Current password is incorrect');
184
+ const afterStale = await storedRow('lena@acme.test');
185
+ expect(afterStale.passwordHash).toBe(afterRace.passwordHash);
186
+ expect(afterStale.sessionVersion).toBe(1);
187
+ expect(await meWithBearer(third.token)).toBe(200);
188
+ },
189
+ PG_TEST_TIMEOUT,
190
+ );
191
+
192
+ it(
193
+ 'ends the sessions opened before an admin reset, and refuses a change that read the row before it',
194
+ async () => {
195
+ await auth.createAccount('noor@acme.test', 'Noor', 'old-password-1');
196
+ const laptop = await signIn('noor@acme.test', 'old-password-1');
197
+ const changer = await signIn('noor@acme.test', 'old-password-1');
198
+ expect(await meWithBearer(laptop.token)).toBe(200);
199
+ expect(await meWithCookie(laptop.cookie)).toBe(200);
200
+
201
+ // The reset lands after the self-service change has proven the old password.
202
+ beforeNextHash.run = async () => {
203
+ await auth.createAccount('noor@acme.test', undefined, 'reset-password-1');
204
+ };
205
+ const raced = await changePassword(changer.token, 'old-password-1', 'self-new-password');
206
+ expect(raced.status).toBe(409);
207
+
208
+ // The reset is the account's password, and the generation moved on once.
209
+ const row = await storedRow('noor@acme.test');
210
+ expect(await verifyPassword('reset-password-1', row.passwordHash!)).toBe(true);
211
+ expect(row.sessionVersion).toBe(1);
212
+ expect(await meWithBearer(laptop.token)).toBe(401);
213
+ expect(await meWithCookie(laptop.cookie)).toBe(401);
214
+ expect(await meWithBearer(changer.token)).toBe(401);
215
+ const fresh = await signIn('noor@acme.test', 'reset-password-1');
216
+ expect(await meWithBearer(fresh.token)).toBe(200);
217
+ },
218
+ PG_TEST_TIMEOUT,
219
+ );
220
+
221
+ it(
222
+ 'ends the old sessions on another replica at their next request too',
223
+ async () => {
224
+ // A second process on the same database, with its own cache, which has
225
+ // just seen the session live.
226
+ const replica = new AuthService(db, { jwtSecret: JWT_SECRET, adminEmail: '', adminPassword: '', allowedEmailDomains: [] });
227
+ await auth.createAccount('omar@acme.test', 'Omar', 'old-password-1');
228
+ const laptop = await signIn('omar@acme.test', 'old-password-1');
229
+ const changer = await signIn('omar@acme.test', 'old-password-1');
230
+ await expect(replica.resolveSession(laptop.token)).resolves.toMatchObject({ email: 'omar@acme.test' });
231
+
232
+ const res = await changePassword(changer.token, 'old-password-1', 'new-password-1');
233
+ expect(res.status).toBe(200);
234
+ const { token } = (await res.json()) as { token: string };
235
+
236
+ const ended = replica.resolveSession(laptop.token);
237
+ await expect(ended).rejects.toBeInstanceOf(SessionEndedError);
238
+ await expect(ended).rejects.toThrow(/password was changed after this session was opened/);
239
+ await expect(replica.resolveSession(token)).resolves.toMatchObject({ email: 'omar@acme.test' });
240
+ },
241
+ PG_TEST_TIMEOUT,
242
+ );
243
+
244
+ it(
245
+ 'refuses a first password set from the Account page when an admin set a starting one meanwhile',
246
+ async () => {
247
+ // Single sign-on only: no password yet, so the Account page asks for no current one.
248
+ const { token: sso } = await auth.loginWithSso('pia@acme.test', 'Pia', { admittedByProvider: true });
249
+ expect(await meWithBearer(sso)).toBe(200);
250
+
251
+ beforeNextHash.run = async () => {
252
+ const set = await auth.createAccountWithStartingPassword('pia@acme.test', 'starting-password-1');
253
+ expect(set.passwordSet).toBe(true);
254
+ };
255
+ const raced = await fetch(`${base}/api/auth/change-password`, {
256
+ method: 'POST',
257
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${sso}` },
258
+ body: JSON.stringify({ newPassword: 'self-new-password' }),
259
+ });
260
+ expect(raced.status).toBe(409);
261
+
262
+ const row = await storedRow('pia@acme.test');
263
+ expect(await verifyPassword('starting-password-1', row.passwordHash!)).toBe(true);
264
+ expect(row.sessionVersion).toBe(1);
265
+ expect(await meWithBearer(sso)).toBe(401);
266
+ },
267
+ PG_TEST_TIMEOUT,
268
+ );
269
+ });
@@ -82,6 +82,25 @@ export class AccountDeactivatedError extends Error {
82
82
  }
83
83
  }
84
84
 
85
+ /**
86
+ * A session token that verifies but is no longer honoured: the account's
87
+ * password was changed after the token was issued, which ends every session
88
+ * opened before it. A 401 with these words, so the person knows why they are
89
+ * back at the sign-in form rather than reading "invalid token".
90
+ */
91
+ export class SessionEndedError extends Error {
92
+ readonly status = 401;
93
+
94
+ constructor(message = SESSION_ENDED_MESSAGE) {
95
+ super(message);
96
+ this.name = 'SessionEndedError';
97
+ }
98
+ }
99
+
100
+ /** What a person whose session a password change ended is told. */
101
+ export const SESSION_ENDED_MESSAGE =
102
+ 'Your password was changed after this session was opened. Sign in again with the new password.';
103
+
85
104
  /**
86
105
  * Whether the account behind a credential is on could not be asked — the
87
106
  * database failed. Not the caller's fault and not a verdict on them: a
@@ -1,6 +1,6 @@
1
1
  import type { Request, Response, NextFunction } from 'express';
2
2
  import type { AuthService } from './auth.service.js';
3
- import { AccountDeactivatedError, AuthBackendError } from './account-admission.js';
3
+ import { AccountDeactivatedError, AuthBackendError, SessionEndedError } from './account-admission.js';
4
4
 
5
5
  // Augment Express Request so userId/userEmail are available after auth middleware
6
6
  declare global {
@@ -20,10 +20,49 @@ declare global {
20
20
  * header, which the frontend stores in memory and attaches via `authFetch`.
21
21
  *
22
22
  * Same JWT value as the Bearer token. The cookie is `HttpOnly` + `SameSite=Lax`
23
- * + `Secure` (in prod) — see `auth.routes.ts` for the issue site.
23
+ * + `Secure` when the deployment is reached over https — {@link setAuthCookie}
24
+ * is its one issue site, for every sign-in path and the password change.
24
25
  */
25
26
  export const AUTH_COOKIE_NAME = 'bevel_token';
26
27
 
28
+ /**
29
+ * Max age of the JWT cookie (seconds). Matches the JWT's own `expiresIn:
30
+ * '7d'` so the cookie expires when the token does — browsers stop
31
+ * sending it past expiry, sparing the auth middleware verifying tokens
32
+ * that are guaranteed to fail.
33
+ */
34
+ export const AUTH_COOKIE_MAX_AGE_S = 7 * 24 * 60 * 60;
35
+
36
+ /**
37
+ * Whether the auth cookie is marked `Secure`, from the scheme the deployment
38
+ * is reached on: a browser never sends a `Secure` cookie over plain http, so
39
+ * deriving it from `NODE_ENV` left a production deployment served over http
40
+ * (a private network, a proxy that terminates nothing) without SSE and
41
+ * images. One rule, asked by the composition root for every path that sets
42
+ * the cookie. Read by the URL parser, as the deployment's URL is everywhere
43
+ * else: a scheme is case-insensitive, and `HTTPS://` is https.
44
+ */
45
+ export function authCookieSecure(publicUrl: string): boolean {
46
+ return new URL(publicUrl).protocol === 'https:';
47
+ }
48
+
49
+ /**
50
+ * Set the JWT as the HttpOnly auth cookie so transports that can't carry
51
+ * headers (EventSource for SSE, image tags) authenticate automatically. JSON
52
+ * callers continue to use the Bearer header returned in the body. The one
53
+ * place the cookie's attributes are spelled: password login, the password
54
+ * change and every SSO provider set it through here.
55
+ */
56
+ export function setAuthCookie(res: Response, token: string, secure: boolean): void {
57
+ res.cookie(AUTH_COOKIE_NAME, token, {
58
+ httpOnly: true,
59
+ sameSite: 'lax',
60
+ secure,
61
+ maxAge: AUTH_COOKIE_MAX_AGE_S * 1000,
62
+ path: '/',
63
+ });
64
+ }
65
+
27
66
  /**
28
67
  * Read the auth cookie out of the raw `Cookie` header without pulling in
29
68
  * `cookie-parser`. We only ever look for one cookie, so a tiny parser
@@ -78,8 +117,10 @@ export function createAuthMiddleware(authService: Pick<AuthService, 'resolveSess
78
117
  return;
79
118
  }
80
119
  // A 401 otherwise, so the app drops the session and shows the login
81
- // page; a switched-off account is told why there.
82
- res.status(401).json({ error: err instanceof AccountDeactivatedError ? err.message : 'Invalid or expired token' });
120
+ // page; a switched-off account, and a session a password change
121
+ // ended, are told why there.
122
+ const worded = err instanceof AccountDeactivatedError || err instanceof SessionEndedError;
123
+ res.status(401).json({ error: worded ? err.message : 'Invalid or expired token' });
83
124
  return;
84
125
  }
85
126
  req.userId = session.userId;
@@ -2,19 +2,26 @@ import express from 'express';
2
2
  import { logger } from '../../shared/logging.js';
3
3
 
4
4
  const log = logger('auth');
5
- import type { AuthService } from './auth.service.js';
6
- import { AUTH_COOKIE_NAME } from './auth.middleware.js'; // also imports Express Request augmentation
5
+ import { PasswordChangedMeanwhileError, PasswordRefusedError, type AuthService } from './auth.service.js';
6
+ import { setAuthCookie } from './auth.middleware.js'; // also imports Express Request augmentation
7
7
  import { FixedWindowRateLimiter } from './rate-limit.js';
8
8
  import { AccountDeactivatedError } from './account-admission.js';
9
9
  import { canonicalEmail } from '../../shared/email-identity.js';
10
10
 
11
- /**
12
- * Max age of the JWT cookie (seconds). Matches the JWT's own `expiresIn:
13
- * '7d'` so the cookie expires when the token does — browsers stop
14
- * sending it past expiry, sparing the auth middleware verifying tokens
15
- * that are guaranteed to fail.
16
- */
17
- export const AUTH_COOKIE_MAX_AGE_S = 7 * 24 * 60 * 60;
11
+ // The cookie's lifetime lives beside its one issue site now; still offered
12
+ // here for the providers that learnt it from this file.
13
+ export { AUTH_COOKIE_MAX_AGE_S } from './auth.middleware.js';
14
+
15
+ /** What {@link createAuthRoutes} takes beyond its services. */
16
+ export interface AuthRoutesOptions {
17
+ /**
18
+ * Whether the auth cookie is marked `Secure`: the composition root derives
19
+ * it from the deployment's public URL (`authCookieSecure`), the one rule
20
+ * every provider's cookie follows too. Absent — a harness that mounts the
21
+ * routes on their own — it falls back to `NODE_ENV === 'production'`.
22
+ */
23
+ cookieSecure?: boolean;
24
+ }
18
25
 
19
26
  /**
20
27
  * A pluggable SSO login method. Core ships password login and (when
@@ -49,8 +56,10 @@ export function createAuthRoutes(
49
56
  authMiddleware: express.RequestHandler,
50
57
  providers: AuthProviderPlugin[] = [],
51
58
  passwordLoginEnabled = true,
59
+ options: AuthRoutesOptions = {},
52
60
  ): express.Router {
53
61
  const router = express.Router();
62
+ const cookieSecure = options.cookieSecure ?? process.env.NODE_ENV === 'production';
54
63
 
55
64
  // Brute-force guards on the password endpoints (scrypt also makes each
56
65
  // attempt expensive server-side, so these double as DoS protection):
@@ -100,18 +109,7 @@ export function createAuthRoutes(
100
109
  // attempts isn't rate-limited on their next visit. (The wider per-ip
101
110
  // window intentionally keeps counting.)
102
111
  loginPairLimiter.reset(pairKey);
103
- // Also set the JWT as an HttpOnly cookie so transports that can't
104
- // carry headers (EventSource for SSE, image tags) authenticate
105
- // automatically. JSON callers continue to use the Bearer header
106
- // returned in the body. Secure in production; off in dev so the
107
- // cookie survives `http://localhost` requests.
108
- res.cookie(AUTH_COOKIE_NAME, result.token, {
109
- httpOnly: true,
110
- sameSite: 'lax',
111
- secure: process.env.NODE_ENV === 'production',
112
- maxAge: AUTH_COOKIE_MAX_AGE_S * 1000,
113
- path: '/',
114
- });
112
+ setAuthCookie(res, result.token, cookieSecure);
115
113
  res.json(result);
116
114
  } catch (error) {
117
115
  // The password was right and an admin switched the account off: said
@@ -144,6 +142,9 @@ export function createAuthRoutes(
144
142
  // POST /api/auth/change-password — self-service (protected). Requires the
145
143
  // current password whenever one is set; an SSO-only account sets its first
146
144
  // password without one.
145
+ //
146
+ // A change ends every session opened before it, this one included, so the
147
+ // caller is handed a fresh token (body and cookie) to carry on with.
147
148
  router.post('/auth/change-password', authMiddleware, async (req, res) => {
148
149
  const { currentPassword, newPassword } = req.body as {
149
150
  currentPassword?: string;
@@ -158,13 +159,27 @@ export function createAuthRoutes(
158
159
  return;
159
160
  }
160
161
  try {
161
- await authService.changePassword(req.userId!, currentPassword, newPassword);
162
- res.json({ ok: true });
162
+ const { token } = await authService.changePassword(req.userId!, currentPassword, newPassword);
163
+ setAuthCookie(res, token, cookieSecure);
164
+ res.json({ ok: true, token });
163
165
  } catch (error) {
164
- const msg = error instanceof Error ? error.message : 'Unknown error';
165
- // Policy violations and a wrong current password are caller errors, not
166
- // server faults — and the message is safe to show verbatim.
167
- res.status(400).json({ error: msg });
166
+ // Another session changed the password first: what this one proved is
167
+ // no longer current, and nothing was written.
168
+ if (error instanceof PasswordChangedMeanwhileError) {
169
+ res.status(409).json({ error: error.message });
170
+ return;
171
+ }
172
+ // A refusal the service made on purpose — a policy violation, a wrong
173
+ // current password, an account whose password is not kept here — is
174
+ // the caller's to fix, and its words are written for them.
175
+ if (error instanceof PasswordRefusedError) {
176
+ res.status(400).json({ error: error.message });
177
+ return;
178
+ }
179
+ // Anything else is a fault, not a verdict: logged in full, answered
180
+ // generic, so a driver's message never reaches a browser.
181
+ log.error('Change-password error:', { err: error });
182
+ res.status(500).json({ error: 'Could not change the password' });
168
183
  }
169
184
  });
170
185
 
@@ -216,8 +231,10 @@ export function createAuthRoutes(
216
231
  }
217
232
  res.json(user);
218
233
  } catch (error) {
219
- const msg = error instanceof Error ? error.message : 'Unknown error';
220
- res.status(500).json({ error: msg });
234
+ // Logged in full, returned generic — the rule every route here follows:
235
+ // a raw driver message would hand the caller the schema or the host.
236
+ log.error('Auth me error:', { err: error });
237
+ res.status(500).json({ error: 'Could not load your account' });
221
238
  }
222
239
  });
223
240