@bevel-software/platform-core-backend 0.26.0 → 0.27.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 (613) hide show
  1. package/agent-guide/access-control.md +17 -0
  2. package/agent-guide/conventions.md +8 -0
  3. package/agent-guide/html-views.md +83 -0
  4. package/agent-guide/new-knowledge-base.md +24 -0
  5. package/agent-guide/tool-manuals.md +10 -0
  6. package/dist/assets.d.ts +19 -0
  7. package/dist/assets.d.ts.map +1 -1
  8. package/dist/assets.js +26 -2
  9. package/dist/assets.js.map +1 -1
  10. package/dist/core/catalog-revision.d.ts +19 -1
  11. package/dist/core/catalog-revision.d.ts.map +1 -1
  12. package/dist/core/catalog-revision.js +14 -3
  13. package/dist/core/catalog-revision.js.map +1 -1
  14. package/dist/core/create-core-server.d.ts +5 -0
  15. package/dist/core/create-core-server.d.ts.map +1 -1
  16. package/dist/core/create-core-server.js +54 -3
  17. package/dist/core/create-core-server.js.map +1 -1
  18. package/dist/core/create-core-services.d.ts +26 -0
  19. package/dist/core/create-core-services.d.ts.map +1 -1
  20. package/dist/core/create-core-services.js +100 -8
  21. package/dist/core/create-core-services.js.map +1 -1
  22. package/dist/core/lifecycle.d.ts +16 -1
  23. package/dist/core/lifecycle.d.ts.map +1 -1
  24. package/dist/core/lifecycle.js +18 -1
  25. package/dist/core/lifecycle.js.map +1 -1
  26. package/dist/core-config.d.ts +21 -0
  27. package/dist/core-config.d.ts.map +1 -1
  28. package/dist/core-config.js +22 -1
  29. package/dist/core-config.js.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/modules/access/access-control.interface.d.ts +8 -0
  35. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  36. package/dist/modules/access/access-control.service.d.ts +1 -0
  37. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  38. package/dist/modules/access/access-control.service.js +11 -0
  39. package/dist/modules/access/access-control.service.js.map +1 -1
  40. package/dist/modules/access/directory-sync-bot.js +1 -1
  41. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  42. package/dist/modules/agent-guide/agent-guide.d.ts +6 -0
  43. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  44. package/dist/modules/agent-guide/agent-guide.js +8 -0
  45. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  46. package/dist/modules/agent-guide/index.d.ts +1 -1
  47. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  48. package/dist/modules/agent-guide/index.js +1 -1
  49. package/dist/modules/agent-guide/index.js.map +1 -1
  50. package/dist/modules/agent-instructions/compose.d.ts +5 -0
  51. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  52. package/dist/modules/agent-instructions/compose.js +19 -2
  53. package/dist/modules/agent-instructions/compose.js.map +1 -1
  54. package/dist/modules/agent-instructions/shared-file-rules.js +19 -19
  55. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  56. package/dist/modules/audit/agent-audit.service.d.ts +21 -2
  57. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  58. package/dist/modules/audit/agent-audit.service.js +43 -2
  59. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  60. package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
  61. package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
  62. package/dist/modules/audit/agent-connection.routes.js +38 -0
  63. package/dist/modules/audit/agent-connection.routes.js.map +1 -0
  64. package/dist/modules/audit/audit.contract.d.ts +24 -0
  65. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  66. package/dist/modules/audit/audit.contract.js.map +1 -1
  67. package/dist/modules/audit/index.d.ts +1 -0
  68. package/dist/modules/audit/index.d.ts.map +1 -1
  69. package/dist/modules/audit/index.js +1 -0
  70. package/dist/modules/audit/index.js.map +1 -1
  71. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  72. package/dist/modules/auth/account.routes.js +10 -3
  73. package/dist/modules/auth/account.routes.js.map +1 -1
  74. package/dist/modules/auth/auth.middleware.d.ts +7 -0
  75. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  76. package/dist/modules/auth/auth.middleware.js +1 -1
  77. package/dist/modules/auth/auth.middleware.js.map +1 -1
  78. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  79. package/dist/modules/code-mode/code-mode.tool.js +11 -3
  80. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  81. package/dist/modules/database/core-schema.d.ts +96 -0
  82. package/dist/modules/database/core-schema.d.ts.map +1 -1
  83. package/dist/modules/database/core-schema.js +32 -3
  84. package/dist/modules/database/core-schema.js.map +1 -1
  85. package/dist/modules/embed/account-link.service.d.ts +42 -0
  86. package/dist/modules/embed/account-link.service.d.ts.map +1 -0
  87. package/dist/modules/embed/account-link.service.js +50 -0
  88. package/dist/modules/embed/account-link.service.js.map +1 -0
  89. package/dist/modules/embed/embed-link.d.ts +58 -0
  90. package/dist/modules/embed/embed-link.d.ts.map +1 -0
  91. package/dist/modules/embed/embed-link.js +160 -0
  92. package/dist/modules/embed/embed-link.js.map +1 -0
  93. package/dist/modules/embed/embed.errors.d.ts +30 -0
  94. package/dist/modules/embed/embed.errors.d.ts.map +1 -0
  95. package/dist/modules/embed/embed.errors.js +42 -0
  96. package/dist/modules/embed/embed.errors.js.map +1 -0
  97. package/dist/modules/embed/embed.interface.d.ts +204 -0
  98. package/dist/modules/embed/embed.interface.d.ts.map +1 -0
  99. package/dist/modules/embed/embed.interface.js +2 -0
  100. package/dist/modules/embed/embed.interface.js.map +1 -0
  101. package/dist/modules/embed/embed.routes.d.ts +59 -0
  102. package/dist/modules/embed/embed.routes.d.ts.map +1 -0
  103. package/dist/modules/embed/embed.routes.js +343 -0
  104. package/dist/modules/embed/embed.routes.js.map +1 -0
  105. package/dist/modules/embed/embed.service.d.ts +169 -0
  106. package/dist/modules/embed/embed.service.d.ts.map +1 -0
  107. package/dist/modules/embed/embed.service.js +627 -0
  108. package/dist/modules/embed/embed.service.js.map +1 -0
  109. package/dist/modules/embed/embed.tools.d.ts +51 -0
  110. package/dist/modules/embed/embed.tools.d.ts.map +1 -0
  111. package/dist/modules/embed/embed.tools.js +170 -0
  112. package/dist/modules/embed/embed.tools.js.map +1 -0
  113. package/dist/modules/embed/mcp-app.d.ts +64 -0
  114. package/dist/modules/embed/mcp-app.d.ts.map +1 -0
  115. package/dist/modules/embed/mcp-app.js +147 -0
  116. package/dist/modules/embed/mcp-app.js.map +1 -0
  117. package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
  118. package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
  119. package/dist/modules/embed/mcp-app.routes.js +36 -0
  120. package/dist/modules/embed/mcp-app.routes.js.map +1 -0
  121. package/dist/modules/github-app/github-app.connection.d.ts +15 -0
  122. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
  123. package/dist/modules/github-app/github-app.connection.js +34 -3
  124. package/dist/modules/github-app/github-app.connection.js.map +1 -1
  125. package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
  126. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  127. package/dist/modules/kb-fs/locking-filesystem.js +300 -0
  128. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  129. package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
  130. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
  131. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  132. package/dist/modules/kb-sync/kb-sync.service.js +6 -0
  133. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  134. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  135. package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
  136. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  137. package/dist/modules/mcp/mcp.service.d.ts +28 -1
  138. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  139. package/dist/modules/mcp/mcp.service.js +64 -8
  140. package/dist/modules/mcp/mcp.service.js.map +1 -1
  141. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
  142. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  143. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
  144. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  145. package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
  146. package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
  147. package/dist/modules/onboarding/onboarding.contract.js +7 -0
  148. package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
  149. package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
  150. package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
  151. package/dist/modules/onboarding/onboarding.routes.js +81 -0
  152. package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
  153. package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
  154. package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
  155. package/dist/modules/onboarding/starter-pack.service.js +492 -0
  156. package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
  157. package/dist/modules/onboarding/starter-packs.d.ts +78 -0
  158. package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
  159. package/dist/modules/onboarding/starter-packs.js +156 -0
  160. package/dist/modules/onboarding/starter-packs.js.map +1 -0
  161. package/dist/modules/plugins/index.d.ts +1 -1
  162. package/dist/modules/plugins/index.d.ts.map +1 -1
  163. package/dist/modules/plugins/index.js +1 -1
  164. package/dist/modules/plugins/index.js.map +1 -1
  165. package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
  166. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  167. package/dist/modules/plugins/plugin-provision.service.js +18 -1
  168. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  169. package/dist/modules/plugins/plugins.routes.d.ts +0 -36
  170. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  171. package/dist/modules/plugins/plugins.routes.js +31 -0
  172. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  173. package/dist/modules/settings/deployment-settings.service.d.ts +34 -0
  174. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  175. package/dist/modules/settings/deployment-settings.service.js +134 -1
  176. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  177. package/dist/modules/settings/repository-source.d.ts +1 -0
  178. package/dist/modules/settings/repository-source.d.ts.map +1 -1
  179. package/dist/modules/settings/repository-source.js +3 -3
  180. package/dist/modules/settings/repository-source.js.map +1 -1
  181. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  182. package/dist/modules/skills/skills.tools.js +50 -32
  183. package/dist/modules/skills/skills.tools.js.map +1 -1
  184. package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
  185. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
  186. package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
  187. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
  188. package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
  189. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  190. package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
  191. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  192. package/dist/modules/tool-auth/external-api-key.service.js +59 -2
  193. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  194. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  195. package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
  196. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  197. package/dist/modules/tool-helpers/index.d.ts +28 -1
  198. package/dist/modules/tool-helpers/index.d.ts.map +1 -1
  199. package/dist/modules/tool-helpers/index.js +28 -1
  200. package/dist/modules/tool-helpers/index.js.map +1 -1
  201. package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
  202. package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
  203. package/dist/modules/tool-helpers/route-argument-check.js +132 -0
  204. package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
  205. package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
  206. package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
  207. package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
  208. package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
  209. package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
  210. package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
  211. package/dist/modules/tool-helpers/tool-def.js +70 -6
  212. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  213. package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
  214. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  215. package/dist/modules/tool-helpers/tool-handler.js +157 -8
  216. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  217. package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
  218. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  219. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  220. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  221. package/dist/modules/tool-manuals/tool-manuals.tools.js +14 -5
  222. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  223. package/dist/modules/tool-registry/description-length.d.ts +6 -2
  224. package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
  225. package/dist/modules/tool-registry/description-length.js +27 -5
  226. package/dist/modules/tool-registry/description-length.js.map +1 -1
  227. package/dist/modules/tool-registry/guide-first.d.ts +4 -0
  228. package/dist/modules/tool-registry/guide-first.d.ts.map +1 -1
  229. package/dist/modules/tool-registry/guide-first.js +9 -1
  230. package/dist/modules/tool-registry/guide-first.js.map +1 -1
  231. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +53 -0
  232. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
  233. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +97 -68
  234. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
  235. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
  236. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  237. package/dist/modules/workflow/agent-tools/workflow.tools.js +124 -37
  238. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  239. package/dist/modules/workflow/file-lock.service.d.ts +29 -5
  240. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  241. package/dist/modules/workflow/file-lock.service.js +62 -5
  242. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  243. package/dist/modules/workflow/git/git.service.d.ts +72 -1
  244. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  245. package/dist/modules/workflow/git/git.service.js +172 -36
  246. package/dist/modules/workflow/git/git.service.js.map +1 -1
  247. package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
  248. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  249. package/dist/modules/workflow/git/node-git-runner.js +78 -6
  250. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  251. package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
  252. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  253. package/dist/modules/workflow/pending-commits.service.js +25 -1
  254. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  255. package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
  256. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  257. package/dist/modules/workflow/pending-commits.worker.js +23 -6
  258. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  259. package/dist/modules/workflow/recovery-bot.js +1 -1
  260. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  261. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  262. package/dist/modules/workflow/review-workflow/review-workflow.service.js +2 -1
  263. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  264. package/dist/modules/workflow/sanitize-error.d.ts +16 -0
  265. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  266. package/dist/modules/workflow/sanitize-error.js +45 -0
  267. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  268. package/dist/modules/workflow/workflow.service.d.ts +183 -15
  269. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  270. package/dist/modules/workflow/workflow.service.js +805 -106
  271. package/dist/modules/workflow/workflow.service.js.map +1 -1
  272. package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
  273. package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
  274. package/dist/modules/workspace/agent-download.builder.js +238 -0
  275. package/dist/modules/workspace/agent-download.builder.js.map +1 -0
  276. package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
  277. package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
  278. package/dist/modules/workspace/agent-download.routes.js +156 -0
  279. package/dist/modules/workspace/agent-download.routes.js.map +1 -0
  280. package/dist/modules/workspace/agent-download.store.d.ts +225 -0
  281. package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
  282. package/dist/modules/workspace/agent-download.store.js +423 -0
  283. package/dist/modules/workspace/agent-download.store.js.map +1 -0
  284. package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
  285. package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
  286. package/dist/modules/workspace/agent-upload.store.js +16 -6
  287. package/dist/modules/workspace/agent-upload.store.js.map +1 -1
  288. package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
  289. package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
  290. package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
  291. package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
  292. package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
  293. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  294. package/dist/modules/workspace/file-readers/text-reader.js +5 -8
  295. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  296. package/dist/modules/workspace/first-run.d.ts +111 -0
  297. package/dist/modules/workspace/first-run.d.ts.map +1 -0
  298. package/dist/modules/workspace/first-run.js +181 -0
  299. package/dist/modules/workspace/first-run.js.map +1 -0
  300. package/dist/modules/workspace/move-links.d.ts +92 -0
  301. package/dist/modules/workspace/move-links.d.ts.map +1 -0
  302. package/dist/modules/workspace/move-links.js +173 -0
  303. package/dist/modules/workspace/move-links.js.map +1 -0
  304. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  305. package/dist/modules/workspace/startup/steps/seed-tree.js +3 -13
  306. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  307. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  308. package/dist/modules/workspace/workspace.routes.js +63 -22
  309. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  310. package/dist/modules/workspace/workspace.service.d.ts +75 -2
  311. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  312. package/dist/modules/workspace/workspace.service.js +197 -24
  313. package/dist/modules/workspace/workspace.service.js.map +1 -1
  314. package/dist/modules/workspace/workspace.tools.d.ts +59 -1
  315. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  316. package/dist/modules/workspace/workspace.tools.js +443 -65
  317. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  318. package/dist/modules/write-access/write-access.d.ts.map +1 -1
  319. package/dist/modules/write-access/write-access.js +9 -0
  320. package/dist/modules/write-access/write-access.js.map +1 -1
  321. package/dist/shared/domain-errors.d.ts +73 -1
  322. package/dist/shared/domain-errors.d.ts.map +1 -1
  323. package/dist/shared/domain-errors.js +86 -12
  324. package/dist/shared/domain-errors.js.map +1 -1
  325. package/dist/shared/git.contract.d.ts +10 -1
  326. package/dist/shared/git.contract.d.ts.map +1 -1
  327. package/dist/shared/git.contract.js.map +1 -1
  328. package/dist/shared/utf8-text.d.ts +15 -0
  329. package/dist/shared/utf8-text.d.ts.map +1 -0
  330. package/dist/shared/utf8-text.js +20 -0
  331. package/dist/shared/utf8-text.js.map +1 -0
  332. package/dist/tenancy/static-tenant-source.d.ts +2 -1
  333. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  334. package/dist/tenancy/static-tenant-source.js +9 -2
  335. package/dist/tenancy/static-tenant-source.js.map +1 -1
  336. package/mcp-app/page.html +349 -0
  337. package/migrations/0017_atlassian_account_links.sql +37 -0
  338. package/migrations/meta/0017_snapshot.json +2396 -0
  339. package/migrations/meta/_journal.json +8 -1
  340. package/package.json +5 -3
  341. package/src/__tests__/packaged-assets-ship.test.ts +16 -4
  342. package/src/assets.ts +28 -2
  343. package/src/core/__tests__/catalog-revision.test.ts +40 -0
  344. package/src/core/__tests__/gated-boot.test.ts +1 -0
  345. package/src/core/__tests__/lifecycle.test.ts +14 -0
  346. package/src/core/catalog-revision.ts +15 -2
  347. package/src/core/create-core-server.ts +67 -3
  348. package/src/core/create-core-services.ts +140 -6
  349. package/src/core/lifecycle.ts +28 -2
  350. package/src/core-config.ts +24 -1
  351. package/src/index.ts +2 -2
  352. package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
  353. package/src/modules/access/access-control.interface.ts +13 -0
  354. package/src/modules/access/access-control.service.ts +16 -0
  355. package/src/modules/access/directory-sync-bot.ts +1 -1
  356. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +58 -0
  357. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +19 -1
  358. package/src/modules/agent-guide/agent-guide.ts +9 -0
  359. package/src/modules/agent-guide/index.ts +1 -0
  360. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +14 -0
  361. package/src/modules/agent-instructions/compose.ts +17 -2
  362. package/src/modules/agent-instructions/shared-file-rules.ts +19 -19
  363. package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
  364. package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
  365. package/src/modules/audit/agent-audit.service.ts +58 -3
  366. package/src/modules/audit/agent-connection.routes.ts +56 -0
  367. package/src/modules/audit/audit.contract.ts +26 -0
  368. package/src/modules/audit/index.ts +1 -0
  369. package/src/modules/auth/__tests__/account.routes.test.ts +5 -3
  370. package/src/modules/auth/account.routes.ts +10 -3
  371. package/src/modules/auth/auth.middleware.ts +1 -1
  372. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
  373. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  374. package/src/modules/database/core-schema.ts +33 -3
  375. package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
  376. package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
  377. package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
  378. package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
  379. package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
  380. package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
  381. package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
  382. package/src/modules/embed/account-link.service.ts +76 -0
  383. package/src/modules/embed/embed-link.ts +180 -0
  384. package/src/modules/embed/embed.errors.ts +43 -0
  385. package/src/modules/embed/embed.interface.ts +203 -0
  386. package/src/modules/embed/embed.routes.ts +363 -0
  387. package/src/modules/embed/embed.service.ts +705 -0
  388. package/src/modules/embed/embed.tools.ts +206 -0
  389. package/src/modules/embed/mcp-app.routes.ts +41 -0
  390. package/src/modules/embed/mcp-app.ts +173 -0
  391. package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
  392. package/src/modules/github-app/github-app.connection.ts +38 -4
  393. package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
  394. package/src/modules/kb-fs/locking-filesystem.ts +287 -0
  395. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
  396. package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
  397. package/src/modules/kb-sync/kb-sync.service.ts +6 -0
  398. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
  399. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
  400. package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
  401. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
  402. package/src/modules/mcp/__tests__/mcp.service.test.ts +23 -13
  403. package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
  404. package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
  405. package/src/modules/mcp/mcp.service.ts +81 -12
  406. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
  407. package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
  408. package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
  409. package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
  410. package/src/modules/onboarding/onboarding.contract.ts +85 -0
  411. package/src/modules/onboarding/onboarding.routes.ts +85 -0
  412. package/src/modules/onboarding/starter-pack.service.ts +580 -0
  413. package/src/modules/onboarding/starter-packs.ts +182 -0
  414. package/src/modules/plugins/__tests__/plugins.tools.test.ts +25 -2
  415. package/src/modules/plugins/index.ts +1 -0
  416. package/src/modules/plugins/plugin-provision.service.ts +22 -1
  417. package/src/modules/plugins/plugins.routes.ts +29 -0
  418. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +150 -1
  419. package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
  420. package/src/modules/settings/deployment-settings.service.ts +131 -1
  421. package/src/modules/settings/repository-source.ts +4 -4
  422. package/src/modules/skills/__tests__/skills.tools.description.test.ts +5 -1
  423. package/src/modules/skills/skills.tools.ts +55 -35
  424. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
  425. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
  426. package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
  427. package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
  428. package/src/modules/tool-auth/external-api-key.service.ts +61 -4
  429. package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
  430. package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
  431. package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
  432. package/src/modules/tool-helpers/index.ts +34 -1
  433. package/src/modules/tool-helpers/route-argument-check.ts +149 -0
  434. package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
  435. package/src/modules/tool-helpers/tool-def.ts +132 -7
  436. package/src/modules/tool-helpers/tool-handler.ts +196 -8
  437. package/src/modules/tool-helpers/tool.contract.ts +9 -0
  438. package/src/modules/tool-manuals/tool-manuals.tools.ts +15 -5
  439. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +14 -1
  440. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +23 -10
  441. package/src/modules/tool-registry/description-length.ts +34 -7
  442. package/src/modules/tool-registry/guide-first.ts +8 -1
  443. package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
  444. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
  445. package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
  446. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
  447. package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
  448. package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
  449. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
  450. package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
  451. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
  452. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
  453. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
  454. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
  455. package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +41 -5
  456. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +127 -2
  457. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +157 -122
  458. package/src/modules/workflow/agent-tools/workflow.tools.ts +129 -35
  459. package/src/modules/workflow/file-lock.service.ts +67 -5
  460. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
  461. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
  462. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
  463. package/src/modules/workflow/git/git.service.ts +176 -38
  464. package/src/modules/workflow/git/node-git-runner.ts +82 -7
  465. package/src/modules/workflow/pending-commits.service.ts +29 -1
  466. package/src/modules/workflow/pending-commits.worker.ts +24 -6
  467. package/src/modules/workflow/recovery-bot.ts +1 -1
  468. package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -1
  469. package/src/modules/workflow/sanitize-error.ts +50 -0
  470. package/src/modules/workflow/workflow.service.ts +879 -112
  471. package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
  472. package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
  473. package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
  474. package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
  475. package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
  476. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
  477. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
  478. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
  479. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
  480. package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
  481. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
  482. package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
  483. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
  484. package/src/modules/workspace/__tests__/workspace.tools.test.ts +409 -12
  485. package/src/modules/workspace/agent-download.builder.ts +308 -0
  486. package/src/modules/workspace/agent-download.routes.ts +183 -0
  487. package/src/modules/workspace/agent-download.store.ts +533 -0
  488. package/src/modules/workspace/agent-upload.store.ts +23 -7
  489. package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
  490. package/src/modules/workspace/file-readers/text-reader.ts +5 -8
  491. package/src/modules/workspace/first-run.ts +222 -0
  492. package/src/modules/workspace/move-links.ts +237 -0
  493. package/src/modules/workspace/startup/steps/seed-tree.ts +3 -12
  494. package/src/modules/workspace/workspace.routes.ts +64 -22
  495. package/src/modules/workspace/workspace.service.ts +202 -25
  496. package/src/modules/workspace/workspace.tools.ts +492 -63
  497. package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
  498. package/src/modules/write-access/write-access.ts +9 -0
  499. package/src/shared/domain-errors.ts +108 -11
  500. package/src/shared/git.contract.ts +8 -1
  501. package/src/shared/utf8-text.ts +21 -0
  502. package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
  503. package/src/tenancy/static-tenant-source.ts +10 -2
  504. package/starter-packs/README.md +103 -0
  505. package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
  506. package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
  507. package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
  508. package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
  509. package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
  510. package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
  511. package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
  512. package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
  513. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
  514. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
  515. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
  516. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
  517. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
  518. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
  519. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
  520. package/starter-packs/engineering/pack.yaml +11 -0
  521. package/starter-packs/general/KnowledgeBase/About us.md +9 -0
  522. package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
  523. package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
  524. package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
  525. package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
  526. package/starter-packs/general/Plugins/general-starter/access.md +19 -0
  527. package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
  528. package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
  529. package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
  530. package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
  531. package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
  532. package/starter-packs/general/pack.yaml +10 -0
  533. package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
  534. package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
  535. package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
  536. package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
  537. package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
  538. package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
  539. package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
  540. package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
  541. package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
  542. package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
  543. package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
  544. package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
  545. package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
  546. package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
  547. package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
  548. package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
  549. package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
  550. package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
  551. package/starter-packs/operations/pack.yaml +12 -0
  552. package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
  553. package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
  554. package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
  555. package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
  556. package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
  557. package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
  558. package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
  559. package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
  560. package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
  561. package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
  562. package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
  563. package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
  564. package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
  565. package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
  566. package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
  567. package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
  568. package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
  569. package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
  570. package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
  571. package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
  572. package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
  573. package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
  574. package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
  575. package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
  576. package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
  577. package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
  578. package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
  579. package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
  580. package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
  581. package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
  582. package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
  583. package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
  584. package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
  585. package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
  586. package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
  587. package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
  588. package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
  589. package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
  590. package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
  591. package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
  592. package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
  593. package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
  594. package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
  595. package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
  596. package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
  597. package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
  598. package/starter-packs/sales/pack.yaml +13 -0
  599. package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
  600. package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
  601. package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
  602. package/starter-packs/support/KnowledgeBase/Products.md +9 -0
  603. package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
  604. package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
  605. package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
  606. package/starter-packs/support/Plugins/support-starter/access.md +19 -0
  607. package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
  608. package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
  609. package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
  610. package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
  611. package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
  612. package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
  613. package/starter-packs/support/pack.yaml +12 -0
@@ -3,6 +3,7 @@ import type { IToolRegistry, UtcpTool } from '../tool-registry/tool.contract.js'
3
3
  import type { KbContext } from '../../shared/kb-context.js';
4
4
  import type { ToolContext } from '../tool-helpers/tool.contract.js';
5
5
  import { toolDef } from '../tool-helpers/tool-def.js';
6
+ import { declareRouteTool } from '../tool-helpers/route-tool-schemas.js';
6
7
  import type { ToolHandlerFactory } from '../tool-helpers/tool-handler.js';
7
8
  import type { IAccessControl } from '../access/access-control.interface.js';
8
9
  import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
@@ -54,6 +55,11 @@ export function registerToolManualsTools(
54
55
  ): void {
55
56
  registry.registerExternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
56
57
  registry.registerInternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
58
+ // The def above is built per catalog listing (its description names the
59
+ // tools configured for THIS caller), so declare the arguments here as well:
60
+ // a direct REST call that lands before the first listing must be checked
61
+ // against them too.
62
+ declareRouteTool('list_local_tools', LIST_LOCAL_TOOLS_INPUTS);
57
63
 
58
64
  router.post(
59
65
  '/agent/tools/list_local_tools',
@@ -79,13 +85,14 @@ export function registerToolManualsTools(
79
85
  // answers and the four things an agent cannot read off a field.
80
86
  //
81
87
  // It sits a few characters under `TOOL_DESCRIPTION_CAP` WITH the guide-first
82
- // sentence the registry puts in front of it (`tool-registry/guide-first.ts`),
88
+ // sentence the registry puts in front of it (`tool-registry/guide-first.ts`)
89
+ // and the `Call:` line generated ahead of everything,
83
90
  // which is why every sentence here is the short form: the `hiddenTools`
84
91
  // clause and that opener both came out of the same budget, and what a
85
92
  // hidden tool's entry CONTAINS is on the field below rather than here.
86
93
  description:
87
- 'Configuration status of every `.tool` the current user can access: what each tool needs set up and what is ' +
88
- 'already configured, as `{ tools, invalid, onBranchOnly, note? }`. Scoped to the CALLER: a `.tool` it cannot ' +
94
+ 'Configuration status of every `.tool` the current user can access: what each needs set up and what is ' +
95
+ 'configured, as `{ tools, invalid, onBranchOnly, note? }`. Scoped to the CALLER: a `.tool` it cannot ' +
89
96
  'READ is absent, and every flag is the caller\'s own. ' +
90
97
  'Secret VALUES are never returned and can never be set through a tool: an admin enters them in the tool ' +
91
98
  'editor; users sign in on /connect. ' +
@@ -96,7 +103,7 @@ export function registerToolManualsTools(
96
103
  'The listing is the RELEASED catalog, built from the default branch only: a server or `.tool` you declared on a ' +
97
104
  'draft is not listed, callable or signed-in-able until it is merged. Pass `branch` (the draft you wrote the ' +
98
105
  'declaration on) and `onBranchOnly` names every tool declared there that the default branch does not serve ' +
99
- 'yet — open a change request and ask the user to merge it in the app to activate it.',
106
+ 'yet — open a change request and ask the user to merge it.',
100
107
  path: '/api/agent/tools/list_tool_setup',
101
108
  inputs: {
102
109
  type: 'object',
@@ -303,6 +310,9 @@ async function localToolsLine(svc: IToolManualService, userEmail?: string): Prom
303
310
  return `Local-only tools configured for you: ${tools.map((t) => `\`${t.name}\``).join(', ')}.`;
304
311
  }
305
312
 
313
+ /** `list_local_tools` takes nothing; a const so the route and the def share one declaration. */
314
+ const LIST_LOCAL_TOOLS_INPUTS = { type: 'object' as const, properties: {}, additionalProperties: false };
315
+
306
316
  async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: string): Promise<UtcpTool> {
307
317
  return toolDef({
308
318
  name: 'list_local_tools',
@@ -318,7 +328,7 @@ async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: strin
318
328
  'setup by hand. ' +
319
329
  (await localToolsLine(svc, userEmail)),
320
330
  path: '/api/agent/tools/list_local_tools',
321
- inputs: { type: 'object', properties: {}, additionalProperties: false },
331
+ inputs: LIST_LOCAL_TOOLS_INPUTS,
322
332
  outputs: {
323
333
  type: 'object',
324
334
  properties: {
@@ -9,6 +9,7 @@ import { registerWorkspaceTools } from '../../workspace/workspace.tools.js';
9
9
  import { registerSkillsTools } from '../../skills/skills.tools.js';
10
10
  import { registerPluginsTools } from '../../plugins/plugins.tools.js';
11
11
  import { registerToolManualsTools } from '../../tool-manuals/tool-manuals.tools.js';
12
+ import { registerEmbedTools } from '../../embed/embed.tools.js';
12
13
  import { registerAgentGuideTool } from '../../agent-guide/agent-guide.tools.js';
13
14
  import { ToolDescriptionNotes } from '../../workspace/agent-access.gate.js';
14
15
  import { testKbContext } from '../../../__tests__/kb-context.js';
@@ -58,7 +59,7 @@ const CORE_SERVER_SOURCE = new URL('../../../core/create-core-server.ts', import
58
59
  const MODULES: ReadonlyArray<{ name: string; register: (registry: ToolRegistry) => void }> = [
59
60
  {
60
61
  name: 'registerWorkflowTools',
61
- register: (registry) => registerWorkflowTools(registry, express.Router(), pass, handler, testKbContext()),
62
+ register: (registry) => registerWorkflowTools(registry, express.Router(), pass, handler, testKbContext(), async () => true),
62
63
  },
63
64
  {
64
65
  name: 'registerChangeRequestReadTools',
@@ -93,6 +94,18 @@ const MODULES: ReadonlyArray<{ name: string; register: (registry: ToolRegistry)
93
94
  register: (registry) => registerSkillsTools(registry, express.Router(), pass, handler, nothing),
94
95
  },
95
96
  { name: 'registerPluginsTools', register: (registry) => registerPluginsTools(registry, testKbContext()) },
97
+ {
98
+ name: 'registerEmbedTools',
99
+ register: (registry) =>
100
+ registerEmbedTools(registry, express.Router(), pass, handler, {
101
+ embedService: nothing,
102
+ kb: testKbContext(),
103
+ readForTool: nothing,
104
+ canBeReached: () => true,
105
+ appUrlFor: (repoRelative) => `https://example.test/workspace/main/knowledge-base/${repoRelative}`,
106
+ notes: new ToolDescriptionNotes(),
107
+ }),
108
+ },
96
109
  {
97
110
  name: 'registerToolManualsTools',
98
111
  register: (registry) =>
@@ -4,7 +4,9 @@ import {
4
4
  CALL_TOOL_CHAIN_NAME,
5
5
  CHAIN_FAILURES_RULE,
6
6
  CHAIN_LARGE_RESULTS_RULE,
7
+ callLine,
7
8
  codeModeMetaTools,
9
+ splitCallLine,
8
10
  } from '@bevel-software/platform-mcp-core';
9
11
  import { testKbContext } from '../../../__tests__/kb-context.js';
10
12
  import { EXTERNAL_KB_MANUAL_NAME } from '../../tool-manuals/tool-manuals.contract.js';
@@ -103,8 +105,12 @@ async function hexisTools(): Promise<UtcpTool[]> {
103
105
  // every real composition supplies one: without it they would be the two
104
106
  // descriptions this suite never measured.
105
107
  unused(),
108
+ // The guide reader, not needed to build defs; then the download store,
109
+ // mounted on the same terms as the upload one.
110
+ undefined,
111
+ unused(),
106
112
  );
107
- registerWorkflowTools(registry, router, toolAuth, toolHandler, kb);
113
+ registerWorkflowTools(registry, router, toolAuth, toolHandler, kb, async () => true);
108
114
  registerPluginsTools(registry, kb);
109
115
  registerSkillsTools(registry, router, toolAuth, toolHandler, emptySkills);
110
116
  registerToolManualsTools(registry, router, toolAuth, toolHandler, emptyManuals, {
@@ -170,8 +176,10 @@ describe('no Hexis tool description is long enough to be cut', () => {
170
176
  // ONE predicate for both halves, so no description falls between them: a
171
177
  // tool whose own description is empty is served the sentence alone, and
172
178
  // one with text after it has whitespace between — not `here.Read`.
179
+ // Behind the `Call:` line, when the description already carries one (the
180
+ // meta-tools are built with theirs): that line comes first on every tool.
173
181
  const opensWithGuide = (t: UtcpTool): boolean => {
174
- const description = t.description ?? '';
182
+ const description = splitCallLine(t.description ?? '').rest;
175
183
  if (description === GUIDE_FIRST_SENTENCE) return true;
176
184
  return description.startsWith(GUIDE_FIRST_SENTENCE) && /^\s/.test(description.slice(GUIDE_FIRST_SENTENCE.length));
177
185
  };
@@ -202,12 +210,15 @@ describe('no Hexis tool description is long enough to be cut', () => {
202
210
  it('fails, naming the tool, when a paragraph takes a description over the cap', () => {
203
211
  const padded = { name: 'write_file', description: 'x'.repeat(TOOL_DESCRIPTION_CAP + 1) } as UtcpTool;
204
212
  expect(clientVisibleLength(padded)).toBeGreaterThan(TOOL_DESCRIPTION_CAP);
205
- // A tool with no description at all is not over the cap.
206
- expect(clientVisibleLength({ name: 'nothing' } as UtcpTool)).toBe(0);
207
- // Unless it is a PREFIXED one: the prefix is sent on its own then (no
208
- // description, so no blank line either), and that text is what the client
209
- // was handed. Measuring it as nothing would hide the only thing it got.
210
- expect(clientVisibleLength({ name: 'read_file' } as UtcpTool)).toBe(TOOL_PREFIX_CAP);
213
+ // A tool with no description at all is handed its `Call:` line alone —
214
+ // the example counts toward the cap like any other text a client gets.
215
+ const nothingCall = callLine(`${EXTERNAL_KB_MANUAL_NAME}.nothing`, undefined);
216
+ expect(clientVisibleLength({ name: 'nothing' } as UtcpTool)).toBe(nothingCall.length);
217
+ // A PREFIXED one is handed the prefix too, behind the line (no
218
+ // description, so no blank line after the prefix), and that text is what
219
+ // the client was handed. Measuring it as nothing would hide what it got.
220
+ const readCall = callLine(`${EXTERNAL_KB_MANUAL_NAME}.read_file`, undefined);
221
+ expect(clientVisibleLength({ name: 'read_file' } as UtcpTool)).toBe(readCall.length + 2 + TOOL_PREFIX_CAP);
211
222
  });
212
223
 
213
224
  it('measures the catalog, which is what a client lists — and says what is outside it', async () => {
@@ -292,13 +303,15 @@ describe('the shared rules describe the tools they name', () => {
292
303
  // to the rules. Composed at the mount, because `mcp-core` builds the
293
304
  // constant without knowing where this deployment states them.
294
305
  const served = (await hexisTools()).find((t) => t.name === CALL_TOOL_CHAIN_NAME)!;
295
- expect(served.description!.startsWith(`${GUIDE_FIRST_SENTENCE} `)).toBe(true);
306
+ // Behind its `Call:` line, which is the first line of every description.
307
+ expect(served.description!.startsWith('Call: call_tool_chain(')).toBe(true);
308
+ expect(splitCallLine(served.description!).rest.startsWith(`${GUIDE_FIRST_SENTENCE} `)).toBe(true);
296
309
  expect(served.description!.split(GUIDE_FIRST_SENTENCE)).toHaveLength(2);
297
310
  expect(served.description!.trimEnd().endsWith('.')).toBe(true);
298
311
  expect(clientVisibleLength(served)).toBeLessThanOrEqual(TOOL_DESCRIPTION_CAP);
299
312
  // The other two open the same way: the guide comes before the registry too.
300
313
  for (const tool of servedMetaTools([]).filter((t) => t.name !== CALL_TOOL_CHAIN_NAME)) {
301
- expect(tool.description!.startsWith(`${GUIDE_FIRST_SENTENCE} `), tool.name).toBe(true);
314
+ expect(splitCallLine(tool.description!).rest.startsWith(`${GUIDE_FIRST_SENTENCE} `), tool.name).toBe(true);
302
315
  }
303
316
  });
304
317
 
@@ -11,8 +11,10 @@
11
11
  * what makes the cap below reachable rather than aspirational.
12
12
  */
13
13
 
14
+ import { callLine, splitCallLine } from '@bevel-software/platform-mcp-core';
14
15
  import { TOOL_PREFIX_CAP } from '@bevel-software/platform-shared';
15
16
  import { PREFIXED_TOOLS } from '../agent-instructions/compose.js';
17
+ import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/tool-manuals.contract.js';
16
18
  import { GUIDE_FIRST_SENTENCE } from './guide-first.js';
17
19
  import type { UtcpTool } from './tool.contract.js';
18
20
 
@@ -61,6 +63,25 @@ export const TOOL_DESCRIPTION_CAP = 1_200;
61
63
  */
62
64
  export const CLIENT_SHORT_CUT = 500;
63
65
 
66
+ /** What these measurements read of a tool: its name, its text and the schema its `Call:` line is generated from. */
67
+ export type MeasuredTool = Pick<UtcpTool, 'name' | 'description'> & { inputs?: unknown };
68
+
69
+ /**
70
+ * The `Call:` line a client is handed ahead of everything else, and the blank
71
+ * line after it — counted, because it is part of what a client cuts. Taken
72
+ * from the description when it already opens with one (a meta-tool is built
73
+ * with its line), generated from the tool's inputs under the namespace the
74
+ * hosted endpoint serves otherwise, as mcp-core's `withCallExample` does at
75
+ * listing time. The rest of the description comes back beside it.
76
+ */
77
+ function splitServedCallLine(tool: MeasuredTool): { callChars: number; rest: string } {
78
+ const { call, rest } = splitCallLine(tool.description ?? '');
79
+ const line = call ?? callLine(`${EXTERNAL_KB_MANUAL_NAME}.${tool.name}`, tool.inputs);
80
+ // The blank line after it only when something follows it.
81
+ const followed = rest !== '' || PREFIXED_TOOLS.has(tool.name);
82
+ return { callChars: line.length + (followed ? 2 : 0), rest };
83
+ }
84
+
64
85
  /**
65
86
  * Where the tool's OWN opening sentence ends in the text a client is handed:
66
87
  * the purpose prefix counted at its cap, as in {@link clientVisibleLength},
@@ -71,9 +92,9 @@ export const CLIENT_SHORT_CUT = 500;
71
92
  * A description with no sentence-ending punctuation counts whole — the honest
72
93
  * answer for text that never finishes a sentence.
73
94
  */
74
- export function firstSentenceEnd(tool: Pick<UtcpTool, 'name' | 'description'>): number {
75
- const prefix = PREFIXED_TOOLS.has(tool.name) ? TOOL_PREFIX_CAP + 2 : 0;
76
- const description = tool.description ?? '';
95
+ export function firstSentenceEnd(tool: MeasuredTool): number {
96
+ const { callChars, rest: description } = splitServedCallLine(tool);
97
+ const prefix = callChars + (PREFIXED_TOOLS.has(tool.name) ? TOOL_PREFIX_CAP + 2 : 0);
77
98
  if (description === '') return prefix;
78
99
  // The opener and whatever whitespace follows it: `guideFirstDescription`
79
100
  // joins with one space, and leaves a description that already opens with
@@ -97,13 +118,19 @@ export function firstSentenceEnd(tool: Pick<UtcpTool, 'name' | 'description'>):
97
118
  * The guide-first sentence every listed tool opens with is already in the
98
119
  * description the registry lists, so it is measured as the text it is.
99
120
  */
100
- export function clientVisibleLength(tool: Pick<UtcpTool, 'name' | 'description'>): number {
101
- const own = tool.description?.length ?? 0;
121
+ export function clientVisibleLength(tool: MeasuredTool): number {
122
+ const { callChars, rest } = splitServedCallLine(tool);
123
+ return callChars + ownVisibleLength(tool.name, rest);
124
+ }
125
+
126
+ /** {@link clientVisibleLength} without the `Call:` line: the purpose prefix and the tool's own text. */
127
+ function ownVisibleLength(name: string, description: string): number {
128
+ const own = description.length;
102
129
  // A prefixed tool with no description of its own is still handed the prefix,
103
130
  // and nothing else — `prefixToolDescription` sends the prefix alone, with no
104
131
  // blank line after it. Measuring that as zero would under-report the only
105
132
  // text the client got.
106
- if (own === 0) return PREFIXED_TOOLS.has(tool.name) ? TOOL_PREFIX_CAP : 0;
133
+ if (own === 0) return PREFIXED_TOOLS.has(name) ? TOOL_PREFIX_CAP : 0;
107
134
  // `+ 2` for the blank line `prefixToolDescription` puts between the two.
108
- return PREFIXED_TOOLS.has(tool.name) ? own + TOOL_PREFIX_CAP + 2 : own;
135
+ return PREFIXED_TOOLS.has(name) ? own + TOOL_PREFIX_CAP + 2 : own;
109
136
  }
@@ -1,3 +1,4 @@
1
+ import { splitCallLine } from '@bevel-software/platform-mcp-core';
1
2
  import type { UtcpTool } from './tool.contract.js';
2
3
 
3
4
  /** The one tool that returns the platform's guide (see `modules/agent-guide`). */
@@ -19,9 +20,15 @@ export const GUIDE_FIRST_SENTENCE = `Call \`${GET_AGENT_GUIDE_TOOL}\` first and
19
20
  * sentence is put in front of a description — the registry for the tools it
20
21
  * lists, the MCP service for the meta-tools it builds itself — so the two
21
22
  * cannot drift apart on what "once" means.
23
+ *
24
+ * Behind a `Call:` line, never ahead of it: the call example is the first
25
+ * line of every description (see mcp-core's `withCallExample`), and the
26
+ * sentence opens what follows it.
22
27
  */
23
28
  export function guideFirstDescription(description: string | undefined): string {
24
- const own = description ?? '';
29
+ const { call, rest } = splitCallLine(description ?? '');
30
+ if (call !== null) return `${call}\n\n${guideFirstDescription(rest)}`;
31
+ const own = rest;
25
32
  if (own.startsWith(GUIDE_FIRST_SENTENCE)) return own;
26
33
  return own ? `${GUIDE_FIRST_SENTENCE} ${own}` : GUIDE_FIRST_SENTENCE;
27
34
  }
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import type { AuthUser } from '@bevel-software/platform-shared';
3
+ import type { Database } from '../../database/connection.js';
4
+ import { FileLockService } from '../file-lock.service.js';
5
+ import { WorkflowValidationError } from '../../../shared/domain-errors.js';
6
+ import { makeFakeLockDb, type FakeLockDb } from './fake-file-lock-db.js';
7
+
8
+ /**
9
+ * A deletion runs its last "is any save landing?" check inside
10
+ * `whileNoneAcquired`, and a save starts by taking its file lock. An acquire
11
+ * that passed the gate before the deletion began, but whose row was still
12
+ * being written, must be seen by that check — not land after it.
13
+ */
14
+
15
+ const ALICE: AuthUser = { id: '11111111-1111-4111-8111-111111111111', email: 'alice@example.com', name: 'Alice' };
16
+ const WS = 'alice%2Fdraft';
17
+ const BRANCH = 'alice/draft';
18
+ const PATH = 'knowledge-base/a.md';
19
+
20
+ /** The fake lock db, with every INSERT held until `release()`. */
21
+ function withHeldInserts(fake: FakeLockDb): { db: Database; release: () => void } {
22
+ let release!: () => void;
23
+ const gate = new Promise<void>((resolve) => {
24
+ release = resolve;
25
+ });
26
+ const base = fake.db as unknown as { insert: () => { values: (row: unknown) => Record<string, unknown> } };
27
+ const db = {
28
+ ...base,
29
+ insert: () => ({
30
+ values: (row: unknown) => {
31
+ const chain = base.insert().values(row) as {
32
+ onConflictDoUpdate: (arg: unknown) => unknown;
33
+ returning: () => Promise<unknown>;
34
+ };
35
+ const held = {
36
+ onConflictDoUpdate: (arg: unknown) => {
37
+ chain.onConflictDoUpdate(arg);
38
+ return held;
39
+ },
40
+ returning: async () => {
41
+ await gate;
42
+ return chain.returning();
43
+ },
44
+ };
45
+ return held;
46
+ },
47
+ }),
48
+ } as unknown as Database;
49
+ return { db, release };
50
+ }
51
+
52
+ describe('FileLockService deletion gate', () => {
53
+ let fake: FakeLockDb;
54
+
55
+ beforeEach(() => {
56
+ fake = makeFakeLockDb();
57
+ });
58
+
59
+ it('waits for an acquire already under way, so the check inside sees its lock', async () => {
60
+ const { db, release } = withHeldInserts(fake);
61
+ const locks = new FileLockService(db);
62
+
63
+ const acquire = locks.acquire(WS, BRANCH, PATH, ALICE);
64
+ let sawLock: boolean | null = null;
65
+ const deletion = locks.whileNoneAcquired(BRANCH, async () => {
66
+ sawLock = await locks.hasAnyActive(WS);
67
+ });
68
+
69
+ await new Promise((r) => setTimeout(r, 10));
70
+ // The acquire's row is not written yet, so the check has not run.
71
+ expect(sawLock).toBeNull();
72
+
73
+ release();
74
+ await expect(acquire).resolves.toMatchObject({ acquired: true });
75
+ await deletion;
76
+ expect(sawLock).toBe(true);
77
+ });
78
+
79
+ it('refuses an acquire that starts while the deletion runs', async () => {
80
+ const locks = new FileLockService(fake.db);
81
+ let refused: unknown = null;
82
+ await locks.whileNoneAcquired(BRANCH, async () => {
83
+ refused = await locks.acquire(WS, BRANCH, PATH, ALICE).catch((err: unknown) => err);
84
+ });
85
+ expect(refused).toBeInstanceOf(WorkflowValidationError);
86
+ expect(fake.rows()).toHaveLength(0);
87
+ // And grants it again once the deletion is over.
88
+ await expect(locks.acquire(WS, BRANCH, PATH, ALICE)).resolves.toMatchObject({ acquired: true });
89
+ });
90
+
91
+ it('does not wait for an acquire on another branch', async () => {
92
+ const { db, release } = withHeldInserts(fake);
93
+ const locks = new FileLockService(db);
94
+
95
+ const other = locks.acquire('bob%2Fdraft', 'bob/draft', PATH, ALICE);
96
+ let ran = false;
97
+ await locks.whileNoneAcquired(BRANCH, async () => {
98
+ ran = true;
99
+ });
100
+ expect(ran).toBe(true);
101
+ release();
102
+ await other;
103
+ });
104
+ });
@@ -109,3 +109,69 @@ describe('PendingCommitsService.markNeedsAttentionInWorkspace', () => {
109
109
  await expect(new PendingCommitsService(db).markNeedsAttentionInWorkspace('main', 'whatever')).resolves.toBe(0);
110
110
  });
111
111
  });
112
+
113
+ /**
114
+ * What `mergeBranch` waits on: the commits still between a branch's saved
115
+ * files and its remote. `pending` and `running` both count — a row the worker
116
+ * holds is not pushed yet — and a `needs_attention` row is reported with its
117
+ * message, because nothing will land it without a person.
118
+ */
119
+ describe('PendingCommitsService.queuedOnBranch', () => {
120
+ function selectDb(rows: Array<{ status: string; lastError: string | null }>) {
121
+ const orderBy = vi.fn(async () => rows);
122
+ const where = vi.fn(() => ({ orderBy }));
123
+ const db = { select: vi.fn(() => ({ from: () => ({ where }) })) } as unknown as Database;
124
+ return { db, where, orderBy };
125
+ }
126
+
127
+ it('counts pending and running rows, and reports no attention when none is stuck', async () => {
128
+ const { db, where } = selectDb([
129
+ { status: 'pending', lastError: null },
130
+ { status: 'running', lastError: null },
131
+ ]);
132
+ await expect(new PendingCommitsService(db).queuedOnBranch('alice/draft')).resolves.toEqual({
133
+ queued: 2,
134
+ stuck: 0,
135
+ needsAttention: null,
136
+ });
137
+ // Keyed on the branch column.
138
+ const sql = new PgDialect().sqlToQuery((where.mock.calls[0] as unknown as [SQL])[0]);
139
+ expect(sql.sql).toMatch(/"branch" = \$1/);
140
+ expect(sql.params).toEqual(['alice/draft']);
141
+ });
142
+
143
+ it('reports the oldest needs_attention row\'s message alongside the count', async () => {
144
+ const { db, orderBy } = selectDb([
145
+ { status: 'pending', lastError: null },
146
+ { status: 'needs_attention', lastError: 'push rejected' },
147
+ { status: 'needs_attention', lastError: 'later' },
148
+ ]);
149
+ await expect(new PendingCommitsService(db).queuedOnBranch('alice/draft')).resolves.toEqual({
150
+ queued: 1,
151
+ stuck: 2,
152
+ needsAttention: 'push rejected',
153
+ });
154
+ // "Oldest" is the query's order, so every call reports the same message.
155
+ const dialect = new PgDialect();
156
+ const order = (orderBy.mock.calls[0] as unknown as SQL[]).map((o) => dialect.sqlToQuery(o).sql);
157
+ expect(order).toEqual(['"pending_commits"."queued_at" asc', '"pending_commits"."id" asc']);
158
+ });
159
+
160
+ it('falls back to a message when a stuck row has an empty error, so it is never read as retryable', async () => {
161
+ const { db } = selectDb([{ status: 'needs_attention', lastError: '' }]);
162
+ await expect(new PendingCommitsService(db).queuedOnBranch('alice/draft')).resolves.toEqual({
163
+ queued: 0,
164
+ stuck: 1,
165
+ needsAttention: 'A queued commit on this branch failed and needs a person.',
166
+ });
167
+ });
168
+
169
+ it('answers nothing queued for a quiet branch', async () => {
170
+ const { db } = selectDb([]);
171
+ await expect(new PendingCommitsService(db).queuedOnBranch('quiet')).resolves.toEqual({
172
+ queued: 0,
173
+ stuck: 0,
174
+ needsAttention: null,
175
+ });
176
+ });
177
+ });
@@ -139,6 +139,30 @@ describe('PendingCommitsWorker.drainOnce', () => {
139
139
  expect(feedback.send).not.toHaveBeenCalled();
140
140
  });
141
141
 
142
+ /**
143
+ * The author's synthetic id reaches log lines (`user=<id>` when a push or
144
+ * its recovery fails), and log lines carry no email addresses: the id is
145
+ * opaque, and the same person gets the same one however their address was
146
+ * typed.
147
+ */
148
+ it("the commit author's id carries no address, and is the same for one person", async () => {
149
+ const seen: string[] = [];
150
+ workflow.runPendingCommit.mockImplementation(async (_ws: string, _b: string, _p: string, user: AuthUser) => {
151
+ seen.push(user.id);
152
+ });
153
+ for (const authorEmail of ['alice@example.com', 'Alice@Example.com ', 'bob@example.com']) {
154
+ (service.claimNext as ReturnType<typeof vi.fn>).mockResolvedValueOnce(makeRow({ authorEmail }));
155
+ await worker.drainOnce();
156
+ }
157
+ expect(seen).toHaveLength(3);
158
+ for (const id of seen) {
159
+ expect(id).not.toContain('@');
160
+ expect(id).not.toMatch(/alice|bob|example/i);
161
+ }
162
+ expect(seen[0]).toBe(seen[1]);
163
+ expect(seen[0]).not.toBe(seen[2]);
164
+ });
165
+
142
166
  it('skips work entirely when claimNext returns null (idle queue)', async () => {
143
167
  (service.claimNext as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
144
168
 
@@ -225,7 +249,11 @@ describe('PendingCommitsWorker.drainOnce', () => {
225
249
  expect(call.message).toContain('ws-1');
226
250
  expect(call.message).toContain('Foo.md');
227
251
  expect(call.message).toContain('unrecoverable');
228
- expect(call.message).toContain('alice@example.com');
252
+ // The author is on the queue row the notice names — never in the body,
253
+ // which a core-only deployment writes to its log.
254
+ expect(call.message).toContain('Queue row: row-1');
255
+ expect(call.message).not.toContain('@');
256
+ expect(call.message).not.toContain('Alice');
229
257
  });
230
258
 
231
259
  it('a path-outside-repo refusal escalates at once: no retry, no recovery agent, the notice names the corrected path', async () => {
@@ -258,7 +286,8 @@ describe('PendingCommitsWorker.drainOnce', () => {
258
286
  // The correction survives the truncation: it rides on the error payload,
259
287
  // not on the message.
260
288
  expect(call.message).toContain('Use instead: knowledge-base/KnowledgeBase/Reviews/PR-12.html');
261
- expect(call.message).toContain('alice@example.com');
289
+ expect(call.message).toContain('Queue row: row-1');
290
+ expect(call.message).not.toContain('@');
262
291
  });
263
292
 
264
293
  it('a feedback-sink failure does not throw — the row stays needs_attention regardless', async () => {