@bevel-software/platform-core-backend 0.22.0 → 0.24.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 (466) 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 +46 -13
  7. package/dist/core/create-core-server.js.map +1 -1
  8. package/dist/core/create-core-services.d.ts +12 -2
  9. package/dist/core/create-core-services.d.ts.map +1 -1
  10. package/dist/core/create-core-services.js +137 -24
  11. package/dist/core/create-core-services.js.map +1 -1
  12. package/dist/core/lifecycle.d.ts +46 -1
  13. package/dist/core/lifecycle.d.ts.map +1 -1
  14. package/dist/core/lifecycle.js +99 -13
  15. package/dist/core/lifecycle.js.map +1 -1
  16. package/dist/core-config.d.ts +16 -12
  17. package/dist/core-config.d.ts.map +1 -1
  18. package/dist/core-config.js +28 -13
  19. package/dist/core-config.js.map +1 -1
  20. package/dist/index.d.ts +6 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +12 -3
  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 +17 -16
  32. package/dist/modules/access/access.routes.js.map +1 -1
  33. package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
  34. package/dist/modules/access/directory-sync-bot.js +7 -3
  35. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  36. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +8 -1
  37. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
  38. package/dist/modules/agent-instructions/agent-instructions.routes.js +8 -2
  39. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
  40. package/dist/modules/agent-instructions/compose.d.ts +38 -6
  41. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  42. package/dist/modules/agent-instructions/compose.js +39 -6
  43. package/dist/modules/agent-instructions/compose.js.map +1 -1
  44. package/dist/modules/agent-instructions/index.d.ts +2 -1
  45. package/dist/modules/agent-instructions/index.d.ts.map +1 -1
  46. package/dist/modules/agent-instructions/index.js +2 -1
  47. package/dist/modules/agent-instructions/index.js.map +1 -1
  48. package/dist/modules/agent-instructions/shared-file-rules.d.ts +115 -0
  49. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -0
  50. package/dist/modules/agent-instructions/shared-file-rules.js +272 -0
  51. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -0
  52. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  53. package/dist/modules/audit/agent-audit.service.js +4 -2
  54. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  55. package/dist/modules/auth/account-admission.d.ts +58 -7
  56. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  57. package/dist/modules/auth/account-admission.js +44 -1
  58. package/dist/modules/auth/account-admission.js.map +1 -1
  59. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
  60. package/dist/modules/auth/account-erasure.service.js +57 -16
  61. package/dist/modules/auth/account-erasure.service.js.map +1 -1
  62. package/dist/modules/auth/account.routes.d.ts +1 -1
  63. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  64. package/dist/modules/auth/account.routes.js +61 -5
  65. package/dist/modules/auth/account.routes.js.map +1 -1
  66. package/dist/modules/auth/auth.middleware.d.ts +1 -1
  67. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  68. package/dist/modules/auth/auth.middleware.js +18 -7
  69. package/dist/modules/auth/auth.middleware.js.map +1 -1
  70. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  71. package/dist/modules/auth/auth.routes.js +8 -0
  72. package/dist/modules/auth/auth.routes.js.map +1 -1
  73. package/dist/modules/auth/auth.service.d.ts +70 -2
  74. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  75. package/dist/modules/auth/auth.service.js +197 -18
  76. package/dist/modules/auth/auth.service.js.map +1 -1
  77. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  78. package/dist/modules/auth/oidc-auth-provider.js +7 -2
  79. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  80. package/dist/modules/code-mode/code-mode.tool.d.ts +20 -2
  81. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  82. package/dist/modules/code-mode/code-mode.tool.js +66 -35
  83. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  84. package/dist/modules/database/connection.d.ts +16 -0
  85. package/dist/modules/database/connection.d.ts.map +1 -1
  86. package/dist/modules/database/connection.js +117 -0
  87. package/dist/modules/database/connection.js.map +1 -1
  88. package/dist/modules/database/core-schema.d.ts +337 -120
  89. package/dist/modules/database/core-schema.d.ts.map +1 -1
  90. package/dist/modules/database/core-schema.js +116 -58
  91. package/dist/modules/database/core-schema.js.map +1 -1
  92. package/dist/modules/database/migrate.d.ts +8 -0
  93. package/dist/modules/database/migrate.d.ts.map +1 -1
  94. package/dist/modules/database/migrate.js +271 -1
  95. package/dist/modules/database/migrate.js.map +1 -1
  96. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  97. package/dist/modules/kb-fs/branch-name.js +12 -2
  98. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  99. package/dist/modules/kb-fs/repo-path.d.ts +11 -16
  100. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  101. package/dist/modules/kb-fs/repo-path.js +79 -0
  102. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  103. package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
  104. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
  105. package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
  106. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
  107. package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
  108. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
  109. package/dist/modules/kb-sync/sync-auth.js +1 -1
  110. package/dist/modules/kb-sync/sync-auth.js.map +1 -1
  111. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  112. package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
  113. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  114. package/dist/modules/mcp/mcp.service.d.ts +8 -0
  115. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  116. package/dist/modules/mcp/mcp.service.js +38 -8
  117. package/dist/modules/mcp/mcp.service.js.map +1 -1
  118. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  119. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
  120. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  121. package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -1
  122. package/dist/modules/plugins/join-request-records.store.js +7 -4
  123. package/dist/modules/plugins/join-request-records.store.js.map +1 -1
  124. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  125. package/dist/modules/settings/deployment-settings.service.js +8 -3
  126. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  127. package/dist/modules/settings/setup.routes.d.ts +52 -1
  128. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  129. package/dist/modules/settings/setup.routes.js +207 -19
  130. package/dist/modules/settings/setup.routes.js.map +1 -1
  131. package/dist/modules/skills/skills.contract.d.ts +90 -18
  132. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  133. package/dist/modules/skills/skills.contract.js +4 -1
  134. package/dist/modules/skills/skills.contract.js.map +1 -1
  135. package/dist/modules/skills/skills.service.d.ts +98 -9
  136. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  137. package/dist/modules/skills/skills.service.js +239 -37
  138. package/dist/modules/skills/skills.service.js.map +1 -1
  139. package/dist/modules/skills/skills.tools.d.ts +7 -0
  140. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  141. package/dist/modules/skills/skills.tools.js +71 -7
  142. package/dist/modules/skills/skills.tools.js.map +1 -1
  143. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  144. package/dist/modules/tool-auth/external-api-key.service.js +12 -4
  145. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  146. package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
  147. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
  148. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  149. package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
  150. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  151. package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
  152. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  153. package/dist/modules/tool-helpers/tool-handler.js +25 -4
  154. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  155. package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
  156. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  157. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  158. package/dist/modules/tool-helpers/validate-token.js +1 -1
  159. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  160. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  161. package/dist/modules/tool-manuals/tool-manuals.tools.js +42 -31
  162. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  163. package/dist/modules/tool-registry/description-length.d.ts +80 -0
  164. package/dist/modules/tool-registry/description-length.d.ts.map +1 -0
  165. package/dist/modules/tool-registry/description-length.js +108 -0
  166. package/dist/modules/tool-registry/description-length.js.map +1 -0
  167. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
  168. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
  169. package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
  170. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
  171. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  172. package/dist/modules/workflow/agent-tools/workflow.tools.js +75 -35
  173. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  174. package/dist/modules/workflow/file-lock.service.d.ts +24 -0
  175. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  176. package/dist/modules/workflow/file-lock.service.js +30 -0
  177. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  178. package/dist/modules/workflow/git/git.service.d.ts +94 -0
  179. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  180. package/dist/modules/workflow/git/git.service.js +193 -3
  181. package/dist/modules/workflow/git/git.service.js.map +1 -1
  182. package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
  183. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  184. package/dist/modules/workflow/pending-commits.service.js +60 -1
  185. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  186. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
  187. package/dist/modules/workflow/recovery-bot.js +7 -3
  188. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  189. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  190. package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
  191. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  192. package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
  193. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
  194. package/dist/modules/workflow/workflow-hooks.js +16 -1
  195. package/dist/modules/workflow/workflow-hooks.js.map +1 -1
  196. package/dist/modules/workflow/workflow.service.d.ts +60 -6
  197. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  198. package/dist/modules/workflow/workflow.service.js +115 -5
  199. package/dist/modules/workflow/workflow.service.js.map +1 -1
  200. package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
  201. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
  202. package/dist/modules/workspace/agent-access.gate.js +123 -0
  203. package/dist/modules/workspace/agent-access.gate.js.map +1 -0
  204. package/dist/modules/workspace/agent-upload.routes.d.ts +77 -0
  205. package/dist/modules/workspace/agent-upload.routes.d.ts.map +1 -0
  206. package/dist/modules/workspace/agent-upload.routes.js +210 -0
  207. package/dist/modules/workspace/agent-upload.routes.js.map +1 -0
  208. package/dist/modules/workspace/agent-upload.store.d.ts +284 -0
  209. package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -0
  210. package/dist/modules/workspace/agent-upload.store.js +553 -0
  211. package/dist/modules/workspace/agent-upload.store.js.map +1 -0
  212. package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
  213. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
  214. package/dist/modules/workspace/routine-write-policy.js +5 -6
  215. package/dist/modules/workspace/routine-write-policy.js.map +1 -1
  216. package/dist/modules/workspace/session-sink.d.ts +5 -5
  217. package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
  218. package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
  219. package/dist/modules/workspace/set-aside-clone.js +92 -0
  220. package/dist/modules/workspace/set-aside-clone.js.map +1 -0
  221. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +59 -9
  222. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  223. package/dist/modules/workspace/startup/kb-startup-runner.js +65 -24
  224. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  225. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  226. package/dist/modules/workspace/startup/steps/seed-tree.js +3 -3
  227. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  228. package/dist/modules/workspace/startup/steps/template-source.d.ts +40 -0
  229. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  230. package/dist/modules/workspace/startup/steps/template-source.js +46 -4
  231. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  232. package/dist/modules/workspace/upload-limits.d.ts +13 -0
  233. package/dist/modules/workspace/upload-limits.d.ts.map +1 -0
  234. package/dist/modules/workspace/upload-limits.js +13 -0
  235. package/dist/modules/workspace/upload-limits.js.map +1 -0
  236. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  237. package/dist/modules/workspace/workspace.routes.js +93 -4
  238. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  239. package/dist/modules/workspace/workspace.service.d.ts +128 -5
  240. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  241. package/dist/modules/workspace/workspace.service.js +316 -58
  242. package/dist/modules/workspace/workspace.service.js.map +1 -1
  243. package/dist/modules/workspace/workspace.tools.d.ts +13 -11
  244. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  245. package/dist/modules/workspace/workspace.tools.js +791 -198
  246. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  247. package/dist/modules/workspace/write-denial.d.ts +0 -6
  248. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  249. package/dist/modules/workspace/write-denial.js +0 -6
  250. package/dist/modules/workspace/write-denial.js.map +1 -1
  251. package/dist/modules/workspace/zip-entry-rules.d.ts +114 -0
  252. package/dist/modules/workspace/zip-entry-rules.d.ts.map +1 -0
  253. package/dist/modules/workspace/zip-entry-rules.js +154 -0
  254. package/dist/modules/workspace/zip-entry-rules.js.map +1 -0
  255. package/dist/modules/write-access/write-access.d.ts +60 -0
  256. package/dist/modules/write-access/write-access.d.ts.map +1 -0
  257. package/dist/modules/write-access/write-access.js +129 -0
  258. package/dist/modules/write-access/write-access.js.map +1 -0
  259. package/dist/shared/column-crypto.d.ts +194 -0
  260. package/dist/shared/column-crypto.d.ts.map +1 -0
  261. package/dist/shared/column-crypto.js +144 -0
  262. package/dist/shared/column-crypto.js.map +1 -0
  263. package/dist/shared/domain-errors.d.ts +25 -0
  264. package/dist/shared/domain-errors.d.ts.map +1 -1
  265. package/dist/shared/domain-errors.js +28 -0
  266. package/dist/shared/domain-errors.js.map +1 -1
  267. package/dist/shared/git.contract.d.ts +20 -0
  268. package/dist/shared/git.contract.d.ts.map +1 -1
  269. package/dist/shared/git.contract.js +26 -0
  270. package/dist/shared/git.contract.js.map +1 -1
  271. package/dist/shared/token-crypto.d.ts.map +1 -1
  272. package/dist/shared/token-crypto.js +25 -1
  273. package/dist/shared/token-crypto.js.map +1 -1
  274. package/dist/tenancy/static-tenant-source.d.ts +0 -1
  275. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  276. package/dist/tenancy/static-tenant-source.js +1 -2
  277. package/dist/tenancy/static-tenant-source.js.map +1 -1
  278. package/dist/tenancy/tenant-secrets.d.ts +5 -1
  279. package/dist/tenancy/tenant-secrets.d.ts.map +1 -1
  280. package/dist/tenancy/tenant-secrets.js +4 -0
  281. package/dist/tenancy/tenant-secrets.js.map +1 -1
  282. package/kb-template/AGENTS.md +2 -0
  283. package/migrations/0014_change_request_closed_reason.sql +1 -0
  284. package/migrations/0015_account_deactivation.sql +3 -0
  285. package/migrations/0016_pii_encryption.sql +20 -0
  286. package/migrations/meta/0014_snapshot.json +2265 -0
  287. package/migrations/meta/0015_snapshot.json +2271 -0
  288. package/migrations/meta/0016_snapshot.json +2327 -0
  289. package/migrations/meta/_journal.json +21 -0
  290. package/package.json +3 -3
  291. package/src/__tests__/kb-layout-config.test.ts +0 -2
  292. package/src/__tests__/retired-settings.test.ts +96 -0
  293. package/src/core/__tests__/gated-boot.test.ts +132 -0
  294. package/src/core/__tests__/lifecycle.test.ts +241 -5
  295. package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
  296. package/src/core/core-ports.ts +11 -1
  297. package/src/core/create-core-server.ts +52 -16
  298. package/src/core/create-core-services.ts +156 -24
  299. package/src/core/lifecycle.ts +117 -13
  300. package/src/core-config.ts +31 -14
  301. package/src/index.ts +30 -1
  302. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
  303. package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
  304. package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
  305. package/src/modules/access/__tests__/users-db-double.ts +21 -12
  306. package/src/modules/access/access-control.interface.ts +43 -12
  307. package/src/modules/access/access-control.service.ts +234 -36
  308. package/src/modules/access/access.routes.ts +17 -16
  309. package/src/modules/access/directory-sync-bot.ts +7 -3
  310. package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +11 -5
  311. package/src/modules/agent-instructions/__tests__/compose.test.ts +19 -10
  312. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +238 -0
  313. package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -2
  314. package/src/modules/agent-instructions/compose.ts +50 -7
  315. package/src/modules/agent-instructions/index.ts +12 -0
  316. package/src/modules/agent-instructions/shared-file-rules.ts +314 -0
  317. package/src/modules/audit/agent-audit.service.ts +4 -2
  318. package/src/modules/auth/__tests__/account-deactivation.test.ts +253 -0
  319. package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +6 -2
  320. package/src/modules/auth/__tests__/account.routes.test.ts +87 -6
  321. package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
  322. package/src/modules/auth/__tests__/auth.service.test.ts +4 -2
  323. package/src/modules/auth/account-admission.ts +78 -9
  324. package/src/modules/auth/account-erasure.service.ts +69 -18
  325. package/src/modules/auth/account.routes.ts +63 -7
  326. package/src/modules/auth/auth.middleware.ts +19 -8
  327. package/src/modules/auth/auth.routes.ts +8 -0
  328. package/src/modules/auth/auth.service.ts +206 -23
  329. package/src/modules/auth/oidc-auth-provider.ts +7 -2
  330. package/src/modules/code-mode/__tests__/chain-runtime.e2e.test.ts +335 -0
  331. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +46 -2
  332. package/src/modules/code-mode/code-mode.tool.ts +80 -34
  333. package/src/modules/database/__tests__/connection.test.ts +12 -0
  334. package/src/modules/database/__tests__/pii-backfill.pg.test.ts +561 -0
  335. package/src/modules/database/connection.ts +117 -0
  336. package/src/modules/database/core-schema.ts +116 -58
  337. package/src/modules/database/migrate.ts +353 -1
  338. package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
  339. package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
  340. package/src/modules/kb-fs/branch-name.ts +14 -1
  341. package/src/modules/kb-fs/repo-path.ts +85 -0
  342. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
  343. package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
  344. package/src/modules/kb-sync/sync-auth.ts +2 -2
  345. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
  346. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +4 -3
  347. package/src/modules/mcp/__tests__/mcp.service.test.ts +115 -13
  348. package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
  349. package/src/modules/mcp/mcp.service.ts +46 -7
  350. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
  351. package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
  352. package/src/modules/plugins/join-request-records.store.ts +7 -4
  353. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
  354. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +91 -58
  355. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +25 -8
  356. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +14 -11
  357. package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
  358. package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
  359. package/src/modules/settings/deployment-settings.service.ts +8 -3
  360. package/src/modules/settings/setup.routes.ts +263 -19
  361. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
  362. package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
  363. package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
  364. package/src/modules/skills/skills.contract.ts +91 -18
  365. package/src/modules/skills/skills.service.ts +278 -41
  366. package/src/modules/skills/skills.tools.ts +80 -8
  367. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
  368. package/src/modules/tool-auth/external-api-key.service.ts +12 -4
  369. package/src/modules/tool-auth/internal-token.service.ts +3 -3
  370. package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
  371. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
  372. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
  373. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
  374. package/src/modules/tool-helpers/tool-handler.ts +24 -4
  375. package/src/modules/tool-helpers/tool.contract.ts +3 -2
  376. package/src/modules/tool-helpers/validate-token.ts +1 -1
  377. package/src/modules/tool-manuals/tool-manuals.tools.ts +44 -31
  378. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +378 -0
  379. package/src/modules/tool-registry/description-length.ts +111 -0
  380. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
  381. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
  382. package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
  383. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +264 -6
  384. package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
  385. package/src/modules/workflow/agent-tools/workflow.tools.ts +86 -35
  386. package/src/modules/workflow/file-lock.service.ts +31 -0
  387. package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
  388. package/src/modules/workflow/git/__tests__/git.service.prFetchFailure.test.ts +170 -0
  389. package/src/modules/workflow/git/git.service.ts +215 -1
  390. package/src/modules/workflow/pending-commits.service.ts +64 -2
  391. package/src/modules/workflow/recovery-bot.ts +7 -3
  392. package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +4 -0
  393. package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +25 -3
  394. package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
  395. package/src/modules/workflow/workflow-hooks.ts +64 -26
  396. package/src/modules/workflow/workflow.service.ts +125 -5
  397. package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
  398. package/src/modules/workspace/__tests__/agent-uploads.test.ts +1604 -0
  399. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +394 -0
  400. package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
  401. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
  402. package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
  403. package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
  404. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
  405. package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +156 -0
  406. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
  407. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +402 -0
  408. package/src/modules/workspace/__tests__/workspace.service.test.ts +77 -9
  409. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +41 -39
  410. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +2 -3
  411. package/src/modules/workspace/__tests__/workspace.tools.test.ts +766 -56
  412. package/src/modules/workspace/agent-access.gate.ts +164 -0
  413. package/src/modules/workspace/agent-upload.routes.ts +214 -0
  414. package/src/modules/workspace/agent-upload.store.ts +668 -0
  415. package/src/modules/workspace/routine-write-policy.ts +5 -6
  416. package/src/modules/workspace/session-sink.ts +5 -5
  417. package/src/modules/workspace/set-aside-clone.ts +96 -0
  418. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +86 -0
  419. package/src/modules/workspace/startup/kb-startup-runner.ts +115 -34
  420. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +9 -9
  421. package/src/modules/workspace/startup/steps/seed-tree.ts +3 -6
  422. package/src/modules/workspace/startup/steps/template-source.ts +53 -5
  423. package/src/modules/workspace/upload-limits.ts +12 -0
  424. package/src/modules/workspace/workspace.routes.ts +96 -5
  425. package/src/modules/workspace/workspace.service.ts +319 -61
  426. package/src/modules/workspace/workspace.tools.ts +894 -214
  427. package/src/modules/workspace/write-denial.ts +0 -8
  428. package/src/modules/workspace/zip-entry-rules.ts +173 -0
  429. package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
  430. package/src/modules/write-access/write-access.ts +153 -0
  431. package/src/shared/__tests__/column-crypto.test.ts +217 -0
  432. package/src/shared/column-crypto.ts +218 -0
  433. package/src/shared/domain-errors.ts +31 -0
  434. package/src/shared/git.contract.ts +27 -0
  435. package/src/shared/token-crypto.ts +28 -1
  436. package/src/tenancy/__tests__/static-tenant-source.test.ts +4 -1
  437. package/src/tenancy/static-tenant-source.ts +1 -3
  438. package/src/tenancy/tenant-secrets.ts +5 -1
  439. package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
  440. package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
  441. package/dist/modules/workflow/session-ontology.policy.js +0 -62
  442. package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
  443. package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
  444. package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
  445. package/dist/modules/workflow/session-ontology.service.js +0 -147
  446. package/dist/modules/workflow/session-ontology.service.js.map +0 -1
  447. package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
  448. package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
  449. package/dist/modules/workspace/session-ontology.gate.js +0 -161
  450. package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
  451. package/dist/shared/kb-layout.d.ts +0 -39
  452. package/dist/shared/kb-layout.d.ts.map +0 -1
  453. package/dist/shared/kb-layout.js +0 -103
  454. package/dist/shared/kb-layout.js.map +0 -1
  455. package/dist/shared/kb-layout.test.d.ts +0 -2
  456. package/dist/shared/kb-layout.test.d.ts.map +0 -1
  457. package/dist/shared/kb-layout.test.js +0 -75
  458. package/dist/shared/kb-layout.test.js.map +0 -1
  459. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
  460. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
  461. package/src/modules/workflow/session-ontology.policy.ts +0 -70
  462. package/src/modules/workflow/session-ontology.service.ts +0 -183
  463. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
  464. package/src/modules/workspace/session-ontology.gate.ts +0 -191
  465. package/src/shared/kb-layout.test.ts +0 -98
  466. package/src/shared/kb-layout.ts +0 -102
@@ -0,0 +1,238 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import {
4
+ DEFAULT_KB_LAYOUT,
5
+ LEGACY_AGENTS_FILE,
6
+ PREAMBLE_CAP,
7
+ renderKbLayoutPlaceholders,
8
+ } from '@bevel-software/platform-shared';
9
+ import { describe, expect, it } from 'vitest';
10
+ import { defaultKbTemplateDir } from '../../../assets.js';
11
+ import { renderTemplateText, SHARED_FILE_RULES_PLACEHOLDER } from '../../workspace/startup/steps/template-source.js';
12
+ import {
13
+ INSTRUCTIONS_CAP,
14
+ PLATFORM_HEADER,
15
+ PREAMBLE_TRUNCATION_MARKER,
16
+ composeAgentInstructions,
17
+ platformInstructions,
18
+ } from '../compose.js';
19
+ import {
20
+ POINTER_GUIDE_NAME_BUDGET,
21
+ SHARED_FILE_RULES_CAP,
22
+ SHARED_RULES_POINTER_MAX,
23
+ SHARED_RULES_SECTION,
24
+ sharedFileRules,
25
+ sharedFileRulesSection,
26
+ sharedRulesPointer,
27
+ } from '../shared-file-rules.js';
28
+
29
+ /**
30
+ * The rules every file tool shares are stated in TWO places — the handshake
31
+ * `instructions` and the platform-managed agent guide — and in neither tool
32
+ * description. These tests are what makes "stated once" true: the two places
33
+ * carry the SAME string, built from one text, so a rule cannot be changed in one
34
+ * and left stale in the other.
35
+ */
36
+
37
+ /** The packaged template's agent guide, as it ships (placeholders unrendered). */
38
+ async function guideTemplate(): Promise<string> {
39
+ return readFile(path.join(defaultKbTemplateDir(), LEGACY_AGENTS_FILE), 'utf8');
40
+ }
41
+
42
+ describe('the shared file rules are one text, in two places', () => {
43
+ it('puts the identical section in the handshake instructions and in the managed guide', async () => {
44
+ const section = sharedFileRulesSection(DEFAULT_KB_LAYOUT);
45
+ expect(section.startsWith(`## ${SHARED_RULES_SECTION}\n\n`)).toBe(true);
46
+
47
+ const instructions = composeAgentInstructions(null).instructions;
48
+ expect(instructions).toContain(section);
49
+
50
+ const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
51
+ expect(guide).toContain(section);
52
+
53
+ // Byte-for-byte the same text in both, which is the whole point: neither is
54
+ // a hand-written copy that could be edited on its own.
55
+ const inGuide = guide.slice(guide.indexOf(section), guide.indexOf(section) + section.length);
56
+ const inInstructions = instructions.slice(
57
+ instructions.indexOf(section),
58
+ instructions.indexOf(section) + section.length,
59
+ );
60
+ expect(inGuide).toBe(inInstructions);
61
+ });
62
+
63
+ it('states every rule in both places, and the content rule exactly once in each', async () => {
64
+ const instructions = composeAgentInstructions(null).instructions;
65
+ const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
66
+ const rules = sharedFileRules(DEFAULT_KB_LAYOUT);
67
+ expect(rules.map((r) => r.id)).toEqual([
68
+ 'agent-guide',
69
+ 'content-kinds',
70
+ 'write-mode',
71
+ 'images-in-pages',
72
+ 'escape-sequences',
73
+ 'upload-route',
74
+ 'dry-run-confirm',
75
+ 'managed-items',
76
+ 'refused-for-permissions',
77
+ 'tool-chain',
78
+ ]);
79
+ for (const rule of rules) {
80
+ expect(instructions, rule.id).toContain(rule.body);
81
+ expect(guide, rule.id).toContain(rule.body);
82
+ }
83
+ // Once in each, not twice: the rule used to arrive on a dozen tools at a time.
84
+ const contentRule = rules.find((r) => r.id === 'content-kinds')!.body.split('\n\n')[0];
85
+ expect(instructions.split(contentRule)).toHaveLength(2);
86
+ expect(guide.split(contentRule)).toHaveLength(2);
87
+ });
88
+
89
+ it('carries the whole content rule — the refused families, the byte tools and the upload path', async () => {
90
+ for (const text of [composeAgentInstructions(null).instructions, renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT)]) {
91
+ expect(text).toContain('`binary_not_writable`');
92
+ expect(text).toContain('copy_file, move_file and delete_file act on bytes of any kind');
93
+ expect(text).toContain('unzip extracts the entries of a `.zip`');
94
+ // The upload path by the names of the tools this deployment serves.
95
+ expect(text).toContain('call `request_file_upload`');
96
+ expect(text).toContain('then `apply_file_upload`');
97
+ expect(text).not.toContain('request_upload_token');
98
+ expect(text).toContain('`contentMode`');
99
+ expect(text).toContain('.docx/.pptx/.xlsx/.odt/.odp/.ods/.pdf');
100
+ expect(text).toContain('.eml/.msg');
101
+ expect(text).toContain('.doc/.ppt/.xls');
102
+ }
103
+ });
104
+
105
+ it('names the guide under the name this deployment gave it, in both places and in the pointer', async () => {
106
+ const layout = { ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' };
107
+ const section = sharedFileRulesSection(layout);
108
+ // Ours first, then the organisation's own AGENTS.md, then the pre-rename name.
109
+ expect(section).toContain('read `HEXIS.md` at the KB root, then `AGENTS.md` if it also exists');
110
+ expect(section).toContain('`CLAUDE.md` on a knowledge base seeded before it was renamed');
111
+ // The platform files a move refuses list the guide under its own name too.
112
+ expect(section).toContain('`HEXIS.md`');
113
+ expect(composeAgentInstructions(null, layout).instructions).toContain(section);
114
+ expect(renderTemplateText(await guideTemplate(), layout)).toContain(section);
115
+ expect(sharedRulesPointer(layout)).toBe(` Shared rules for all file tools: see "${SHARED_RULES_SECTION}" in HEXIS.md.`);
116
+ });
117
+
118
+ it('keeps the pointer bounded whatever the guide is called', () => {
119
+ // The pointer rides on every file tool, so its length is not the guide's
120
+ // business: `validateFilename` allows a 255-byte name, and an unbounded
121
+ // pointer would reach 318 characters and push `file_stat` past the
122
+ // description cap — on a renamed deployment only, which no measurement
123
+ // taken under the default layout would ever show.
124
+ const nameOfLength = (n: number): string => `${'x'.repeat(n - 3)}.md`;
125
+ for (const length of [9, POINTER_GUIDE_NAME_BUDGET, POINTER_GUIDE_NAME_BUDGET + 1, 120, 255]) {
126
+ const pointer = sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: nameOfLength(length) });
127
+ expect(pointer.length, `a ${length}-character name`).toBeLessThanOrEqual(SHARED_RULES_POINTER_MAX);
128
+ }
129
+ // Up to the budget the file is NAMED, which is the better sentence.
130
+ const named = nameOfLength(POINTER_GUIDE_NAME_BUDGET);
131
+ expect(sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: named })).toContain(named);
132
+ // Past it the guide is named by its role instead — a sentence that still
133
+ // says where to look, rather than a catalog entry the client cuts.
134
+ const tooLong = nameOfLength(POINTER_GUIDE_NAME_BUDGET + 1);
135
+ const fallback = sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: tooLong });
136
+ expect(fallback).not.toContain(tooLong);
137
+ expect(fallback).toBe(` Shared rules for all file tools: see "${SHARED_RULES_SECTION}" in the agent guide at the KB root.`);
138
+ // Either way the section's first rule still names the file, so the name is
139
+ // never actually lost.
140
+ expect(sharedFileRulesSection({ ...DEFAULT_KB_LAYOUT, agentsFile: tooLong })).toContain(tooLong);
141
+ });
142
+
143
+ it('points at the section with one short sentence under the default name', () => {
144
+ expect(sharedRulesPointer(DEFAULT_KB_LAYOUT)).toBe(
145
+ ' Shared rules for all file tools: see "Working with files" in AGENTS.md.',
146
+ );
147
+ // An unset guide name means the default, as it does everywhere else.
148
+ expect(sharedRulesPointer({ ...DEFAULT_KB_LAYOUT, agentsFile: undefined })).toBe(
149
+ sharedRulesPointer(DEFAULT_KB_LAYOUT),
150
+ );
151
+ });
152
+ });
153
+
154
+ describe('the guide template asks for the rules rather than repeating them', () => {
155
+ it('carries the placeholder once, and no rule text of its own', async () => {
156
+ const template = await guideTemplate();
157
+ expect(template.split(SHARED_FILE_RULES_PLACEHOLDER)).toHaveLength(2);
158
+ // The rules are not ALSO written into the template — that is the copy that
159
+ // would drift. Checked on a fragment of each rule rather than on the whole.
160
+ for (const rule of sharedFileRules(DEFAULT_KB_LAYOUT)) {
161
+ expect(template, rule.id).not.toContain(rule.body.slice(0, 60));
162
+ }
163
+ });
164
+
165
+ it('renders the placeholder nowhere else, so other managed files are untouched', async () => {
166
+ const access = await readFile(path.join(defaultKbTemplateDir(), 'access.md'), 'utf8');
167
+ expect(access).not.toContain(SHARED_FILE_RULES_PLACEHOLDER);
168
+ // So rendering it gains no rule text: whatever the layout renderer does to
169
+ // its own tokens, the section is nowhere in the result.
170
+ const rendered = renderTemplateText(access, DEFAULT_KB_LAYOUT);
171
+ expect(rendered).not.toContain(sharedFileRulesSection(DEFAULT_KB_LAYOUT));
172
+ expect(rendered).not.toContain(`## ${SHARED_RULES_SECTION}`);
173
+ });
174
+
175
+ it('leaves no placeholder behind once rendered', async () => {
176
+ const guide = renderTemplateText(await guideTemplate(), DEFAULT_KB_LAYOUT);
177
+ expect(guide).not.toContain('{{');
178
+ });
179
+
180
+ it('renders the layout tokens first, then injects the rules — so the rules are never re-rendered', async () => {
181
+ // A layout the two orders DISAGREE on, which an ordinary one does not: the
182
+ // section states the knowledge-base folder's name, so a deployment whose
183
+ // folder is literally called `{{skillsDir}}` puts a layout token inside the
184
+ // rendered section. Injecting first would then rewrite it on the second
185
+ // pass and the guide would name the skills folder where the rule means the
186
+ // knowledge-base one. Pathological on purpose: it is the only kind of input
187
+ // on which the order is observable at all, which is why a test that pins
188
+ // the order has to use one.
189
+ const layout = { ...DEFAULT_KB_LAYOUT, knowledgeBaseDir: '{{skillsDir}}', skillsDir: 'Playbooks' };
190
+ const section = sharedFileRulesSection(layout);
191
+ expect(section).toContain('{{skillsDir}}/');
192
+
193
+ const template = `Skills live in {{skillsDir}}/.\n\n${SHARED_FILE_RULES_PLACEHOLDER}\n`;
194
+ const rendered = renderTemplateText(template, layout);
195
+ // The author's token is rendered, and the section survives byte for byte.
196
+ expect(rendered).toBe(`Skills live in Playbooks/.\n\n${section}\n`);
197
+
198
+ // And the other order really does differ on this input, so the assertion
199
+ // above is load-bearing rather than true of both.
200
+ const injectedFirst = renderKbLayoutPlaceholders(
201
+ template.replaceAll(SHARED_FILE_RULES_PLACEHOLDER, () => section),
202
+ layout,
203
+ );
204
+ expect(injectedFirst).not.toBe(rendered);
205
+ expect(injectedFirst).not.toContain(section);
206
+ expect(injectedFirst).toContain('`Playbooks/`) and git metadata are refused');
207
+ });
208
+ });
209
+
210
+ describe('the handshake text stays inside the length it pins', () => {
211
+ it('holds the shared section under its own cap', () => {
212
+ const section = sharedFileRulesSection(DEFAULT_KB_LAYOUT);
213
+ expect(
214
+ section.length,
215
+ `the shared rules are ${section.length} characters (cap ${SHARED_FILE_RULES_CAP})`,
216
+ ).toBeLessThanOrEqual(SHARED_FILE_RULES_CAP);
217
+ });
218
+
219
+ it('holds the worst-case instructions under the cap the caps add up to', () => {
220
+ // Header + the shared rules at their cap + a preamble at ITS cap + the
221
+ // marker a cut appends + the two blank lines between the three parts.
222
+ const worstCase = PLATFORM_HEADER.length + 2 + SHARED_FILE_RULES_CAP + 2 + PREAMBLE_CAP + 1 + PREAMBLE_TRUNCATION_MARKER.length;
223
+ expect(worstCase).toBeLessThanOrEqual(INSTRUCTIONS_CAP);
224
+ // And what an actual over-long preamble produces is under it too.
225
+ const composed = composeAgentInstructions('x'.repeat(PREAMBLE_CAP * 2));
226
+ expect(composed.truncated).toBe(true);
227
+ expect(composed.instructions.length).toBeLessThanOrEqual(INSTRUCTIONS_CAP);
228
+ });
229
+
230
+ it('hands the platform-owned text back as `header`, apart from the admin preamble', () => {
231
+ const out = composeAgentInstructions('We sell permits.');
232
+ expect(out.header).toBe(platformInstructions(DEFAULT_KB_LAYOUT));
233
+ expect(out.header).toContain(PLATFORM_HEADER);
234
+ expect(out.header).toContain(`## ${SHARED_RULES_SECTION}`);
235
+ expect(out.preamble).toBe('We sell permits.');
236
+ expect(out.instructions).toBe(`${out.header}\n\nWe sell permits.`);
237
+ });
238
+ });
@@ -1,4 +1,5 @@
1
1
  import express, { type RequestHandler } from 'express';
2
+ import type { KbLayout } from '@bevel-software/platform-shared';
2
3
  import { logger } from '../../shared/logging.js';
3
4
 
4
5
  const log = logger('agent-instructions');
@@ -22,7 +23,16 @@ import type { AgentPreambleReader } from './read-preamble.js';
22
23
  * name folders the caller cannot open. A reader throw is a 500, never an
23
24
  * empty preamble, so the caller's own fallback decides what to send.
24
25
  */
25
- export function createAgentInstructionsRoutes(manualAuth: RequestHandler, readPreamble: AgentPreambleReader): express.Router {
26
+ export function createAgentInstructionsRoutes(
27
+ manualAuth: RequestHandler,
28
+ readPreamble: AgentPreambleReader,
29
+ /**
30
+ * The layout in effect, read per request — the shared file rules name the
31
+ * managed guide, whose name the setup save may change without a restart.
32
+ * Optional so a construction with no layout in hand gets `AGENTS.md`.
33
+ */
34
+ kbLayout?: () => KbLayout,
35
+ ): express.Router {
26
36
  const router = express.Router();
27
37
 
28
38
  router.get('/agent/instructions', manualAuth, async (_req, res) => {
@@ -30,7 +40,7 @@ export function createAgentInstructionsRoutes(manualAuth: RequestHandler, readPr
30
40
  // successful one: the contract is on the route, not on the happy path.
31
41
  res.setHeader('Cache-Control', 'no-store');
32
42
  try {
33
- res.json(composeAgentInstructions(await readPreamble()));
43
+ res.json(composeAgentInstructions(await readPreamble(), kbLayout?.()));
34
44
  } catch (err) {
35
45
  log.error('reading mcp-description.md failed:', { err });
36
46
  res.status(500).json({ error: 'Failed to read the agent instructions' });
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * What a connected agent is told at the start of an MCP session, composed
3
- * from two layers: a platform header the code owns, and the deployment
4
- * preamble an admin writes in `mcp-description.md` at the repository root.
3
+ * from three layers: a platform header the code owns, the rules every file tool
4
+ * shares (`shared-file-rules.ts`, the same text the managed guide carries), and
5
+ * the deployment preamble an admin writes in `mcp-description.md` at the
6
+ * repository root.
5
7
  *
6
8
  * Two channels carry the result. The full text goes out as `instructions` on
7
9
  * the initialize handshake, which Claude Code, Claude Desktop and Cursor place
@@ -21,11 +23,14 @@
21
23
  // apart. Re-exported here because this module is where the backend reads them
22
24
  // from.
23
25
  import {
26
+ DEFAULT_KB_LAYOUT,
24
27
  PREAMBLE_CAP,
25
28
  PREAMBLE_FILE,
26
29
  TOOL_PREFIX_CAP,
27
30
  stripHtmlComments,
31
+ type KbLayout,
28
32
  } from '@bevel-software/platform-shared';
33
+ import { sharedFileRulesSection } from './shared-file-rules.js';
29
34
 
30
35
  export { PREAMBLE_CAP, PREAMBLE_FILE, TOOL_PREFIX_CAP };
31
36
 
@@ -53,13 +58,42 @@ export const PLATFORM_HEADER =
53
58
  */
54
59
  export const TOOL_PREFIX_LINE = "This organisation's knowledge base. Search it before answering from memory.";
55
60
 
61
+ /**
62
+ * The whole platform-owned part of the handshake text: what Hexis is, then the
63
+ * rules every file tool shares (see `shared-file-rules.ts`). This is what the
64
+ * `header` field carries and what the card shows as fixed and not editable;
65
+ * the admin's preamble follows it.
66
+ *
67
+ * A FUNCTION of the layout, because the shared rules name the managed guide
68
+ * and its file name is a deployment setting.
69
+ */
70
+ export function platformInstructions(layout: KbLayout): string {
71
+ return `${PLATFORM_HEADER}\n\n${sharedFileRulesSection(layout)}`;
72
+ }
73
+
74
+ /**
75
+ * The ceiling on the WHOLE handshake text, pinned by a test.
76
+ *
77
+ * No client publishes a limit for `instructions` — what was observed being cut
78
+ * was tool descriptions — so this is not a measured client limit but the
79
+ * arithmetic ceiling of the parts, held low enough that it stays a plausible
80
+ * system-prompt insert (~3,500 tokens): the header, the shared rules under their
81
+ * own cap, and the preamble under its cap plus the marker a cut appends. The
82
+ * test is what makes it a ceiling rather than a hope: a shared rule that grew
83
+ * past it fails before it reaches an agent.
84
+ */
85
+ export const INSTRUCTIONS_CAP = 14_000;
86
+
56
87
  /** The one-line marker that replaces everything past the preamble cap. */
57
88
  export const PREAMBLE_TRUNCATION_MARKER = `[preamble truncated at ${PREAMBLE_CAP.toLocaleString('en-US')} characters; shorten ${PREAMBLE_FILE}]`;
58
89
 
59
90
  export interface ComposedAgentInstructions {
60
- /** The header, then the preamble body when there is one. Sent on the initialize handshake. */
91
+ /** The platform text, then the preamble body when there is one. Sent on the initialize handshake. */
61
92
  instructions: string;
62
- /** The platform header alone, so a card can show the fixed part apart from the admin's. */
93
+ /**
94
+ * The platform-owned text alone — the header and the shared file rules — so a
95
+ * card can show the fixed part apart from the admin's.
96
+ */
63
97
  header: string;
64
98
  /** The preamble body as sent (cut and marked when over the cap); empty when there is none. */
65
99
  preamble: string;
@@ -84,15 +118,24 @@ export interface ComposedAgentInstructions {
84
118
  * absent). HTML comments are private notes and never leave the file; an
85
119
  * unterminated `<!--` strips everything after it, so the most likely editing
86
120
  * slip withholds text rather than leaking it.
121
+ *
122
+ * `layout` decides only the guide's name inside the shared rules, and defaults
123
+ * to the standard one — a caller with no layout in hand (a test, a surface that
124
+ * predates the setting) gets `AGENTS.md`, which is what an unset name means
125
+ * everywhere else.
87
126
  */
88
- export function composeAgentInstructions(preamble: string | null): ComposedAgentInstructions {
127
+ export function composeAgentInstructions(
128
+ preamble: string | null,
129
+ layout: KbLayout = DEFAULT_KB_LAYOUT,
130
+ ): ComposedAgentInstructions {
131
+ const platform = platformInstructions(layout);
89
132
  const { text, unterminated } = stripHtmlComments(preamble ?? '');
90
133
  const normalized = text.replace(/\r\n?/g, '\n');
91
134
  const stripped = normalized.trim();
92
135
  const preambleChars = stripped.length;
93
136
  const truncated = preambleChars > PREAMBLE_CAP;
94
137
  const body = truncated ? `${cutAtCodePoint(stripped, PREAMBLE_CAP)}\n${PREAMBLE_TRUNCATION_MARKER}` : stripped;
95
- const instructions = body ? `${PLATFORM_HEADER}\n\n${body}` : PLATFORM_HEADER;
138
+ const instructions = body ? `${platform}\n\n${body}` : platform;
96
139
 
97
140
  // Classified UNTRIMMED: the leading indentation of a first line is what
98
141
  // makes it an indented code block, and the trim above would turn that
@@ -105,7 +148,7 @@ export function composeAgentInstructions(preamble: string | null): ComposedAgent
105
148
 
106
149
  return {
107
150
  instructions,
108
- header: PLATFORM_HEADER,
151
+ header: platform,
109
152
  preamble: body,
110
153
  toolPrefix,
111
154
  toolPrefixLine: TOOL_PREFIX_LINE,
@@ -1,6 +1,8 @@
1
1
  export {
2
2
  composeAgentInstructions,
3
3
  prefixToolDescription,
4
+ platformInstructions,
5
+ INSTRUCTIONS_CAP,
4
6
  PLATFORM_HEADER,
5
7
  TOOL_PREFIX_LINE,
6
8
  PREAMBLE_CAP,
@@ -10,5 +12,15 @@ export {
10
12
  PREAMBLE_TRUNCATION_MARKER,
11
13
  type ComposedAgentInstructions,
12
14
  } from './compose.js';
15
+ export {
16
+ POINTER_GUIDE_NAME_BUDGET,
17
+ SHARED_FILE_RULES_CAP,
18
+ SHARED_RULES_POINTER_MAX,
19
+ SHARED_RULES_SECTION,
20
+ sharedFileRules,
21
+ sharedFileRulesSection,
22
+ sharedRulesPointer,
23
+ type SharedFileRule,
24
+ } from './shared-file-rules.js';
13
25
  export { readAgentPreamble, type AgentPreambleReader, type PreambleWorkspace } from './read-preamble.js';
14
26
  export { createAgentInstructionsRoutes } from './agent-instructions.routes.js';