@bevel-software/platform-core-backend 0.22.0 → 0.23.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 (325) hide show
  1. package/dist/core/core-ports.d.ts +11 -1
  2. package/dist/core/core-ports.d.ts.map +1 -1
  3. package/dist/core/core-ports.js.map +1 -1
  4. package/dist/core/create-core-server.d.ts +3 -3
  5. package/dist/core/create-core-server.d.ts.map +1 -1
  6. package/dist/core/create-core-server.js +30 -11
  7. package/dist/core/create-core-server.js.map +1 -1
  8. package/dist/core/create-core-services.d.ts +9 -2
  9. package/dist/core/create-core-services.d.ts.map +1 -1
  10. package/dist/core/create-core-services.js +112 -23
  11. package/dist/core/create-core-services.js.map +1 -1
  12. package/dist/core/lifecycle.d.ts +34 -1
  13. package/dist/core/lifecycle.d.ts.map +1 -1
  14. package/dist/core/lifecycle.js +89 -13
  15. package/dist/core/lifecycle.js.map +1 -1
  16. package/dist/core-config.d.ts +0 -12
  17. package/dist/core-config.d.ts.map +1 -1
  18. package/dist/core-config.js +11 -13
  19. package/dist/core-config.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/modules/access/access-control.interface.d.ts +42 -12
  25. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  26. package/dist/modules/access/access-control.service.d.ts +67 -10
  27. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  28. package/dist/modules/access/access-control.service.js +214 -35
  29. package/dist/modules/access/access-control.service.js.map +1 -1
  30. package/dist/modules/access/access.routes.d.ts.map +1 -1
  31. package/dist/modules/access/access.routes.js +13 -15
  32. package/dist/modules/access/access.routes.js.map +1 -1
  33. package/dist/modules/auth/account-admission.d.ts +58 -7
  34. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  35. package/dist/modules/auth/account-admission.js +44 -1
  36. package/dist/modules/auth/account-admission.js.map +1 -1
  37. package/dist/modules/auth/account.routes.d.ts +1 -1
  38. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  39. package/dist/modules/auth/account.routes.js +61 -5
  40. package/dist/modules/auth/account.routes.js.map +1 -1
  41. package/dist/modules/auth/auth.middleware.d.ts +1 -1
  42. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  43. package/dist/modules/auth/auth.middleware.js +18 -7
  44. package/dist/modules/auth/auth.middleware.js.map +1 -1
  45. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  46. package/dist/modules/auth/auth.routes.js +8 -0
  47. package/dist/modules/auth/auth.routes.js.map +1 -1
  48. package/dist/modules/auth/auth.service.d.ts +70 -2
  49. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  50. package/dist/modules/auth/auth.service.js +176 -7
  51. package/dist/modules/auth/auth.service.js.map +1 -1
  52. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  53. package/dist/modules/auth/oidc-auth-provider.js +7 -2
  54. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  55. package/dist/modules/database/core-schema.d.ts +41 -24
  56. package/dist/modules/database/core-schema.d.ts.map +1 -1
  57. package/dist/modules/database/core-schema.js +35 -27
  58. package/dist/modules/database/core-schema.js.map +1 -1
  59. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  60. package/dist/modules/kb-fs/branch-name.js +12 -2
  61. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  62. package/dist/modules/kb-fs/repo-path.d.ts +11 -16
  63. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  64. package/dist/modules/kb-fs/repo-path.js +79 -0
  65. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  66. package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
  67. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  68. package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
  69. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  70. package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
  71. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
  72. package/dist/modules/kb-sync/sync-auth.js +1 -1
  73. package/dist/modules/kb-sync/sync-auth.js.map +1 -1
  74. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  75. package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
  76. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  77. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  78. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
  79. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  80. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  81. package/dist/modules/settings/deployment-settings.service.js +8 -3
  82. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  83. package/dist/modules/settings/setup.routes.d.ts +52 -1
  84. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  85. package/dist/modules/settings/setup.routes.js +207 -19
  86. package/dist/modules/settings/setup.routes.js.map +1 -1
  87. package/dist/modules/skills/skills.contract.d.ts +90 -18
  88. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  89. package/dist/modules/skills/skills.contract.js +4 -1
  90. package/dist/modules/skills/skills.contract.js.map +1 -1
  91. package/dist/modules/skills/skills.service.d.ts +98 -9
  92. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  93. package/dist/modules/skills/skills.service.js +239 -37
  94. package/dist/modules/skills/skills.service.js.map +1 -1
  95. package/dist/modules/skills/skills.tools.d.ts +7 -0
  96. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  97. package/dist/modules/skills/skills.tools.js +71 -7
  98. package/dist/modules/skills/skills.tools.js.map +1 -1
  99. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  100. package/dist/modules/tool-auth/external-api-key.service.js +4 -2
  101. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  102. package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
  103. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
  104. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  105. package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
  106. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  107. package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
  108. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  109. package/dist/modules/tool-helpers/tool-handler.js +25 -4
  110. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  111. package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
  112. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  113. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  114. package/dist/modules/tool-helpers/validate-token.js +1 -1
  115. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  116. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
  117. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
  118. package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
  119. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
  120. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  121. package/dist/modules/workflow/agent-tools/workflow.tools.js +75 -35
  122. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  123. package/dist/modules/workflow/file-lock.service.d.ts +24 -0
  124. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  125. package/dist/modules/workflow/file-lock.service.js +30 -0
  126. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  127. package/dist/modules/workflow/git/git.service.d.ts +69 -0
  128. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  129. package/dist/modules/workflow/git/git.service.js +153 -2
  130. package/dist/modules/workflow/git/git.service.js.map +1 -1
  131. package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
  132. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  133. package/dist/modules/workflow/pending-commits.service.js +55 -0
  134. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  135. package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
  136. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
  137. package/dist/modules/workflow/workflow-hooks.js +16 -1
  138. package/dist/modules/workflow/workflow-hooks.js.map +1 -1
  139. package/dist/modules/workflow/workflow.service.d.ts +60 -6
  140. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  141. package/dist/modules/workflow/workflow.service.js +111 -4
  142. package/dist/modules/workflow/workflow.service.js.map +1 -1
  143. package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
  144. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
  145. package/dist/modules/workspace/agent-access.gate.js +123 -0
  146. package/dist/modules/workspace/agent-access.gate.js.map +1 -0
  147. package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
  148. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
  149. package/dist/modules/workspace/routine-write-policy.js +5 -6
  150. package/dist/modules/workspace/routine-write-policy.js.map +1 -1
  151. package/dist/modules/workspace/session-sink.d.ts +5 -5
  152. package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
  153. package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
  154. package/dist/modules/workspace/set-aside-clone.js +92 -0
  155. package/dist/modules/workspace/set-aside-clone.js.map +1 -0
  156. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +59 -9
  157. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  158. package/dist/modules/workspace/startup/kb-startup-runner.js +65 -24
  159. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  160. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  161. package/dist/modules/workspace/workspace.routes.js +92 -3
  162. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  163. package/dist/modules/workspace/workspace.service.d.ts +115 -5
  164. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  165. package/dist/modules/workspace/workspace.service.js +255 -25
  166. package/dist/modules/workspace/workspace.service.js.map +1 -1
  167. package/dist/modules/workspace/workspace.tools.d.ts +2 -2
  168. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  169. package/dist/modules/workspace/workspace.tools.js +309 -115
  170. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  171. package/dist/modules/write-access/write-access.d.ts +60 -0
  172. package/dist/modules/write-access/write-access.d.ts.map +1 -0
  173. package/dist/modules/write-access/write-access.js +129 -0
  174. package/dist/modules/write-access/write-access.js.map +1 -0
  175. package/dist/shared/domain-errors.d.ts +25 -0
  176. package/dist/shared/domain-errors.d.ts.map +1 -1
  177. package/dist/shared/domain-errors.js +28 -0
  178. package/dist/shared/domain-errors.js.map +1 -1
  179. package/dist/shared/git.contract.d.ts +20 -0
  180. package/dist/shared/git.contract.d.ts.map +1 -1
  181. package/dist/shared/git.contract.js +26 -0
  182. package/dist/shared/git.contract.js.map +1 -1
  183. package/dist/tenancy/static-tenant-source.d.ts +0 -1
  184. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  185. package/dist/tenancy/static-tenant-source.js +0 -2
  186. package/dist/tenancy/static-tenant-source.js.map +1 -1
  187. package/migrations/0014_change_request_closed_reason.sql +1 -0
  188. package/migrations/0015_account_deactivation.sql +3 -0
  189. package/migrations/meta/0014_snapshot.json +2265 -0
  190. package/migrations/meta/0015_snapshot.json +2271 -0
  191. package/migrations/meta/_journal.json +14 -0
  192. package/package.json +3 -3
  193. package/src/__tests__/kb-layout-config.test.ts +0 -2
  194. package/src/__tests__/retired-settings.test.ts +96 -0
  195. package/src/core/__tests__/gated-boot.test.ts +132 -0
  196. package/src/core/__tests__/lifecycle.test.ts +170 -1
  197. package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
  198. package/src/core/core-ports.ts +11 -1
  199. package/src/core/create-core-server.ts +32 -14
  200. package/src/core/create-core-services.ts +129 -23
  201. package/src/core/lifecycle.ts +98 -13
  202. package/src/core-config.ts +13 -14
  203. package/src/index.ts +12 -1
  204. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
  205. package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
  206. package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
  207. package/src/modules/access/access-control.interface.ts +43 -12
  208. package/src/modules/access/access-control.service.ts +234 -36
  209. package/src/modules/access/access.routes.ts +13 -15
  210. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +3 -1
  211. package/src/modules/auth/__tests__/account-deactivation.test.ts +252 -0
  212. package/src/modules/auth/__tests__/account.routes.test.ts +81 -3
  213. package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
  214. package/src/modules/auth/__tests__/auth.service.test.ts +1 -1
  215. package/src/modules/auth/account-admission.ts +78 -9
  216. package/src/modules/auth/account.routes.ts +63 -7
  217. package/src/modules/auth/auth.middleware.ts +19 -8
  218. package/src/modules/auth/auth.routes.ts +8 -0
  219. package/src/modules/auth/auth.service.ts +179 -6
  220. package/src/modules/auth/oidc-auth-provider.ts +7 -2
  221. package/src/modules/database/core-schema.ts +35 -27
  222. package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
  223. package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
  224. package/src/modules/kb-fs/branch-name.ts +14 -1
  225. package/src/modules/kb-fs/repo-path.ts +85 -0
  226. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
  227. package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
  228. package/src/modules/kb-sync/sync-auth.ts +2 -2
  229. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
  230. package/src/modules/mcp/__tests__/mcp.service.test.ts +68 -6
  231. package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
  232. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
  233. package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
  234. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
  235. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +91 -58
  236. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +25 -8
  237. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +14 -11
  238. package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
  239. package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
  240. package/src/modules/settings/deployment-settings.service.ts +8 -3
  241. package/src/modules/settings/setup.routes.ts +263 -19
  242. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
  243. package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
  244. package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
  245. package/src/modules/skills/skills.contract.ts +91 -18
  246. package/src/modules/skills/skills.service.ts +278 -41
  247. package/src/modules/skills/skills.tools.ts +80 -8
  248. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
  249. package/src/modules/tool-auth/external-api-key.service.ts +4 -2
  250. package/src/modules/tool-auth/internal-token.service.ts +3 -3
  251. package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
  252. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
  253. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
  254. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
  255. package/src/modules/tool-helpers/tool-handler.ts +24 -4
  256. package/src/modules/tool-helpers/tool.contract.ts +3 -2
  257. package/src/modules/tool-helpers/validate-token.ts +1 -1
  258. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
  259. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
  260. package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
  261. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +264 -6
  262. package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
  263. package/src/modules/workflow/agent-tools/workflow.tools.ts +86 -35
  264. package/src/modules/workflow/file-lock.service.ts +31 -0
  265. package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
  266. package/src/modules/workflow/git/git.service.ts +168 -0
  267. package/src/modules/workflow/pending-commits.service.ts +59 -1
  268. package/src/modules/workflow/workflow-hooks.ts +64 -26
  269. package/src/modules/workflow/workflow.service.ts +121 -4
  270. package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
  271. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +384 -0
  272. package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
  273. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
  274. package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
  275. package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
  276. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
  277. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
  278. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +326 -0
  279. package/src/modules/workspace/__tests__/workspace.service.test.ts +20 -9
  280. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +2 -3
  281. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +2 -3
  282. package/src/modules/workspace/__tests__/workspace.tools.test.ts +685 -14
  283. package/src/modules/workspace/agent-access.gate.ts +164 -0
  284. package/src/modules/workspace/routine-write-policy.ts +5 -6
  285. package/src/modules/workspace/session-sink.ts +5 -5
  286. package/src/modules/workspace/set-aside-clone.ts +96 -0
  287. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +86 -0
  288. package/src/modules/workspace/startup/kb-startup-runner.ts +115 -34
  289. package/src/modules/workspace/workspace.routes.ts +95 -3
  290. package/src/modules/workspace/workspace.service.ts +256 -24
  291. package/src/modules/workspace/workspace.tools.ts +338 -119
  292. package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
  293. package/src/modules/write-access/write-access.ts +153 -0
  294. package/src/shared/domain-errors.ts +31 -0
  295. package/src/shared/git.contract.ts +27 -0
  296. package/src/tenancy/__tests__/static-tenant-source.test.ts +0 -1
  297. package/src/tenancy/static-tenant-source.ts +0 -3
  298. package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
  299. package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
  300. package/dist/modules/workflow/session-ontology.policy.js +0 -62
  301. package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
  302. package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
  303. package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
  304. package/dist/modules/workflow/session-ontology.service.js +0 -147
  305. package/dist/modules/workflow/session-ontology.service.js.map +0 -1
  306. package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
  307. package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
  308. package/dist/modules/workspace/session-ontology.gate.js +0 -161
  309. package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
  310. package/dist/shared/kb-layout.d.ts +0 -39
  311. package/dist/shared/kb-layout.d.ts.map +0 -1
  312. package/dist/shared/kb-layout.js +0 -103
  313. package/dist/shared/kb-layout.js.map +0 -1
  314. package/dist/shared/kb-layout.test.d.ts +0 -2
  315. package/dist/shared/kb-layout.test.d.ts.map +0 -1
  316. package/dist/shared/kb-layout.test.js +0 -75
  317. package/dist/shared/kb-layout.test.js.map +0 -1
  318. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
  319. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
  320. package/src/modules/workflow/session-ontology.policy.ts +0 -70
  321. package/src/modules/workflow/session-ontology.service.ts +0 -183
  322. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
  323. package/src/modules/workspace/session-ontology.gate.ts +0 -191
  324. package/src/shared/kb-layout.test.ts +0 -98
  325. package/src/shared/kb-layout.ts +0 -102
@@ -1,14 +1,13 @@
1
- import type { ValidationReport } from '@bevel-software/platform-shared';
1
+ import type { AuthUser, ValidationReport } from '@bevel-software/platform-shared';
2
2
 
3
3
  /**
4
4
  * Workflow lifecycle hooks — the generic seam that replaced the
5
- * constructor-injected KB validator (and the core-owned ontology write block).
6
- * Core code invokes the hooks at fixed lifecycle points; the modules that OWN
7
- * the behavior (the enterprise kb/kb-graph modules today) register handlers in
8
- * the composition root, right after `createCoreServices` returns. Core
9
- * registers none, so a core-only deployment runs every hook point as a no-op.
5
+ * constructor-injected KB validator. Core code invokes the hooks at fixed
6
+ * lifecycle points; the modules that OWN the behavior register handlers in the
7
+ * composition root, right after `createCoreServices` returns. Core registers
8
+ * none, so a core-only deployment runs every hook point as a no-op.
10
9
  *
11
- * Two hook kinds with deliberately different failure semantics:
10
+ * Three hook kinds, with deliberately different failure semantics:
12
11
  *
13
12
  * - `commitValidation` — ADVISORY. Runs at commit time in `GitService`
14
13
  * (`commit` / `commitFile`), exactly where the injected `IKbValidator`
@@ -18,12 +17,18 @@ import type { ValidationReport } from '@bevel-software/platform-shared';
18
17
  * `runValidation(workspaceId)` shape so `KbValidatorService` registers
19
18
  * with a one-line adapter.
20
19
  *
21
- * - `preWrite` — BLOCKING. Runs before a gated agent write (file-write
22
- * tools) or a write-capable shell command; a hook that throws REJECTS the
23
- * operation (the error propagates to the tool caller). This is where the
24
- * enterprise ontology-session block registers; core registers nothing, so
25
- * core never blocks (touch TRACKING stays core — see
26
- * `session-ontology.gate.ts`).
20
+ * - `agentRead` — BLOCKING. Runs before a gated agent READ of a
21
+ * knowledge-base path (reads, `delete_file`, `delete_folder`, and each
22
+ * file a `grep` walk opens). A hook that throws REFUSES the read.
23
+ *
24
+ * - `preWrite` — BLOCKING. Runs before a gated agent WRITE (the file-write
25
+ * tools, once per path) or a write-capable shell command, which carries no
26
+ * path. A hook that throws REFUSES the operation, and the caller sees the
27
+ * hook's own message and status.
28
+ *
29
+ * The two blocking hooks are the seam a deployment builds a per-conversation
30
+ * rule on: they carry the session id the call named, and core itself neither
31
+ * decides nor records anything (see `workspace/agent-access.gate.ts`).
27
32
  */
28
33
 
29
34
  /** What `GitService` knows at the advisory commit-validation point. */
@@ -47,19 +52,35 @@ export type CommitValidationHook = (
47
52
  ) => Promise<ValidationReport | void>;
48
53
 
49
54
  /**
50
- * What the pre-write gate knows when it consults the blocking hooks. The core
51
- * gate has already resolved all skip conditions (boundary flag off, neutral
52
- * path, non-agent caller, recovery bot, missing session id — see
53
- * `resolveGatedSession`), so by hook time there is always a session id.
55
+ * What an agent operation looks like to the blocking hooks. The core gate has
56
+ * already resolved the only two skips it owns (the caller is not an agent, or
57
+ * is the recovery bot), so every hook call describes a real agent operation.
58
+ *
59
+ * `sessionId` is present only when the call carried one — core does NOT fail
60
+ * closed on its absence, because whether a path needs a session is the
61
+ * registering deployment's rule, not core's.
54
62
  */
55
- export type PreWriteContext =
56
- /** A path-addressed content write (write_file/edit_file/mkdir/move/copy/unzip…). */
57
- | { kind: 'file-write'; sessionId: string; wsPath: string }
58
- /** A write-capable operation with no resolvable target path (execute_command). */
59
- | { kind: 'shell'; sessionId: string };
63
+ export interface AgentOperationContext {
64
+ /** The conversation the call belongs to, when it named one. */
65
+ sessionId?: string;
66
+ /**
67
+ * The workspace-relative path the operation targets. Absent for a
68
+ * write-capable operation with no resolvable target (`execute_command`).
69
+ */
70
+ wsPath?: string;
71
+ /** The branch (draft) whose workspace the operation acts on. */
72
+ branch: string;
73
+ /** The calling user. */
74
+ user: AuthUser;
75
+ /** Whether the caller is the in-app agent (`internal`) or an external one. */
76
+ source: 'internal' | 'external';
77
+ }
60
78
 
61
- /** Blocking pre-write hook: throw to reject the write; return to allow. */
62
- export type PreWriteHook = (ctx: PreWriteContext) => Promise<void>;
79
+ /** Blocking pre-read hook: throw to refuse the read; return to allow. */
80
+ export type AgentReadHook = (ctx: AgentOperationContext) => Promise<void>;
81
+
82
+ /** Blocking pre-write hook: throw to refuse the write; return to allow. */
83
+ export type PreWriteHook = (ctx: AgentOperationContext) => Promise<void>;
63
84
 
64
85
  /**
65
86
  * The registry instance. ONE per composition (created in
@@ -68,6 +89,7 @@ export type PreWriteHook = (ctx: PreWriteContext) => Promise<void>;
68
89
  */
69
90
  export class WorkflowHooks {
70
91
  private readonly commitValidation: CommitValidationHook[] = [];
92
+ private readonly agentRead: AgentReadHook[] = [];
71
93
  private readonly preWrite: PreWriteHook[] = [];
72
94
 
73
95
  /** Register an advisory commit-time validation hook. */
@@ -75,6 +97,11 @@ export class WorkflowHooks {
75
97
  this.commitValidation.push(hook);
76
98
  }
77
99
 
100
+ /** Register a blocking pre-read hook. */
101
+ onAgentRead(hook: AgentReadHook): void {
102
+ this.agentRead.push(hook);
103
+ }
104
+
78
105
  /** Register a blocking pre-write hook. */
79
106
  onPreWrite(hook: PreWriteHook): void {
80
107
  this.preWrite.push(hook);
@@ -88,12 +115,23 @@ export class WorkflowHooks {
88
115
  return this.commitValidation;
89
116
  }
90
117
 
118
+ /**
119
+ * Run every blocking pre-read hook in registration order. The first throw
120
+ * propagates and refuses the read; with no hooks registered (core-only)
121
+ * this resolves immediately.
122
+ */
123
+ async runAgentRead(ctx: AgentOperationContext): Promise<void> {
124
+ for (const hook of this.agentRead) {
125
+ await hook(ctx);
126
+ }
127
+ }
128
+
91
129
  /**
92
130
  * Run every blocking pre-write hook in registration order. The first throw
93
- * propagates and rejects the write; with no hooks registered (core-only)
131
+ * propagates and refuses the write; with no hooks registered (core-only)
94
132
  * this resolves immediately.
95
133
  */
96
- async runPreWrite(ctx: PreWriteContext): Promise<void> {
134
+ async runPreWrite(ctx: AgentOperationContext): Promise<void> {
97
135
  for (const hook of this.preWrite) {
98
136
  await hook(ctx);
99
137
  }
@@ -63,7 +63,7 @@ import {
63
63
  isPlatformRestoreShape,
64
64
  } from '@bevel-software/platform-shared';
65
65
  import type { KbContext } from '../../shared/kb-context.js';
66
- import { and, desc, eq, inArray, isNotNull, isNull, lt, ne, or } from 'drizzle-orm';
66
+ import { and, desc, eq, inArray, isNotNull, isNull, lt, ne, or, sql } from 'drizzle-orm';
67
67
  import type { Database } from '../database/connection.js';
68
68
  import { changeRequests } from '../database/schema.js';
69
69
  import type { GitService } from './git/git.service.js';
@@ -324,9 +324,9 @@ export class WorkflowService implements IWorkflowService {
324
324
  * the composition root registers module-owned behavior against it
325
325
  * (`core.workflowService.hooks.onCommitValidation(…)` /
326
326
  * `.onPreWrite(…)`) after `createCoreServices` returns. The composition
327
- * root passes the SAME instance `GitService` and the session-ontology
328
- * gate consult; the default keeps test constructions (which don't
329
- * exercise hooks) unchanged.
327
+ * root passes the SAME instance `GitService` and the agent-access gate
328
+ * consult; the default keeps test constructions (which don't exercise
329
+ * hooks) unchanged.
330
330
  */
331
331
  public readonly hooks: WorkflowHooks = new WorkflowHooks(),
332
332
  ) {
@@ -910,6 +910,22 @@ export class WorkflowService implements IWorkflowService {
910
910
  return this.git.fileContentsAtCommit(workspaceId, path, sha);
911
911
  }
912
912
 
913
+ /**
914
+ * The BYTES of one file at a change — what the file page's byte-reading
915
+ * viewers (pdf, image, docx, pptx, xlsx, email) need to show a past save,
916
+ * and what "Download this version" hands over. `fileAtChange` above cannot
917
+ * serve them: it decodes to UTF-8, which ruins every format that is not
918
+ * text.
919
+ */
920
+ fileBytesAtChange(
921
+ workspaceId: string,
922
+ path: string,
923
+ sha: string,
924
+ side: 'after' | 'before',
925
+ ): Promise<{ bytes: Buffer; blobId: string } | null> {
926
+ return this.git.fileBytesAtCommit(workspaceId, path, sha, side);
927
+ }
928
+
913
929
  /**
914
930
  * Resolve write permission for `targetPath` against the access tree at
915
931
  * `HEAD` of `branch`. Throws `AccessDeniedError` on denial with the
@@ -3024,6 +3040,107 @@ export class WorkflowService implements IWorkflowService {
3024
3040
  return closed;
3025
3041
  }
3026
3042
 
3043
+ /**
3044
+ * How many change requests are open right now.
3045
+ *
3046
+ * Read by the setup screen's repository-change confirmation, which has to
3047
+ * say how many requests the admin is deciding about BEFORE the address is
3048
+ * saved. A plain count — nothing here reaches the repository, so it answers
3049
+ * just as well for a repository that is already gone.
3050
+ */
3051
+ async countOpenChangeRequests(): Promise<number> {
3052
+ const [row] = await this.db
3053
+ .select({ open: sql<number>`count(*)::int` })
3054
+ .from(changeRequests)
3055
+ .where(eq(changeRequests.state, 'open'));
3056
+ return row?.open ?? 0;
3057
+ }
3058
+
3059
+ /**
3060
+ * Close every open change request because the knowledge-base REPOSITORY was
3061
+ * replaced, and answer how many were closed.
3062
+ *
3063
+ * The admin's own choice on the setup screen: a repository that merely
3064
+ * MOVED keeps its requests (the branches came along), while a repository
3065
+ * that was REPLACED has none of those branches, so every request points at
3066
+ * something this deployment can no longer fetch. Nothing is deleted — the
3067
+ * rows stay, closed, carrying `closed_reason = 'repository-replaced'` so
3068
+ * the reason survives the person who chose it.
3069
+ *
3070
+ * Locks held on those branches go with them. Their holder cannot publish
3071
+ * the bytes (the branch is not in the new repository) and cannot release
3072
+ * the lock by finishing (the request is closed), so the row would sit there
3073
+ * refusing the path until its TTL ran out — and a still-connected client's
3074
+ * heartbeat keeps pushing that out.
3075
+ *
3076
+ * The close is ONE statement per pass — `update … where state = 'open'
3077
+ * returning` — not a count followed by a row-at-a-time loop. Read first and
3078
+ * then written, every request opened while the loop ran would be missed and
3079
+ * left open against a branch the new repository does not have. One
3080
+ * set-shaped statement cannot miss a row that exists when it runs, and the
3081
+ * guard is the statement's own `where`, so a merge or a withdrawal racing it
3082
+ * wins and its request is simply not in the returned set.
3083
+ *
3084
+ * Repeated while a pass still finds something, because a request can be
3085
+ * opened BETWEEN passes: the caller has not saved the new address yet, so
3086
+ * the deployment is still live and `POST /change-requests` is still being
3087
+ * served. Bounded, so a client opening requests in a loop cannot hold the
3088
+ * save open forever — what survives the last pass is a request opened after
3089
+ * the repository was replaced, which the deleted-branch sweep closes on its
3090
+ * own terms.
3091
+ */
3092
+ async closeOpenChangeRequestsAsRepositoryReplaced(): Promise<number> {
3093
+ let closed = 0;
3094
+ for (let pass = 0; pass < 5; pass++) {
3095
+ const now = new Date();
3096
+ const justClosed = await this.db
3097
+ .update(changeRequests)
3098
+ .set({ state: 'closed', closedAt: now, updatedAt: now, closedReason: 'repository-replaced' })
3099
+ .where(eq(changeRequests.state, 'open'))
3100
+ .returning({ number: changeRequests.number, sourceBranch: changeRequests.sourceBranch });
3101
+ if (justClosed.length === 0) break;
3102
+ for (const cr of justClosed) {
3103
+ // Best effort, per branch: a lock table that will not answer must not
3104
+ // leave the requests half closed — the close is the decision the admin
3105
+ // made, and the locks expire on their own within the minute.
3106
+ try {
3107
+ const released = await this.fileLocks.releaseAllOnBranch(cr.sourceBranch);
3108
+ if (released > 0) {
3109
+ crLog.info(`released ${released} file lock(s) held on "${cr.sourceBranch}"`);
3110
+ }
3111
+ } catch (err) {
3112
+ crLog.warn(`could not release the file locks on "${cr.sourceBranch}":`, { err });
3113
+ }
3114
+ // The bytes a release already enqueued belong to the repository that
3115
+ // is gone — see `markNeedsAttentionOnBranch`. Taken out of the
3116
+ // worker's reach in the same breath as the locks, because dropping a
3117
+ // lock is precisely what leaves a row behind.
3118
+ try {
3119
+ const quarantined = await this.pendingCommits.markNeedsAttentionOnBranch(
3120
+ cr.sourceBranch,
3121
+ 'The knowledge-base repository was replaced while this commit was still queued, so it was never ' +
3122
+ 'written. The bytes are kept here: the branch it was meant for belongs to the previous repository.',
3123
+ );
3124
+ if (quarantined > 0) {
3125
+ crLog.warn(
3126
+ `${quarantined} queued commit(s) on "${cr.sourceBranch}" need attention: the repository was replaced ` +
3127
+ 'before they landed, so they were not written to the new one.',
3128
+ );
3129
+ }
3130
+ } catch (err) {
3131
+ crLog.warn(`could not set aside the queued commits on "${cr.sourceBranch}":`, { err });
3132
+ }
3133
+ this.prs.invalidateDetailCache(cr.number);
3134
+ this.events?.emit({ kind: 'change-request-rejected', number: cr.number });
3135
+ closed++;
3136
+ }
3137
+ }
3138
+ if (closed > 0) {
3139
+ crLog.info(`closed ${closed} change request(s): the knowledge-base repository was replaced`);
3140
+ }
3141
+ return closed;
3142
+ }
3143
+
3027
3144
  /**
3028
3145
  * The sweep above, on demand. Boot runs it once (`create-core-server`),
3029
3146
  * which clears requests stranded BEFORE the process started; a branch that
@@ -0,0 +1,208 @@
1
+ import { describe, expect, it, beforeEach } from 'vitest';
2
+ import {
3
+ notifyAgentRead,
4
+ assertAgentWriteAllowed,
5
+ SESSION_ID_DESCRIPTION,
6
+ SESSION_ID_INPUT,
7
+ ToolDescriptionNotes,
8
+ type AgentAccessGate,
9
+ } from '../agent-access.gate.js';
10
+ import { WorkflowHooks, type AgentOperationContext } from '../../workflow/workflow-hooks.js';
11
+ import { ToolError, type ToolContext } from '../../tool-helpers/tool.contract.js';
12
+
13
+ const RECOVERY_EMAIL = 'recovery-bot@bevel.local';
14
+ const P_FILE = 'knowledge-base/KnowledgeBase/Product/Knowledge/Roadmap.md';
15
+
16
+ function ctx(over: Partial<ToolContext> = {}): ToolContext {
17
+ return {
18
+ user: { id: 'u1', email: 'a@b.c', name: 'A' },
19
+ scope: 'write',
20
+ source: 'external',
21
+ sessionId: 's1',
22
+ abortSignal: new AbortController().signal,
23
+ workspaceService: {} as never,
24
+ workflowService: {} as never,
25
+ events: {} as never,
26
+ getFilesystem: async () => ({}) as never,
27
+ ...over,
28
+ } as ToolContext;
29
+ }
30
+
31
+ /** A gate with a fresh hook registry, plus the two lists of what the hooks saw. */
32
+ function makeGate(): { gate: AgentAccessGate; reads: AgentOperationContext[]; writes: AgentOperationContext[] } {
33
+ const hooks = new WorkflowHooks();
34
+ const reads: AgentOperationContext[] = [];
35
+ const writes: AgentOperationContext[] = [];
36
+ hooks.onAgentRead(async (op) => {
37
+ reads.push(op);
38
+ });
39
+ hooks.onPreWrite(async (op) => {
40
+ writes.push(op);
41
+ });
42
+ return { gate: { recoveryBotEmail: RECOVERY_EMAIL, hooks, notes: new ToolDescriptionNotes() }, reads, writes };
43
+ }
44
+
45
+ describe('notifyAgentRead', () => {
46
+ it('calls the read hook once with the session, path, branch, user and source', async () => {
47
+ const { gate, reads, writes } = makeGate();
48
+ await notifyAgentRead(gate, ctx(), 'draft-1', P_FILE);
49
+ expect(reads).toEqual([
50
+ {
51
+ sessionId: 's1',
52
+ wsPath: P_FILE,
53
+ branch: 'draft-1',
54
+ user: { id: 'u1', email: 'a@b.c', name: 'A' },
55
+ source: 'external',
56
+ },
57
+ ]);
58
+ expect(writes).toEqual([]);
59
+ });
60
+
61
+ it('carries the in-app agent as source `internal`', async () => {
62
+ const { gate, reads } = makeGate();
63
+ await notifyAgentRead(gate, ctx({ source: 'internal' }), 'main', P_FILE);
64
+ expect(reads[0].source).toBe('internal');
65
+ });
66
+
67
+ it('calls the hook with no session id when the call carried none — the hook decides', async () => {
68
+ const { gate, reads } = makeGate();
69
+ await notifyAgentRead(gate, ctx({ sessionId: undefined }), 'main', P_FILE);
70
+ expect(reads).toHaveLength(1);
71
+ expect(reads[0].sessionId).toBeUndefined();
72
+ });
73
+
74
+ it('never reaches the hooks for a caller that is not an agent', async () => {
75
+ const { gate, reads } = makeGate();
76
+ await notifyAgentRead(gate, ctx({ source: 'session' }), 'main', P_FILE);
77
+ expect(reads).toEqual([]);
78
+ });
79
+
80
+ it('never reaches the hooks for the recovery bot, whatever the case of its address', async () => {
81
+ const { gate, reads } = makeGate();
82
+ await notifyAgentRead(gate, ctx({ user: { id: 'bot', email: 'Recovery-Bot@Bevel.local', name: 'bot' } }), 'main', P_FILE);
83
+ expect(reads).toEqual([]);
84
+ });
85
+
86
+ it('refuses the read with the hook\'s own message and status', async () => {
87
+ const hooks = new WorkflowHooks();
88
+ hooks.onAgentRead(async () => {
89
+ throw new ToolError('Not in this conversation.', 403);
90
+ });
91
+ const gate: AgentAccessGate = { recoveryBotEmail: RECOVERY_EMAIL, hooks, notes: new ToolDescriptionNotes() };
92
+ await expect(notifyAgentRead(gate, ctx(), 'main', P_FILE)).rejects.toMatchObject({
93
+ message: 'Not in this conversation.',
94
+ status: 403,
95
+ });
96
+ });
97
+
98
+ it('is a no-op with no hook registered', async () => {
99
+ const gate: AgentAccessGate = {
100
+ recoveryBotEmail: RECOVERY_EMAIL,
101
+ hooks: new WorkflowHooks(),
102
+ notes: new ToolDescriptionNotes(),
103
+ };
104
+ await expect(notifyAgentRead(gate, ctx({ sessionId: undefined }), 'main', P_FILE)).resolves.toBeUndefined();
105
+ });
106
+ });
107
+
108
+ describe('assertAgentWriteAllowed', () => {
109
+ it('calls the write hook with the path, and never the read hook', async () => {
110
+ const { gate, reads, writes } = makeGate();
111
+ await assertAgentWriteAllowed(gate, ctx(), 'draft-1', P_FILE);
112
+ expect(reads).toEqual([]);
113
+ expect(writes).toHaveLength(1);
114
+ expect(writes[0]).toMatchObject({ sessionId: 's1', wsPath: P_FILE, branch: 'draft-1', source: 'external' });
115
+ });
116
+
117
+ it('calls the write hook with NO path for a write-capable call that has none', async () => {
118
+ const { gate, writes } = makeGate();
119
+ await assertAgentWriteAllowed(gate, ctx(), 'main');
120
+ expect(writes).toHaveLength(1);
121
+ expect(writes[0].wsPath).toBeUndefined();
122
+ expect(writes[0].branch).toBe('main');
123
+ });
124
+
125
+ it('refuses the write with the hook\'s own message and status', async () => {
126
+ const hooks = new WorkflowHooks();
127
+ hooks.onPreWrite(async () => {
128
+ throw new ToolError('Not in this conversation.', 403);
129
+ });
130
+ const gate: AgentAccessGate = { recoveryBotEmail: RECOVERY_EMAIL, hooks, notes: new ToolDescriptionNotes() };
131
+ await expect(assertAgentWriteAllowed(gate, ctx(), 'main', P_FILE)).rejects.toMatchObject({
132
+ message: 'Not in this conversation.',
133
+ status: 403,
134
+ });
135
+ });
136
+
137
+ it('runs every registered write hook, in registration order, until one throws', async () => {
138
+ const hooks = new WorkflowHooks();
139
+ const seen: string[] = [];
140
+ hooks.onPreWrite(async () => {
141
+ seen.push('first');
142
+ });
143
+ hooks.onPreWrite(async () => {
144
+ seen.push('second');
145
+ throw new ToolError('no', 403);
146
+ });
147
+ hooks.onPreWrite(async () => {
148
+ seen.push('third');
149
+ });
150
+ const gate: AgentAccessGate = { recoveryBotEmail: RECOVERY_EMAIL, hooks, notes: new ToolDescriptionNotes() };
151
+ await expect(assertAgentWriteAllowed(gate, ctx(), 'main', P_FILE)).rejects.toThrow('no');
152
+ expect(seen).toEqual(['first', 'second']);
153
+ });
154
+
155
+ it('never reaches the hooks for a person in the app or for the recovery bot', async () => {
156
+ const { gate, writes } = makeGate();
157
+ await assertAgentWriteAllowed(gate, ctx({ source: 'session' }), 'main', P_FILE);
158
+ await assertAgentWriteAllowed(gate, ctx({ user: { id: 'bot', email: RECOVERY_EMAIL, name: 'bot' } }), 'main', P_FILE);
159
+ expect(writes).toEqual([]);
160
+ });
161
+
162
+ it('is a no-op with no hook registered, with or without a session id', async () => {
163
+ const gate: AgentAccessGate = {
164
+ recoveryBotEmail: RECOVERY_EMAIL,
165
+ hooks: new WorkflowHooks(),
166
+ notes: new ToolDescriptionNotes(),
167
+ };
168
+ await expect(assertAgentWriteAllowed(gate, ctx(), 'main', P_FILE)).resolves.toBeUndefined();
169
+ await expect(assertAgentWriteAllowed(gate, ctx({ sessionId: undefined }), 'main', P_FILE)).resolves.toBeUndefined();
170
+ });
171
+ });
172
+
173
+ describe('ToolDescriptionNotes', () => {
174
+ let notes: ToolDescriptionNotes;
175
+ beforeEach(() => {
176
+ notes = new ToolDescriptionNotes();
177
+ });
178
+
179
+ it('has no note registered by default, which is every Hexis-only deployment', () => {
180
+ expect(notes.gatedToolNote()).toBe('');
181
+ expect(notes.sessionIdDescription()).toBe(SESSION_ID_DESCRIPTION);
182
+ });
183
+
184
+ it('appends a registered gated-tool note and a registered sessionId note', () => {
185
+ notes.registerGatedToolNote(' Stay put.');
186
+ notes.registerSessionIdNote(' It also pins the boundary.');
187
+ expect(notes.gatedToolNote()).toBe(' Stay put.');
188
+ expect(notes.sessionIdDescription()).toBe(`${SESSION_ID_DESCRIPTION} It also pins the boundary.`);
189
+ });
190
+
191
+ it('tells its subscribers on every registration, so mounted tools re-describe', () => {
192
+ let calls = 0;
193
+ notes.onChange(() => {
194
+ calls++;
195
+ });
196
+ notes.registerGatedToolNote(' a');
197
+ notes.registerSessionIdNote(' b');
198
+ expect(calls).toBe(2);
199
+ });
200
+
201
+ it('the default sessionId input says what the id is, and mentions no ontology', () => {
202
+ expect(SESSION_ID_INPUT.description).toBe(SESSION_ID_DESCRIPTION);
203
+ expect(SESSION_ID_DESCRIPTION).toMatch(/conversation/i);
204
+ expect(SESSION_ID_DESCRIPTION).toMatch(/start_session/);
205
+ expect(SESSION_ID_DESCRIPTION).toMatch(/ask/);
206
+ expect(SESSION_ID_DESCRIPTION.toLowerCase()).not.toContain('ontolog');
207
+ });
208
+ });