@bevel-software/platform-core-backend 0.26.0 → 0.28.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 (681) 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 +56 -5
  17. package/dist/core/create-core-server.js.map +1 -1
  18. package/dist/core/create-core-services.d.ts +33 -0
  19. package/dist/core/create-core-services.d.ts.map +1 -1
  20. package/dist/core/create-core-services.js +105 -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 +17 -0
  35. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  36. package/dist/modules/access/access-control.service.d.ts +2 -0
  37. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  38. package/dist/modules/access/access-control.service.js +31 -0
  39. package/dist/modules/access/access-control.service.js.map +1 -1
  40. package/dist/modules/access/access.routes.d.ts +4 -1
  41. package/dist/modules/access/access.routes.d.ts.map +1 -1
  42. package/dist/modules/access/access.routes.js +66 -7
  43. package/dist/modules/access/access.routes.js.map +1 -1
  44. package/dist/modules/access/directory-sync-bot.js +1 -1
  45. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  46. package/dist/modules/access/roles-admin.service.d.ts +35 -6
  47. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  48. package/dist/modules/access/roles-admin.service.js +36 -9
  49. package/dist/modules/access/roles-admin.service.js.map +1 -1
  50. package/dist/modules/access/user-access-removal.service.js +1 -1
  51. package/dist/modules/access/user-access-removal.service.js.map +1 -1
  52. package/dist/modules/agent-guide/agent-guide.d.ts +6 -0
  53. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  54. package/dist/modules/agent-guide/agent-guide.js +8 -0
  55. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  56. package/dist/modules/agent-guide/index.d.ts +1 -1
  57. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  58. package/dist/modules/agent-guide/index.js +1 -1
  59. package/dist/modules/agent-guide/index.js.map +1 -1
  60. package/dist/modules/agent-instructions/compose.d.ts +5 -0
  61. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  62. package/dist/modules/agent-instructions/compose.js +19 -2
  63. package/dist/modules/agent-instructions/compose.js.map +1 -1
  64. package/dist/modules/agent-instructions/shared-file-rules.js +19 -19
  65. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  66. package/dist/modules/audit/agent-audit.service.d.ts +21 -2
  67. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  68. package/dist/modules/audit/agent-audit.service.js +43 -2
  69. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  70. package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
  71. package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
  72. package/dist/modules/audit/agent-connection.routes.js +38 -0
  73. package/dist/modules/audit/agent-connection.routes.js.map +1 -0
  74. package/dist/modules/audit/audit.contract.d.ts +24 -0
  75. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  76. package/dist/modules/audit/audit.contract.js.map +1 -1
  77. package/dist/modules/audit/index.d.ts +1 -0
  78. package/dist/modules/audit/index.d.ts.map +1 -1
  79. package/dist/modules/audit/index.js +1 -0
  80. package/dist/modules/audit/index.js.map +1 -1
  81. package/dist/modules/auth/account-hooks.d.ts +46 -0
  82. package/dist/modules/auth/account-hooks.d.ts.map +1 -0
  83. package/dist/modules/auth/account-hooks.js +25 -0
  84. package/dist/modules/auth/account-hooks.js.map +1 -0
  85. package/dist/modules/auth/account-switch-guard.d.ts +15 -0
  86. package/dist/modules/auth/account-switch-guard.d.ts.map +1 -0
  87. package/dist/modules/auth/account-switch-guard.js +2 -0
  88. package/dist/modules/auth/account-switch-guard.js.map +1 -0
  89. package/dist/modules/auth/account-switch-lock.d.ts +34 -0
  90. package/dist/modules/auth/account-switch-lock.d.ts.map +1 -0
  91. package/dist/modules/auth/account-switch-lock.js +48 -0
  92. package/dist/modules/auth/account-switch-lock.js.map +1 -0
  93. package/dist/modules/auth/account.routes.d.ts +4 -1
  94. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  95. package/dist/modules/auth/account.routes.js +110 -13
  96. package/dist/modules/auth/account.routes.js.map +1 -1
  97. package/dist/modules/auth/auth.middleware.d.ts +7 -0
  98. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  99. package/dist/modules/auth/auth.middleware.js +1 -1
  100. package/dist/modules/auth/auth.middleware.js.map +1 -1
  101. package/dist/modules/auth/auth.service.d.ts +101 -6
  102. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  103. package/dist/modules/auth/auth.service.js +246 -51
  104. package/dist/modules/auth/auth.service.js.map +1 -1
  105. package/dist/modules/auth/password-hash.d.ts +5 -2
  106. package/dist/modules/auth/password-hash.d.ts.map +1 -1
  107. package/dist/modules/auth/password-hash.js +5 -2
  108. package/dist/modules/auth/password-hash.js.map +1 -1
  109. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  110. package/dist/modules/code-mode/code-mode.tool.js +11 -3
  111. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  112. package/dist/modules/database/core-schema.d.ts +96 -0
  113. package/dist/modules/database/core-schema.d.ts.map +1 -1
  114. package/dist/modules/database/core-schema.js +32 -3
  115. package/dist/modules/database/core-schema.js.map +1 -1
  116. package/dist/modules/database/pg-error.d.ts +9 -0
  117. package/dist/modules/database/pg-error.d.ts.map +1 -0
  118. package/dist/modules/database/pg-error.js +17 -0
  119. package/dist/modules/database/pg-error.js.map +1 -0
  120. package/dist/modules/embed/account-link.service.d.ts +42 -0
  121. package/dist/modules/embed/account-link.service.d.ts.map +1 -0
  122. package/dist/modules/embed/account-link.service.js +50 -0
  123. package/dist/modules/embed/account-link.service.js.map +1 -0
  124. package/dist/modules/embed/embed-link.d.ts +58 -0
  125. package/dist/modules/embed/embed-link.d.ts.map +1 -0
  126. package/dist/modules/embed/embed-link.js +160 -0
  127. package/dist/modules/embed/embed-link.js.map +1 -0
  128. package/dist/modules/embed/embed.errors.d.ts +30 -0
  129. package/dist/modules/embed/embed.errors.d.ts.map +1 -0
  130. package/dist/modules/embed/embed.errors.js +42 -0
  131. package/dist/modules/embed/embed.errors.js.map +1 -0
  132. package/dist/modules/embed/embed.interface.d.ts +204 -0
  133. package/dist/modules/embed/embed.interface.d.ts.map +1 -0
  134. package/dist/modules/embed/embed.interface.js +2 -0
  135. package/dist/modules/embed/embed.interface.js.map +1 -0
  136. package/dist/modules/embed/embed.routes.d.ts +59 -0
  137. package/dist/modules/embed/embed.routes.d.ts.map +1 -0
  138. package/dist/modules/embed/embed.routes.js +343 -0
  139. package/dist/modules/embed/embed.routes.js.map +1 -0
  140. package/dist/modules/embed/embed.service.d.ts +169 -0
  141. package/dist/modules/embed/embed.service.d.ts.map +1 -0
  142. package/dist/modules/embed/embed.service.js +627 -0
  143. package/dist/modules/embed/embed.service.js.map +1 -0
  144. package/dist/modules/embed/embed.tools.d.ts +51 -0
  145. package/dist/modules/embed/embed.tools.d.ts.map +1 -0
  146. package/dist/modules/embed/embed.tools.js +170 -0
  147. package/dist/modules/embed/embed.tools.js.map +1 -0
  148. package/dist/modules/embed/mcp-app.d.ts +64 -0
  149. package/dist/modules/embed/mcp-app.d.ts.map +1 -0
  150. package/dist/modules/embed/mcp-app.js +147 -0
  151. package/dist/modules/embed/mcp-app.js.map +1 -0
  152. package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
  153. package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
  154. package/dist/modules/embed/mcp-app.routes.js +36 -0
  155. package/dist/modules/embed/mcp-app.routes.js.map +1 -0
  156. package/dist/modules/github-app/github-app.connection.d.ts +15 -0
  157. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
  158. package/dist/modules/github-app/github-app.connection.js +34 -3
  159. package/dist/modules/github-app/github-app.connection.js.map +1 -1
  160. package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
  161. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  162. package/dist/modules/kb-fs/locking-filesystem.js +300 -0
  163. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  164. package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
  165. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
  166. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  167. package/dist/modules/kb-sync/kb-sync.service.js +6 -0
  168. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  169. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  170. package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
  171. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  172. package/dist/modules/mcp/mcp.service.d.ts +28 -1
  173. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  174. package/dist/modules/mcp/mcp.service.js +64 -8
  175. package/dist/modules/mcp/mcp.service.js.map +1 -1
  176. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
  177. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  178. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
  179. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  180. package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
  181. package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
  182. package/dist/modules/onboarding/onboarding.contract.js +7 -0
  183. package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
  184. package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
  185. package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
  186. package/dist/modules/onboarding/onboarding.routes.js +81 -0
  187. package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
  188. package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
  189. package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
  190. package/dist/modules/onboarding/starter-pack.service.js +492 -0
  191. package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
  192. package/dist/modules/onboarding/starter-packs.d.ts +78 -0
  193. package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
  194. package/dist/modules/onboarding/starter-packs.js +156 -0
  195. package/dist/modules/onboarding/starter-packs.js.map +1 -0
  196. package/dist/modules/plugins/index.d.ts +1 -1
  197. package/dist/modules/plugins/index.d.ts.map +1 -1
  198. package/dist/modules/plugins/index.js +1 -1
  199. package/dist/modules/plugins/index.js.map +1 -1
  200. package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
  201. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  202. package/dist/modules/plugins/plugin-provision.service.js +18 -1
  203. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  204. package/dist/modules/plugins/plugins.routes.d.ts +0 -36
  205. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  206. package/dist/modules/plugins/plugins.routes.js +31 -0
  207. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  208. package/dist/modules/settings/deployment-settings.service.d.ts +34 -0
  209. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  210. package/dist/modules/settings/deployment-settings.service.js +134 -1
  211. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  212. package/dist/modules/settings/repository-source.d.ts +1 -0
  213. package/dist/modules/settings/repository-source.d.ts.map +1 -1
  214. package/dist/modules/settings/repository-source.js +3 -3
  215. package/dist/modules/settings/repository-source.js.map +1 -1
  216. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  217. package/dist/modules/skills/skills.tools.js +50 -32
  218. package/dist/modules/skills/skills.tools.js.map +1 -1
  219. package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
  220. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
  221. package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
  222. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
  223. package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
  224. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  225. package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
  226. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  227. package/dist/modules/tool-auth/external-api-key.service.js +59 -2
  228. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  229. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  230. package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
  231. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  232. package/dist/modules/tool-helpers/index.d.ts +28 -1
  233. package/dist/modules/tool-helpers/index.d.ts.map +1 -1
  234. package/dist/modules/tool-helpers/index.js +28 -1
  235. package/dist/modules/tool-helpers/index.js.map +1 -1
  236. package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
  237. package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
  238. package/dist/modules/tool-helpers/route-argument-check.js +132 -0
  239. package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
  240. package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
  241. package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
  242. package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
  243. package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
  244. package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
  245. package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
  246. package/dist/modules/tool-helpers/tool-def.js +70 -6
  247. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  248. package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
  249. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  250. package/dist/modules/tool-helpers/tool-handler.js +157 -8
  251. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  252. package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
  253. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  254. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  255. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  256. package/dist/modules/tool-manuals/tool-manuals.tools.js +14 -5
  257. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  258. package/dist/modules/tool-registry/description-length.d.ts +6 -2
  259. package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
  260. package/dist/modules/tool-registry/description-length.js +27 -5
  261. package/dist/modules/tool-registry/description-length.js.map +1 -1
  262. package/dist/modules/tool-registry/guide-first.d.ts +4 -0
  263. package/dist/modules/tool-registry/guide-first.d.ts.map +1 -1
  264. package/dist/modules/tool-registry/guide-first.js +9 -1
  265. package/dist/modules/tool-registry/guide-first.js.map +1 -1
  266. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +53 -0
  267. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
  268. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +97 -68
  269. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
  270. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
  271. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  272. package/dist/modules/workflow/agent-tools/workflow.tools.js +124 -37
  273. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  274. package/dist/modules/workflow/file-lock.service.d.ts +29 -5
  275. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  276. package/dist/modules/workflow/file-lock.service.js +62 -5
  277. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  278. package/dist/modules/workflow/git/git.service.d.ts +72 -1
  279. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  280. package/dist/modules/workflow/git/git.service.js +172 -36
  281. package/dist/modules/workflow/git/git.service.js.map +1 -1
  282. package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
  283. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  284. package/dist/modules/workflow/git/node-git-runner.js +78 -6
  285. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  286. package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
  287. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  288. package/dist/modules/workflow/pending-commits.service.js +25 -1
  289. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  290. package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
  291. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  292. package/dist/modules/workflow/pending-commits.worker.js +23 -6
  293. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  294. package/dist/modules/workflow/recovery-bot.js +1 -1
  295. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  296. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  297. package/dist/modules/workflow/review-workflow/review-workflow.service.js +2 -1
  298. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  299. package/dist/modules/workflow/sanitize-error.d.ts +16 -0
  300. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  301. package/dist/modules/workflow/sanitize-error.js +45 -0
  302. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  303. package/dist/modules/workflow/workflow.service.d.ts +183 -15
  304. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  305. package/dist/modules/workflow/workflow.service.js +807 -107
  306. package/dist/modules/workflow/workflow.service.js.map +1 -1
  307. package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
  308. package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
  309. package/dist/modules/workspace/agent-download.builder.js +238 -0
  310. package/dist/modules/workspace/agent-download.builder.js.map +1 -0
  311. package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
  312. package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
  313. package/dist/modules/workspace/agent-download.routes.js +156 -0
  314. package/dist/modules/workspace/agent-download.routes.js.map +1 -0
  315. package/dist/modules/workspace/agent-download.store.d.ts +225 -0
  316. package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
  317. package/dist/modules/workspace/agent-download.store.js +423 -0
  318. package/dist/modules/workspace/agent-download.store.js.map +1 -0
  319. package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
  320. package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
  321. package/dist/modules/workspace/agent-upload.store.js +16 -6
  322. package/dist/modules/workspace/agent-upload.store.js.map +1 -1
  323. package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
  324. package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
  325. package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
  326. package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
  327. package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
  328. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  329. package/dist/modules/workspace/file-readers/text-reader.js +5 -8
  330. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  331. package/dist/modules/workspace/first-run.d.ts +111 -0
  332. package/dist/modules/workspace/first-run.d.ts.map +1 -0
  333. package/dist/modules/workspace/first-run.js +181 -0
  334. package/dist/modules/workspace/first-run.js.map +1 -0
  335. package/dist/modules/workspace/move-links.d.ts +92 -0
  336. package/dist/modules/workspace/move-links.d.ts.map +1 -0
  337. package/dist/modules/workspace/move-links.js +173 -0
  338. package/dist/modules/workspace/move-links.js.map +1 -0
  339. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  340. package/dist/modules/workspace/startup/steps/seed-tree.js +3 -13
  341. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  342. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  343. package/dist/modules/workspace/workspace.routes.js +63 -22
  344. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  345. package/dist/modules/workspace/workspace.service.d.ts +75 -2
  346. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  347. package/dist/modules/workspace/workspace.service.js +197 -24
  348. package/dist/modules/workspace/workspace.service.js.map +1 -1
  349. package/dist/modules/workspace/workspace.tools.d.ts +59 -1
  350. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  351. package/dist/modules/workspace/workspace.tools.js +443 -65
  352. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  353. package/dist/modules/write-access/write-access.d.ts.map +1 -1
  354. package/dist/modules/write-access/write-access.js +9 -0
  355. package/dist/modules/write-access/write-access.js.map +1 -1
  356. package/dist/shared/domain-errors.d.ts +73 -1
  357. package/dist/shared/domain-errors.d.ts.map +1 -1
  358. package/dist/shared/domain-errors.js +86 -12
  359. package/dist/shared/domain-errors.js.map +1 -1
  360. package/dist/shared/git.contract.d.ts +10 -1
  361. package/dist/shared/git.contract.d.ts.map +1 -1
  362. package/dist/shared/git.contract.js.map +1 -1
  363. package/dist/shared/utf8-text.d.ts +15 -0
  364. package/dist/shared/utf8-text.d.ts.map +1 -0
  365. package/dist/shared/utf8-text.js +20 -0
  366. package/dist/shared/utf8-text.js.map +1 -0
  367. package/dist/tenancy/static-tenant-source.d.ts +2 -1
  368. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  369. package/dist/tenancy/static-tenant-source.js +9 -2
  370. package/dist/tenancy/static-tenant-source.js.map +1 -1
  371. package/mcp-app/page.html +349 -0
  372. package/migrations/0017_atlassian_account_links.sql +37 -0
  373. package/migrations/meta/0017_snapshot.json +2396 -0
  374. package/migrations/meta/_journal.json +8 -1
  375. package/package.json +5 -3
  376. package/src/__tests__/packaged-assets-ship.test.ts +16 -4
  377. package/src/__tests__/pg-scratch.ts +48 -0
  378. package/src/assets.ts +28 -2
  379. package/src/core/__tests__/catalog-revision.test.ts +40 -0
  380. package/src/core/__tests__/gated-boot.test.ts +1 -0
  381. package/src/core/__tests__/lifecycle.test.ts +14 -0
  382. package/src/core/catalog-revision.ts +15 -2
  383. package/src/core/create-core-server.ts +69 -3
  384. package/src/core/create-core-services.ts +151 -6
  385. package/src/core/lifecycle.ts +28 -2
  386. package/src/core-config.ts +24 -1
  387. package/src/index.ts +2 -2
  388. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +1 -0
  389. package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
  390. package/src/modules/access/__tests__/access.routes.admins.test.ts +203 -0
  391. package/src/modules/access/__tests__/access.routes.batch.test.ts +25 -5
  392. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +1 -0
  393. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +1 -1
  394. package/src/modules/access/__tests__/access.routes.overrides.test.ts +1 -1
  395. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
  396. package/src/modules/access/__tests__/access.routes.prospective.test.ts +1 -0
  397. package/src/modules/access/__tests__/access.routes.revoke.test.ts +1 -1
  398. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +1 -0
  399. package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
  400. package/src/modules/access/__tests__/roles-admin.service.test.ts +15 -2
  401. package/src/modules/access/__tests__/roles.routes.test.ts +132 -7
  402. package/src/modules/access/__tests__/user-access-removal.service.test.ts +2 -2
  403. package/src/modules/access/__tests__/users-db-double.ts +12 -3
  404. package/src/modules/access/access-control.interface.ts +23 -0
  405. package/src/modules/access/access-control.service.ts +35 -0
  406. package/src/modules/access/access.routes.ts +67 -6
  407. package/src/modules/access/directory-sync-bot.ts +1 -1
  408. package/src/modules/access/roles-admin.service.ts +51 -11
  409. package/src/modules/access/user-access-removal.service.ts +1 -1
  410. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +58 -0
  411. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +19 -1
  412. package/src/modules/agent-guide/agent-guide.ts +9 -0
  413. package/src/modules/agent-guide/index.ts +1 -0
  414. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +14 -0
  415. package/src/modules/agent-instructions/compose.ts +17 -2
  416. package/src/modules/agent-instructions/shared-file-rules.ts +19 -19
  417. package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
  418. package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
  419. package/src/modules/audit/agent-audit.service.ts +58 -3
  420. package/src/modules/audit/agent-connection.routes.ts +56 -0
  421. package/src/modules/audit/audit.contract.ts +26 -0
  422. package/src/modules/audit/index.ts +1 -0
  423. package/src/modules/auth/__tests__/account-deactivation.test.ts +145 -16
  424. package/src/modules/auth/__tests__/account-hooks.test.ts +57 -0
  425. package/src/modules/auth/__tests__/account-switch-lock.pg.test.ts +78 -0
  426. package/src/modules/auth/__tests__/account-switch-lock.test.ts +43 -0
  427. package/src/modules/auth/__tests__/account.routes.test.ts +387 -9
  428. package/src/modules/auth/__tests__/auth.service.created.pg.test.ts +69 -0
  429. package/src/modules/auth/__tests__/auth.service.test.ts +159 -1
  430. package/src/modules/auth/account-hooks.ts +69 -0
  431. package/src/modules/auth/account-switch-guard.ts +14 -0
  432. package/src/modules/auth/account-switch-lock.ts +58 -0
  433. package/src/modules/auth/account.routes.ts +122 -13
  434. package/src/modules/auth/auth.middleware.ts +1 -1
  435. package/src/modules/auth/auth.service.ts +268 -52
  436. package/src/modules/auth/password-hash.ts +5 -2
  437. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
  438. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  439. package/src/modules/database/__tests__/pg-error.test.ts +21 -0
  440. package/src/modules/database/__tests__/pii-backfill.pg.test.ts +6 -27
  441. package/src/modules/database/core-schema.ts +33 -3
  442. package/src/modules/database/pg-error.ts +15 -0
  443. package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
  444. package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
  445. package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
  446. package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
  447. package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
  448. package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
  449. package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
  450. package/src/modules/embed/account-link.service.ts +76 -0
  451. package/src/modules/embed/embed-link.ts +180 -0
  452. package/src/modules/embed/embed.errors.ts +43 -0
  453. package/src/modules/embed/embed.interface.ts +203 -0
  454. package/src/modules/embed/embed.routes.ts +363 -0
  455. package/src/modules/embed/embed.service.ts +705 -0
  456. package/src/modules/embed/embed.tools.ts +206 -0
  457. package/src/modules/embed/mcp-app.routes.ts +41 -0
  458. package/src/modules/embed/mcp-app.ts +173 -0
  459. package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
  460. package/src/modules/github-app/github-app.connection.ts +38 -4
  461. package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
  462. package/src/modules/kb-fs/locking-filesystem.ts +287 -0
  463. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
  464. package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
  465. package/src/modules/kb-sync/kb-sync.service.ts +6 -0
  466. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
  467. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
  468. package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
  469. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
  470. package/src/modules/mcp/__tests__/mcp.service.test.ts +23 -13
  471. package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
  472. package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
  473. package/src/modules/mcp/mcp.service.ts +81 -12
  474. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
  475. package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
  476. package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
  477. package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
  478. package/src/modules/onboarding/onboarding.contract.ts +85 -0
  479. package/src/modules/onboarding/onboarding.routes.ts +85 -0
  480. package/src/modules/onboarding/starter-pack.service.ts +580 -0
  481. package/src/modules/onboarding/starter-packs.ts +182 -0
  482. package/src/modules/plugins/__tests__/plugins.tools.test.ts +25 -2
  483. package/src/modules/plugins/index.ts +1 -0
  484. package/src/modules/plugins/plugin-provision.service.ts +22 -1
  485. package/src/modules/plugins/plugins.routes.ts +29 -0
  486. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +150 -1
  487. package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
  488. package/src/modules/settings/deployment-settings.service.ts +131 -1
  489. package/src/modules/settings/repository-source.ts +4 -4
  490. package/src/modules/skills/__tests__/skills.tools.description.test.ts +5 -1
  491. package/src/modules/skills/skills.tools.ts +55 -35
  492. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
  493. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
  494. package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
  495. package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
  496. package/src/modules/tool-auth/external-api-key.service.ts +61 -4
  497. package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
  498. package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
  499. package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
  500. package/src/modules/tool-helpers/index.ts +34 -1
  501. package/src/modules/tool-helpers/route-argument-check.ts +149 -0
  502. package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
  503. package/src/modules/tool-helpers/tool-def.ts +132 -7
  504. package/src/modules/tool-helpers/tool-handler.ts +196 -8
  505. package/src/modules/tool-helpers/tool.contract.ts +9 -0
  506. package/src/modules/tool-manuals/tool-manuals.tools.ts +15 -5
  507. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +14 -1
  508. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +23 -10
  509. package/src/modules/tool-registry/description-length.ts +34 -7
  510. package/src/modules/tool-registry/guide-first.ts +8 -1
  511. package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
  512. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
  513. package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
  514. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
  515. package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
  516. package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
  517. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
  518. package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
  519. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
  520. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
  521. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
  522. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
  523. package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +41 -5
  524. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +127 -2
  525. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +157 -122
  526. package/src/modules/workflow/agent-tools/workflow.tools.ts +129 -35
  527. package/src/modules/workflow/file-lock.service.ts +67 -5
  528. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
  529. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
  530. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
  531. package/src/modules/workflow/git/git.service.ts +176 -38
  532. package/src/modules/workflow/git/node-git-runner.ts +82 -7
  533. package/src/modules/workflow/pending-commits.service.ts +29 -1
  534. package/src/modules/workflow/pending-commits.worker.ts +24 -6
  535. package/src/modules/workflow/recovery-bot.ts +1 -1
  536. package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -1
  537. package/src/modules/workflow/sanitize-error.ts +50 -0
  538. package/src/modules/workflow/workflow.service.ts +881 -113
  539. package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
  540. package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
  541. package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
  542. package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
  543. package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
  544. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
  545. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
  546. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
  547. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
  548. package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
  549. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
  550. package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
  551. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
  552. package/src/modules/workspace/__tests__/workspace.tools.test.ts +409 -12
  553. package/src/modules/workspace/agent-download.builder.ts +308 -0
  554. package/src/modules/workspace/agent-download.routes.ts +183 -0
  555. package/src/modules/workspace/agent-download.store.ts +533 -0
  556. package/src/modules/workspace/agent-upload.store.ts +23 -7
  557. package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
  558. package/src/modules/workspace/file-readers/text-reader.ts +5 -8
  559. package/src/modules/workspace/first-run.ts +222 -0
  560. package/src/modules/workspace/move-links.ts +237 -0
  561. package/src/modules/workspace/startup/steps/seed-tree.ts +3 -12
  562. package/src/modules/workspace/workspace.routes.ts +64 -22
  563. package/src/modules/workspace/workspace.service.ts +202 -25
  564. package/src/modules/workspace/workspace.tools.ts +492 -63
  565. package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
  566. package/src/modules/write-access/write-access.ts +9 -0
  567. package/src/shared/domain-errors.ts +108 -11
  568. package/src/shared/git.contract.ts +8 -1
  569. package/src/shared/utf8-text.ts +21 -0
  570. package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
  571. package/src/tenancy/static-tenant-source.ts +10 -2
  572. package/starter-packs/README.md +103 -0
  573. package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
  574. package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
  575. package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
  576. package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
  577. package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
  578. package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
  579. package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
  580. package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
  581. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
  582. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
  583. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
  584. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
  585. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
  586. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
  587. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
  588. package/starter-packs/engineering/pack.yaml +11 -0
  589. package/starter-packs/general/KnowledgeBase/About us.md +9 -0
  590. package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
  591. package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
  592. package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
  593. package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
  594. package/starter-packs/general/Plugins/general-starter/access.md +19 -0
  595. package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
  596. package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
  597. package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
  598. package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
  599. package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
  600. package/starter-packs/general/pack.yaml +10 -0
  601. package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
  602. package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
  603. package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
  604. package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
  605. package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
  606. package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
  607. package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
  608. package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
  609. package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
  610. package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
  611. package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
  612. package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
  613. package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
  614. package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
  615. package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
  616. package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
  617. package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
  618. package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
  619. package/starter-packs/operations/pack.yaml +12 -0
  620. package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
  621. package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
  622. package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
  623. package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
  624. package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
  625. package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
  626. package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
  627. package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
  628. package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
  629. package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
  630. package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
  631. package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
  632. package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
  633. package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
  634. package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
  635. package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
  636. package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
  637. package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
  638. package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
  639. package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
  640. package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
  641. package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
  642. package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
  643. package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
  644. package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
  645. package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
  646. package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
  647. package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
  648. package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
  649. package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
  650. package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
  651. package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
  652. package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
  653. package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
  654. package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
  655. package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
  656. package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
  657. package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
  658. package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
  659. package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
  660. package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
  661. package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
  662. package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
  663. package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
  664. package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
  665. package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
  666. package/starter-packs/sales/pack.yaml +13 -0
  667. package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
  668. package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
  669. package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
  670. package/starter-packs/support/KnowledgeBase/Products.md +9 -0
  671. package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
  672. package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
  673. package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
  674. package/starter-packs/support/Plugins/support-starter/access.md +19 -0
  675. package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
  676. package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
  677. package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
  678. package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
  679. package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
  680. package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
  681. package/starter-packs/support/pack.yaml +12 -0
@@ -14,13 +14,17 @@ import type { IExternalApiKeyService } from '../../tool-auth/external-api-key.in
14
14
  * update / delete` pops the next queued result. `transaction` runs its
15
15
  * callback against the same fake, so queued results are consumed in order.
16
16
  * Every `where` predicate and `update` target is captured, so a test can pin
17
- * WHICH rows a statement addressed (see {@link render}), not just that it ran.
17
+ * WHICH rows a statement addressed (see {@link render}), not just that it ran;
18
+ * `orderBy` and `limit` too, since the fake applies neither and a test can
19
+ * only pin WHICH row of several a read keeps by the clauses it sent.
18
20
  */
19
21
  function makeFakeDb(queue: unknown[]) {
20
- const captured: { values: any[]; set: any[]; where: SQL[]; updateTargets: unknown[] } = {
22
+ const captured: { values: any[]; set: any[]; where: SQL[]; orderBy: SQL[][]; limit: number[]; updateTargets: unknown[] } = {
21
23
  values: [],
22
24
  set: [],
23
25
  where: [],
26
+ orderBy: [],
27
+ limit: [],
24
28
  updateTargets: [],
25
29
  };
26
30
  const counts = { insert: 0, select: 0, update: 0, delete: 0 };
@@ -36,10 +40,10 @@ function makeFakeDb(queue: unknown[]) {
36
40
  chain.values = passthrough((a) => captured.values.push(a[0]));
37
41
  chain.set = passthrough((a) => captured.set.push(a[0]));
38
42
  chain.where = passthrough((a) => captured.where.push(a[0]));
39
- chain.limit = passthrough();
43
+ chain.limit = passthrough((a) => captured.limit.push(a[0]));
40
44
  chain.innerJoin = passthrough();
41
45
  chain.from = passthrough();
42
- chain.orderBy = passthrough();
46
+ chain.orderBy = passthrough((a) => captured.orderBy.push(a));
43
47
  chain.groupBy = passthrough();
44
48
  chain.returning = passthrough();
45
49
  chain.then = (onF: any, onR: any) => Promise.resolve(result).then(onF, onR);
@@ -355,6 +359,62 @@ describe('AgentAuditService.revokeConnection', () => {
355
359
  });
356
360
  });
357
361
 
362
+ /**
363
+ * The onboarding's "is your agent connected yet?". The page polls it, so the
364
+ * contract worth pinning is what it reads (one person's LIVE, USED rows of
365
+ * each kind — never the event log) and how the two kinds are combined.
366
+ */
367
+ describe('AgentAuditService.lastAgentUse', () => {
368
+ it('answers null when neither an agent connection nor a key has been used', async () => {
369
+ const { service, counts } = makeService([[] /* connections */, [] /* keys */]);
370
+ await expect(service.lastAgentUse(ALICE.id)).resolves.toBeNull();
371
+ expect(counts.select).toBe(2);
372
+ });
373
+
374
+ it("reads only the caller's live, used rows of each kind", async () => {
375
+ const { service, captured } = makeService([[], []]);
376
+ await service.lastAgentUse(ALICE.id);
377
+ const [connections, keys] = captured.where.map(render);
378
+ expect(connections!.sql).toContain('"agent_connections"."user_id" = $1');
379
+ expect(connections!.sql).toContain('"agent_connections"."revoked_at" is null');
380
+ expect(connections!.sql).toContain('"agent_connections"."last_used_at" is not null');
381
+ expect(connections!.params).toEqual([ALICE.id]);
382
+ expect(keys!.sql).toContain('"api_tokens"."user_id" = $1');
383
+ expect(keys!.sql).toContain('"api_tokens"."revoked_at" is null');
384
+ expect(keys!.sql).toContain('"api_tokens"."last_used_at" is not null');
385
+ expect(keys!.params).toEqual([ALICE.id]);
386
+ });
387
+
388
+ it('keeps the most recently used row of each kind, of however many a person has', async () => {
389
+ // The fake neither sorts nor limits, so what is pinned is what each read
390
+ // asks the database for: newest use first, one row.
391
+ const { service, captured } = makeService([[], []]);
392
+ await service.lastAgentUse(ALICE.id);
393
+ expect(captured.orderBy.map((clauses) => clauses.map((c) => render(c).sql))).toEqual([
394
+ ['"agent_connections"."last_used_at" desc'],
395
+ ['"api_tokens"."last_used_at" desc'],
396
+ ]);
397
+ expect(captured.limit).toEqual([1, 1]);
398
+ });
399
+
400
+ it("names an agent connection by its client, and an unnamed one as such", async () => {
401
+ const at = new Date(NOW - 60_000);
402
+ const named = makeService([[{ client: 'Claude', at }], []]);
403
+ await expect(named.service.lastAgentUse(ALICE.id)).resolves.toEqual({ at, client: 'Claude', kind: 'agent' });
404
+ const unnamed = makeService([[{ client: null, at }], []]);
405
+ await expect(unnamed.service.lastAgentUse(ALICE.id)).resolves.toEqual({ at, client: 'Unnamed agent', kind: 'agent' });
406
+ });
407
+
408
+ it('answers with the newer of the two when both kinds have been used', async () => {
409
+ const older = new Date(NOW - DAY);
410
+ const newer = new Date(NOW - 60_000);
411
+ const keyNewer = makeService([[{ client: 'Claude', at: older }], [{ client: 'Laptop', at: newer }]]);
412
+ await expect(keyNewer.service.lastAgentUse(ALICE.id)).resolves.toEqual({ at: newer, client: 'Laptop', kind: 'key' });
413
+ const agentNewer = makeService([[{ client: 'Claude', at: newer }], [{ client: 'Laptop', at: older }]]);
414
+ await expect(agentNewer.service.lastAgentUse(ALICE.id)).resolves.toEqual({ at: newer, client: 'Claude', kind: 'agent' });
415
+ });
416
+ });
417
+
358
418
  describe('retentionDaysFrom', () => {
359
419
  it('reads a positive whole number of days, and blank, zero or negative as forever', () => {
360
420
  expect(retentionDaysFrom('30')).toBe(30);
@@ -0,0 +1,102 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import express from 'express';
3
+ import type { Server } from 'node:http';
4
+ import { createAgentConnectionRoutes } from '../agent-connection.routes.js';
5
+ import type { AgentUse, IAgentConnectionStatus } from '../audit.contract.js';
6
+
7
+ /**
8
+ * GET /onboarding/agent-connection — what the connect-your-agent page polls.
9
+ * The contract worth pinning:
10
+ *
11
+ * - it answers for the AUTHENTICATED user (req.userId) and nobody else;
12
+ * nothing in the request — query or otherwise — can name another account;
13
+ * - "not yet" and "connected" are the two shapes, the second with when, as
14
+ * what, and whether that name is an agent's or a connection key's label;
15
+ * - the answer is never cached, since the next poll expects it to change;
16
+ * - a failure is a generic 500, never the driver's message.
17
+ */
18
+
19
+ const ALICE = 'u-alice';
20
+ const BOB = 'u-bob';
21
+ const CARA = 'u-cara';
22
+
23
+ const USES: Record<string, AgentUse> = {
24
+ [BOB]: { at: new Date(Date.UTC(2026, 9, 7, 12, 0, 0)), client: 'Claude', kind: 'agent' },
25
+ [CARA]: { at: new Date(Date.UTC(2026, 9, 7, 13, 0, 0)), client: 'Laptop', kind: 'key' },
26
+ };
27
+
28
+ const status = {
29
+ lastAgentUse: vi.fn(async (userId: string) => USES[userId] ?? null),
30
+ } satisfies IAgentConnectionStatus;
31
+
32
+ let server: Server | undefined;
33
+ afterEach(() => {
34
+ server?.close();
35
+ server = undefined;
36
+ vi.clearAllMocks();
37
+ vi.restoreAllMocks();
38
+ });
39
+
40
+ /** Mount the route with a stand-in auth middleware stamping `as` as the caller. */
41
+ async function listen(as: string): Promise<string> {
42
+ const app = express();
43
+ app.use((req, _res, next) => {
44
+ req.userId = as;
45
+ next();
46
+ });
47
+ app.use('/api', createAgentConnectionRoutes(status));
48
+ await new Promise<void>((resolve) => {
49
+ server = app.listen(0, resolve);
50
+ });
51
+ const address = server!.address();
52
+ if (typeof address === 'string' || !address) throw new Error('no port');
53
+ return `http://127.0.0.1:${address.port}`;
54
+ }
55
+
56
+ describe('GET /onboarding/agent-connection', () => {
57
+ it('says "not yet" for a caller whose agents have not been used', async () => {
58
+ const base = await listen(ALICE);
59
+ const res = await fetch(`${base}/api/onboarding/agent-connection`);
60
+ expect(res.status).toBe(200);
61
+ expect(await res.json()).toEqual({ connected: false });
62
+ expect(status.lastAgentUse).toHaveBeenCalledWith(ALICE);
63
+ });
64
+
65
+ it('says connected, with when and as what, once one has', async () => {
66
+ const base = await listen(BOB);
67
+ const res = await fetch(`${base}/api/onboarding/agent-connection`);
68
+ expect(await res.json()).toEqual({ connected: true, at: '2026-10-07T12:00:00.000Z', client: 'Claude', kind: 'agent' });
69
+ });
70
+
71
+ it("keeps a connection key's kind: its label says nothing about which app holds it", async () => {
72
+ const base = await listen(CARA);
73
+ const res = await fetch(`${base}/api/onboarding/agent-connection`);
74
+ expect(await res.json()).toEqual({ connected: true, at: '2026-10-07T13:00:00.000Z', client: 'Laptop', kind: 'key' });
75
+ });
76
+
77
+ it("answers for the caller alone — a named account in the query is not asked about", async () => {
78
+ const base = await listen(ALICE);
79
+ const res = await fetch(`${base}/api/onboarding/agent-connection?userId=${BOB}&user=${BOB}`);
80
+ expect(await res.json()).toEqual({ connected: false });
81
+ expect(status.lastAgentUse).toHaveBeenCalledTimes(1);
82
+ expect(status.lastAgentUse).toHaveBeenCalledWith(ALICE);
83
+ });
84
+
85
+ it('is never cached: the next poll expects a different answer', async () => {
86
+ const base = await listen(ALICE);
87
+ const res = await fetch(`${base}/api/onboarding/agent-connection`);
88
+ expect(res.headers.get('cache-control')).toBe('no-store');
89
+ });
90
+
91
+ it('reports a failed read as a generic 500, keeping the detail server-side', async () => {
92
+ const detail = 'connection to 10.0.0.4:5432 refused';
93
+ status.lastAgentUse.mockRejectedValueOnce(new Error(detail));
94
+ vi.spyOn(console, 'error').mockImplementation(() => {});
95
+ const base = await listen(ALICE);
96
+ const res = await fetch(`${base}/api/onboarding/agent-connection`);
97
+ expect(res.status).toBe(500);
98
+ const body = (await res.json()) as { error: string };
99
+ expect(body.error).toBe('Could not check your agent');
100
+ expect(body.error).not.toContain(detail);
101
+ });
102
+ });
@@ -1,7 +1,7 @@
1
- import { and, count, desc, eq, isNull, lt, or, type SQL } from 'drizzle-orm';
1
+ import { and, count, desc, eq, isNotNull, isNull, lt, or, type SQL } from 'drizzle-orm';
2
2
  import { logger } from '../../shared/logging.js';
3
3
  import type { Database } from '../database/connection.js';
4
- import { agentConnections, agentEvents, oauthTokens, users } from '../database/schema.js';
4
+ import { agentConnections, agentEvents, externalApiKeys, oauthTokens, users } from '../database/schema.js';
5
5
  import type { IExternalApiKeyService } from '../tool-auth/external-api-key.interface.js';
6
6
  import {
7
7
  AuditPrincipalNotFoundError,
@@ -11,10 +11,12 @@ import {
11
11
  type AgentEventInput,
12
12
  type AgentEventPage,
13
13
  type AgentEventView,
14
+ type AgentUse,
14
15
  type AuditPrincipal,
15
16
  type AuditPrincipalRef,
16
17
  type AuditScope,
17
18
  type IAgentAuditService,
19
+ type IAgentConnectionStatus,
18
20
  type IAgentEventRecorder,
19
21
  } from './audit.contract.js';
20
22
 
@@ -39,7 +41,7 @@ const PRUNE_INTERVAL_MS = 60 * 60_000;
39
41
  * Connection keys are read through the key service rather than joined here,
40
42
  * so their summary shape (kind, revoked-by, …) has exactly one definition.
41
43
  */
42
- export class AgentAuditService implements IAgentAuditService, IAgentEventRecorder {
44
+ export class AgentAuditService implements IAgentAuditService, IAgentEventRecorder, IAgentConnectionStatus {
43
45
  private lastPruneAt = 0;
44
46
 
45
47
  constructor(
@@ -242,6 +244,59 @@ export class AgentAuditService implements IAgentAuditService, IAgentEventRecorde
242
244
  };
243
245
  }
244
246
 
247
+ /**
248
+ * Whether one person's agent has reached the platform yet — what the
249
+ * onboarding asks once on arrival (the `agent-connected` event the stamp
250
+ * paths emit tells it the moment it changes). Two single-row reads on the
251
+ * per-user indexes, never a scan of the event log.
252
+ *
253
+ * Read from the `last_used_at` stamps, not from `agent_events`: an agent
254
+ * stamps its connection (or key) on its FIRST authenticated request — the
255
+ * `initialize` / `tools/list` every client sends straight after connecting
256
+ * — while an event is written only once it calls a tool, which may be
257
+ * never. The stamps are also outside the retention sweep, so "connected"
258
+ * cannot quietly turn back into "waiting" when old events are pruned.
259
+ *
260
+ * Live rows only: an agent its owner disconnected, or an admin revoked, is
261
+ * not a connected agent, whatever it did before. The key's columns are read
262
+ * here rather than through the key service because the service has no
263
+ * single-row read and this question needs only two columns of one row.
264
+ */
265
+ async lastAgentUse(userId: string): Promise<AgentUse | null> {
266
+ const [[agent], [key]] = await Promise.all([
267
+ this.db
268
+ .select({ client: agentConnections.clientName, at: agentConnections.lastUsedAt })
269
+ .from(agentConnections)
270
+ .where(
271
+ and(
272
+ eq(agentConnections.userId, userId),
273
+ isNull(agentConnections.revokedAt),
274
+ isNotNull(agentConnections.lastUsedAt),
275
+ ),
276
+ )
277
+ .orderBy(desc(agentConnections.lastUsedAt))
278
+ .limit(1),
279
+ this.db
280
+ .select({ client: externalApiKeys.label, at: externalApiKeys.lastUsedAt })
281
+ .from(externalApiKeys)
282
+ .where(
283
+ and(
284
+ eq(externalApiKeys.userId, userId),
285
+ isNull(externalApiKeys.revokedAt),
286
+ isNotNull(externalApiKeys.lastUsedAt),
287
+ ),
288
+ )
289
+ .orderBy(desc(externalApiKeys.lastUsedAt))
290
+ .limit(1),
291
+ ]);
292
+ const uses = [
293
+ agent?.at ? { at: agent.at, client: agent.client ?? 'Unnamed agent', kind: 'agent' as const } : null,
294
+ key?.at ? { at: key.at, client: key.client, kind: 'key' as const } : null,
295
+ ].filter((u): u is AgentUse => u !== null);
296
+ if (uses.length === 0) return null;
297
+ return uses.reduce((newest, u) => (u.at.getTime() > newest.at.getTime() ? u : newest));
298
+ }
299
+
245
300
  async revokeConnection(id: string, by: 'owner' | 'admin', ownerUserId?: string): Promise<void> {
246
301
  const scope = ownerUserId
247
302
  ? and(eq(agentConnections.id, id), eq(agentConnections.userId, ownerUserId))!
@@ -0,0 +1,56 @@
1
+ import express from 'express';
2
+ import { logger } from '../../shared/logging.js';
3
+ import type { IAgentConnectionStatus } from './audit.contract.js';
4
+ import '../auth/auth.middleware.js'; // Express Request augmentation (req.userId)
5
+
6
+ const log = logger('audit');
7
+
8
+ /** What the onboarding reads: connected or not, and — once connected — when, as what, and through which kind of credential. */
9
+ export interface AgentConnectionResponse {
10
+ connected: boolean;
11
+ /** The agent's most recent call, ISO 8601. */
12
+ at?: string;
13
+ /** The agent's registered client name, or the connection key's label. */
14
+ client?: string;
15
+ /**
16
+ * Which of the two `client` is: `agent` for an OAuth connection's
17
+ * registered name, `key` for a connection key's free-text label. Only an
18
+ * `agent` name says which app connected.
19
+ */
20
+ kind?: 'agent' | 'key';
21
+ }
22
+
23
+ /**
24
+ * GET /api/onboarding/agent-connection — "has an agent of mine reached the
25
+ * platform yet?", JWT-only (mounted behind the session middleware).
26
+ *
27
+ * The connect-your-agent page and the Get set up list ask it once on
28
+ * arrival, and again when the person comes back to the tab; the moment it
29
+ * changes is told by the `agent-connected` event the use-stamp paths emit
30
+ * over the event stream, so nothing asks on a timer. It answers for the
31
+ * CALLER alone: the user is the token's, and nothing in the request can name
32
+ * anyone else — there is no parameter to try.
33
+ *
34
+ * `no-store` because the answer is expected to change between two asks; a
35
+ * cached "not yet" would keep a page waiting on an agent that has already
36
+ * arrived.
37
+ */
38
+ export function createAgentConnectionRoutes(status: IAgentConnectionStatus): express.Router {
39
+ const router = express.Router();
40
+
41
+ router.get('/onboarding/agent-connection', async (req, res) => {
42
+ res.set('Cache-Control', 'no-store');
43
+ try {
44
+ const use = await status.lastAgentUse(req.userId!);
45
+ const body: AgentConnectionResponse = use
46
+ ? { connected: true, at: use.at.toISOString(), client: use.client, kind: use.kind }
47
+ : { connected: false };
48
+ res.json(body);
49
+ } catch (err) {
50
+ log.error('agent connection status failed:', { err });
51
+ res.status(500).json({ error: 'Could not check your agent' });
52
+ }
53
+ });
54
+
55
+ return router;
56
+ }
@@ -121,6 +121,32 @@ export interface IAgentAuditService {
121
121
  revokeConnection(id: string, by: 'owner' | 'admin', ownerUserId?: string): Promise<void>;
122
122
  }
123
123
 
124
+ /**
125
+ * The most recent use of any of one person's live agents: when, what it
126
+ * calls itself (the agent's registered client name, or the key's label), and
127
+ * which of the two that name is.
128
+ */
129
+ export interface AgentUse {
130
+ at: Date;
131
+ client: string;
132
+ /**
133
+ * `agent`: an OAuth connection, and `client` is the name the agent
134
+ * registered. `key`: a connection key, and `client` is whatever label its
135
+ * owner typed — it says nothing about which app holds the key.
136
+ */
137
+ kind: 'agent' | 'key';
138
+ }
139
+
140
+ /**
141
+ * The onboarding's "is your agent connected yet?" — answered for ONE person,
142
+ * from the use stamps the auth paths already write, so it costs two indexed
143
+ * single-row reads and records nothing new.
144
+ */
145
+ export interface IAgentConnectionStatus {
146
+ /** The newest use among the person's live agent connections and connection keys; null when none has been used. */
147
+ lastAgentUse(userId: string): Promise<AgentUse | null>;
148
+ }
149
+
124
150
  export class AuditPrincipalNotFoundError extends Error {
125
151
  constructor() {
126
152
  super('No such agent or key');
@@ -2,6 +2,7 @@ export * from './audit.contract.js';
2
2
  export { AgentAuditService, MAX_EVENT_PAGE, retentionDaysFrom } from './agent-audit.service.js';
3
3
  export { createAgentRestAuditMiddleware } from './agent-rest-audit.middleware.js';
4
4
  export { createAuditRoutes } from './audit.routes.js';
5
+ export { createAgentConnectionRoutes, type AgentConnectionResponse } from './agent-connection.routes.js';
5
6
  export { RequestAudit } from './request-audit.js';
6
7
  export {
7
8
  classifyToolCall,
@@ -1,4 +1,6 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
+ import { PgDialect } from 'drizzle-orm/pg-core';
3
+ import type { SQL } from 'drizzle-orm';
2
4
  import type { Database } from '../../database/connection.js';
3
5
  import type { CoreConfig } from '../../../core-config.js';
4
6
  import { AuthService } from '../auth.service.js';
@@ -16,10 +18,19 @@ import { RECOVERY_BOT_EMAIL } from '../../workflow/recovery-bot.js';
16
18
  /**
17
19
  * Drizzle chain stub, as in `auth.service.test.ts`: each db.select() /
18
20
  * insert() / update() consumes the next queued result; awaiting any point of
19
- * the chain resolves it. Records what was inserted and set.
21
+ * the chain resolves it. Records what was inserted and set, and, in order,
22
+ * every write and lock (`execute`) with the handle it went through: `db`
23
+ * itself, or the `tx` a transaction hands its callback (a different object,
24
+ * between `begin` and `commit`), so a lock taken outside the transaction
25
+ * shows up as `db:`.
20
26
  */
21
27
  function makeFakeDb(queue: unknown[]) {
22
- const captured: { values: Record<string, unknown>[]; set: Record<string, unknown>[] } = { values: [], set: [] };
28
+ const captured: { values: Record<string, unknown>[]; set: Record<string, unknown>[]; order: string[] } = {
29
+ values: [],
30
+ set: [],
31
+ order: [],
32
+ };
33
+ const dialect = new PgDialect();
23
34
  function nextChain(): unknown {
24
35
  const result = queue.shift();
25
36
  const chain: Record<string, unknown> = {};
@@ -42,12 +53,27 @@ function makeFakeDb(queue: unknown[]) {
42
53
  });
43
54
  return chain;
44
55
  }
45
- const db = {
46
- insert: vi.fn(() => nextChain()),
56
+ const handle = (tag: 'db' | 'tx') => ({
57
+ insert: vi.fn(() => (captured.order.push(`${tag}:insert`), nextChain())),
47
58
  select: vi.fn(() => nextChain()),
48
- update: vi.fn(() => nextChain()),
49
- } as unknown as Database;
50
- return { db, captured };
59
+ update: vi.fn(() => (captured.order.push(`${tag}:update`), nextChain())),
60
+ execute: vi.fn(async (query: SQL) => {
61
+ const { sql, params } = dialect.sqlToQuery(query);
62
+ captured.order.push(`${tag}:${sql} ${JSON.stringify(params)}`);
63
+ return [];
64
+ }),
65
+ });
66
+ const tx = handle('tx');
67
+ const db = {
68
+ ...handle('db'),
69
+ transaction: async <T,>(cb: (t: typeof tx) => Promise<T>) => {
70
+ captured.order.push('begin');
71
+ const out = await cb(tx);
72
+ captured.order.push('commit');
73
+ return out;
74
+ },
75
+ };
76
+ return { db: db as unknown as Database, captured };
51
77
  }
52
78
 
53
79
  function makeConfig(over: Partial<CoreConfig> = {}): CoreConfig {
@@ -156,6 +182,64 @@ describe('AuthService.isActive', () => {
156
182
  });
157
183
  });
158
184
 
185
+ // An earlier version could switch off an owner without a server-held
186
+ // password; nothing in the product can switch them back on for a workspace
187
+ // whose only admin they are. Their way in clears it.
188
+ describe('an owner switched off by an earlier version', () => {
189
+ const OWNER_OFF = { ...OFF, id: 'owner-1', email: 'root@example.com' };
190
+
191
+ for (const [label, adminPassword] of [
192
+ ['with ADMIN_PASSWORD', 'sup3r-secret'],
193
+ ['without ADMIN_PASSWORD (the cloud)', ''],
194
+ ] as const) {
195
+ const config = makeConfig({ adminEmail: 'root@example.com', adminPassword });
196
+
197
+ it(`signs in with single sign-on, and the switch-off is cleared on the row, ${label}`, async () => {
198
+ const { db, captured } = makeFakeDb([[OWNER_OFF], []]);
199
+ const { user } = await new AuthService(db, config).loginWithSso('root@example.com', 'Root');
200
+ expect(user.id).toBe('owner-1');
201
+ expect(captured.set).toEqual([expect.objectContaining({ deactivatedAt: null })]);
202
+ });
203
+
204
+ it(`is let in as an embed identity, ${label}`, async () => {
205
+ const { db, captured } = makeFakeDb([[OWNER_OFF], []]);
206
+ await expect(new AuthService(db, config).getOrCreateByEmail('root@example.com')).resolves.toMatchObject({ id: 'owner-1' });
207
+ expect(captured.set).toEqual([expect.objectContaining({ deactivatedAt: null })]);
208
+ });
209
+
210
+ it(`keeps a session they already hold, ${label}`, async () => {
211
+ const { db, captured } = makeFakeDb([[{ id: 'owner-1', email: 'root@example.com', deactivatedAt: new Date() }], []]);
212
+ expect(await new AuthService(db, config).isActive('owner-1')).toBe(true);
213
+ expect(captured.set).toEqual([expect.objectContaining({ deactivatedAt: null })]);
214
+ });
215
+ }
216
+
217
+ it('signs in with a password they hold on the row, without ADMIN_PASSWORD', async () => {
218
+ const passwordHash = await hashPassword('a-long-enough-password');
219
+ const { db, captured } = makeFakeDb([[{ ...OWNER_OFF, passwordHash }], []]);
220
+ const svc = new AuthService(db, makeConfig({ adminEmail: 'root@example.com' }));
221
+ const { user } = await svc.loginWithPassword('root@example.com', 'a-long-enough-password');
222
+ expect(user.id).toBe('owner-1');
223
+ expect(captured.set).toEqual([expect.objectContaining({ deactivatedAt: null })]);
224
+ });
225
+
226
+ it('signs in with ADMIN_PASSWORD, and the switch-off is cleared on the row', async () => {
227
+ // The lookup, the upsert, then the clearing update.
228
+ const { db, captured } = makeFakeDb([[OWNER_OFF], [OWNER_OFF], []]);
229
+ const svc = new AuthService(db, makeConfig({ adminEmail: 'root@example.com', adminPassword: 'sup3r-secret' }));
230
+ const { user } = await svc.loginWithPassword('root@example.com', 'sup3r-secret');
231
+ expect(user.id).toBe('owner-1');
232
+ expect(captured.set).toEqual([expect.objectContaining({ deactivatedAt: null })]);
233
+ });
234
+
235
+ it('leaves a switched-off account that is not an owner switched off', async () => {
236
+ const { db, captured } = makeFakeDb([[OFF]]);
237
+ const svc = new AuthService(db, makeConfig({ adminEmail: 'root@example.com' }));
238
+ await expect(svc.loginWithSso('alice@example.com', 'Alice')).rejects.toBeInstanceOf(AccountDeactivatedError);
239
+ expect(captured.set).toEqual([]);
240
+ });
241
+ });
242
+
159
243
  describe('AuthService.deactivate', () => {
160
244
  it('records when the account was switched off', async () => {
161
245
  const { db, captured } = makeFakeDb([[ROW], []]);
@@ -163,22 +247,59 @@ describe('AuthService.deactivate', () => {
163
247
  expect(captured.set[0].deactivatedAt).toBeInstanceOf(Date);
164
248
  });
165
249
 
250
+ it('switches off under the address switch lock, taken before the update', async () => {
251
+ const { db, captured } = makeFakeDb([[{ ...ROW, email: 'Alice@Example.com' }], []]);
252
+ await new AuthService(db, makeConfig()).deactivate(ROW.id);
253
+ expect(captured.order).toEqual([
254
+ 'begin',
255
+ // The wait for the lock is bounded, inside the transaction.
256
+ `tx:select set_config('lock_timeout', $1, true) ["15000"]`,
257
+ 'tx:select pg_advisory_xact_lock(hashtext($1)) ["account-switch:alice@example.com"]',
258
+ 'tx:update',
259
+ 'commit',
260
+ ]);
261
+ });
262
+
166
263
  it('answers false for an account that does not exist', async () => {
167
264
  const { db } = makeFakeDb([[]]);
168
265
  expect(await new AuthService(db, makeConfig()).deactivate('nobody')).toBe(false);
169
266
  });
170
267
 
171
- it('refuses the deployment admin, whose environment password is the way back in', async () => {
172
- const config = makeConfig({ adminEmail: 'root@example.com', adminPassword: 'sup3r-secret' });
173
- const { db } = makeFakeDb([[{ ...ROW, email: 'root@example.com' }]]);
174
- await expect(new AuthService(db, config).deactivate(ROW.id)).rejects.toBeInstanceOf(AccountChangeRefusedError);
175
- expect(vi.mocked(db.update)).not.toHaveBeenCalled();
268
+ // With a server-held owner password (self-hosted) and without one (the
269
+ // cloud's configuration): the owner is never switched off, and the refusal
270
+ // comes before any lock or write (`captured.order` stays empty).
271
+ for (const [label, adminPassword] of [
272
+ ['with ADMIN_PASSWORD', 'sup3r-secret'],
273
+ ['without ADMIN_PASSWORD (the cloud)', ''],
274
+ ] as const) {
275
+ it(`refuses the owner, ${label}, and says why`, async () => {
276
+ const config = makeConfig({ adminEmail: 'root@example.com', adminPassword });
277
+ const { db, captured } = makeFakeDb([[{ ...ROW, email: 'root@example.com' }]]);
278
+ const refusal = new AuthService(db, config).deactivate(ROW.id);
279
+ await expect(refusal).rejects.toBeInstanceOf(AccountChangeRefusedError);
280
+ await expect(refusal).rejects.toThrow("The owner can't be switched off.");
281
+ expect(captured.order).toEqual([]);
282
+ });
283
+
284
+ it(`still switches off an account that is not the owner, ${label}`, async () => {
285
+ const config = makeConfig({ adminEmail: 'root@example.com', adminPassword });
286
+ const { db, captured } = makeFakeDb([[ROW], []]);
287
+ expect(await new AuthService(db, config).deactivate(ROW.id)).toBe(true);
288
+ expect(captured.set[0].deactivatedAt).toBeInstanceOf(Date);
289
+ });
290
+ }
291
+
292
+ it('refuses the owner even with password sign-in switched off', async () => {
293
+ const config = makeConfig({ adminEmail: 'root@example.com', adminPassword: 'sup3r-secret', loginPasswordEnabled: false });
294
+ const { db, captured } = makeFakeDb([[{ ...ROW, email: 'root@example.com' }]]);
295
+ await expect(new AuthService(db, config).deactivate(ROW.id)).rejects.toThrow("The owner can't be switched off.");
296
+ expect(captured.order).toEqual([]);
176
297
  });
177
298
 
178
299
  it('refuses the accounts the platform runs its own work as', async () => {
179
- const { db } = makeFakeDb([[{ ...ROW, email: RECOVERY_BOT_EMAIL }]]);
300
+ const { db, captured } = makeFakeDb([[{ ...ROW, email: RECOVERY_BOT_EMAIL }]]);
180
301
  await expect(new AuthService(db, makeConfig()).deactivate(ROW.id)).rejects.toThrow('platform itself');
181
- expect(vi.mocked(db.update)).not.toHaveBeenCalled();
302
+ expect(captured.order).toEqual([]);
182
303
  });
183
304
  });
184
305
 
@@ -221,17 +342,25 @@ describe('a first sign-in the port would rather keep waiting', () => {
221
342
  expect(captured.values).toHaveLength(1);
222
343
  expect(captured.values[0]).toMatchObject({ email: 'new@example.com' });
223
344
  expect(captured.values[0].deactivatedAt).toBeInstanceOf(Date);
345
+ // Created switched off, so under the address's switch lock.
346
+ expect(captured.order).toEqual([
347
+ 'begin',
348
+ `tx:select set_config('lock_timeout', $1, true) ["15000"]`,
349
+ 'tx:select pg_advisory_xact_lock(hashtext($1)) ["account-switch:new@example.com"]',
350
+ 'tx:insert',
351
+ 'commit',
352
+ ]);
224
353
  });
225
354
 
226
355
  it('is a plain refusal when an admin creates the account', async () => {
227
356
  const { port } = recordingPort(waiting);
228
- const { db } = makeFakeDb([[]]);
357
+ const { db, captured } = makeFakeDb([[]]);
229
358
  const err = await new AuthService(db, makeConfig(), port)
230
359
  .createAccount('new@example.com', 'New')
231
360
  .catch((e: unknown) => e);
232
361
  expect(err).toBeInstanceOf(AccountAdmissionRefusedError);
233
362
  expect((err as AccountAdmissionRefusedError).waitingForAdmin).toBe(false);
234
- expect(vi.mocked(db.insert)).not.toHaveBeenCalled();
363
+ expect(captured.order).toEqual([]);
235
364
  });
236
365
  });
237
366
 
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { AccountHooks, type AccountInvitedEvent } from '../account-hooks.js';
3
+
4
+ const EVENT: AccountInvitedEvent = {
5
+ account: { id: 'u-lena', email: 'lena@acme.test', name: 'lena' },
6
+ invitedBy: { id: 'u-dana', email: 'dana@acme.test', name: 'Dana Admin' },
7
+ passwordSet: true,
8
+ };
9
+
10
+ describe('AccountHooks: an account was invited', () => {
11
+ it('notifies nobody when nothing is registered, and says so', async () => {
12
+ expect(await new AccountHooks().runAccountInvited(EVENT)).toEqual([]);
13
+ });
14
+
15
+ it('runs every listener in registration order, with the event, and collects the channels that reached the person', async () => {
16
+ const hooks = new AccountHooks();
17
+ const seen: string[] = [];
18
+ hooks.onAccountInvited(async (event) => {
19
+ seen.push(`mail:${event.account.email}:${event.passwordSet}`);
20
+ return 'email';
21
+ });
22
+ hooks.onAccountInvited(async () => {
23
+ seen.push('chat');
24
+ return null;
25
+ });
26
+ hooks.onAccountInvited(async () => {
27
+ seen.push('sms');
28
+ return 'sms';
29
+ });
30
+ expect(await hooks.runAccountInvited(EVENT)).toEqual(['email', 'sms']);
31
+ expect(seen).toEqual(['mail:lena@acme.test:true', 'chat', 'sms']);
32
+ });
33
+
34
+ it('a listener that throws counts as not having notified, and stops nothing', async () => {
35
+ const hooks = new AccountHooks();
36
+ const error = vi.spyOn(console, 'error').mockImplementation(() => {});
37
+ try {
38
+ hooks.onAccountInvited(async () => {
39
+ throw new Error('smtp down');
40
+ });
41
+ hooks.onAccountInvited(async () => 'email');
42
+ expect(await hooks.runAccountInvited(EVENT)).toEqual(['email']);
43
+ // Logged, so a failing mailer is diagnosable from the server's log.
44
+ expect(error).toHaveBeenCalledTimes(1);
45
+ expect(String(error.mock.calls[0]![0])).toContain('an account-invited listener failed');
46
+ } finally {
47
+ error.mockRestore();
48
+ }
49
+ });
50
+
51
+ it('an empty channel name is no channel: it counts as not having notified', async () => {
52
+ const hooks = new AccountHooks();
53
+ hooks.onAccountInvited(async () => '');
54
+ hooks.onAccountInvited(async () => 'email');
55
+ expect(await hooks.runAccountInvited(EVENT)).toEqual(['email']);
56
+ });
57
+ });