@bevel-software/platform-core-backend 0.25.2 → 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 (733) hide show
  1. package/agent-guide/access-control.md +251 -0
  2. package/agent-guide/conventions.md +35 -0
  3. package/agent-guide/directory-structure.md +145 -0
  4. package/agent-guide/finding-things.md +7 -0
  5. package/agent-guide/html-views.md +83 -0
  6. package/agent-guide/introduction.md +27 -0
  7. package/agent-guide/new-knowledge-base.md +24 -0
  8. package/agent-guide/skills.md +47 -0
  9. package/agent-guide/tool-manuals.md +227 -0
  10. package/agent-guide/where-a-new-file-goes.md +36 -0
  11. package/dist/assets.d.ts +26 -0
  12. package/dist/assets.d.ts.map +1 -1
  13. package/dist/assets.js +35 -2
  14. package/dist/assets.js.map +1 -1
  15. package/dist/core/catalog-revision.d.ts +19 -1
  16. package/dist/core/catalog-revision.d.ts.map +1 -1
  17. package/dist/core/catalog-revision.js +14 -3
  18. package/dist/core/catalog-revision.js.map +1 -1
  19. package/dist/core/core-ports.d.ts +11 -0
  20. package/dist/core/core-ports.d.ts.map +1 -1
  21. package/dist/core/core-ports.js.map +1 -1
  22. package/dist/core/create-core-server.d.ts +5 -0
  23. package/dist/core/create-core-server.d.ts.map +1 -1
  24. package/dist/core/create-core-server.js +65 -3
  25. package/dist/core/create-core-server.js.map +1 -1
  26. package/dist/core/create-core-services.d.ts +35 -0
  27. package/dist/core/create-core-services.d.ts.map +1 -1
  28. package/dist/core/create-core-services.js +114 -12
  29. package/dist/core/create-core-services.js.map +1 -1
  30. package/dist/core/lifecycle.d.ts +16 -1
  31. package/dist/core/lifecycle.d.ts.map +1 -1
  32. package/dist/core/lifecycle.js +18 -1
  33. package/dist/core/lifecycle.js.map +1 -1
  34. package/dist/core-config.d.ts +21 -0
  35. package/dist/core-config.d.ts.map +1 -1
  36. package/dist/core-config.js +22 -1
  37. package/dist/core-config.js.map +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/modules/access/access-control.interface.d.ts +17 -0
  43. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  44. package/dist/modules/access/access-control.service.d.ts +2 -0
  45. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  46. package/dist/modules/access/access-control.service.js +27 -0
  47. package/dist/modules/access/access-control.service.js.map +1 -1
  48. package/dist/modules/access/directory-sync-bot.js +1 -1
  49. package/dist/modules/access/directory-sync-bot.js.map +1 -1
  50. package/dist/modules/agent-guide/agent-guide.d.ts +145 -0
  51. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -0
  52. package/dist/modules/agent-guide/agent-guide.js +199 -0
  53. package/dist/modules/agent-guide/agent-guide.js.map +1 -0
  54. package/dist/modules/agent-guide/agent-guide.tools.d.ts +24 -0
  55. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -0
  56. package/dist/modules/agent-guide/agent-guide.tools.js +100 -0
  57. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -0
  58. package/dist/modules/agent-guide/index.d.ts +4 -0
  59. package/dist/modules/agent-guide/index.d.ts.map +1 -0
  60. package/dist/modules/agent-guide/index.js +4 -0
  61. package/dist/modules/agent-guide/index.js.map +1 -0
  62. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +3 -2
  63. package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
  64. package/dist/modules/agent-instructions/agent-instructions.routes.js +3 -2
  65. package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
  66. package/dist/modules/agent-instructions/compose.d.ts +14 -6
  67. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  68. package/dist/modules/agent-instructions/compose.js +28 -8
  69. package/dist/modules/agent-instructions/compose.js.map +1 -1
  70. package/dist/modules/agent-instructions/index.d.ts +1 -1
  71. package/dist/modules/agent-instructions/index.d.ts.map +1 -1
  72. package/dist/modules/agent-instructions/index.js +1 -1
  73. package/dist/modules/agent-instructions/index.js.map +1 -1
  74. package/dist/modules/agent-instructions/shared-file-rules.d.ts +10 -50
  75. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  76. package/dist/modules/agent-instructions/shared-file-rules.js +51 -104
  77. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  78. package/dist/modules/audit/agent-audit.service.d.ts +21 -2
  79. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  80. package/dist/modules/audit/agent-audit.service.js +43 -2
  81. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  82. package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
  83. package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
  84. package/dist/modules/audit/agent-connection.routes.js +38 -0
  85. package/dist/modules/audit/agent-connection.routes.js.map +1 -0
  86. package/dist/modules/audit/audit.contract.d.ts +24 -0
  87. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  88. package/dist/modules/audit/audit.contract.js.map +1 -1
  89. package/dist/modules/audit/index.d.ts +1 -0
  90. package/dist/modules/audit/index.d.ts.map +1 -1
  91. package/dist/modules/audit/index.js +1 -0
  92. package/dist/modules/audit/index.js.map +1 -1
  93. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  94. package/dist/modules/auth/account.routes.js +10 -3
  95. package/dist/modules/auth/account.routes.js.map +1 -1
  96. package/dist/modules/auth/auth.middleware.d.ts +7 -0
  97. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  98. package/dist/modules/auth/auth.middleware.js +1 -1
  99. package/dist/modules/auth/auth.middleware.js.map +1 -1
  100. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
  101. package/dist/modules/code-mode/code-mode.tool.js +11 -3
  102. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  103. package/dist/modules/database/core-schema.d.ts +96 -0
  104. package/dist/modules/database/core-schema.d.ts.map +1 -1
  105. package/dist/modules/database/core-schema.js +32 -3
  106. package/dist/modules/database/core-schema.js.map +1 -1
  107. package/dist/modules/embed/account-link.service.d.ts +42 -0
  108. package/dist/modules/embed/account-link.service.d.ts.map +1 -0
  109. package/dist/modules/embed/account-link.service.js +50 -0
  110. package/dist/modules/embed/account-link.service.js.map +1 -0
  111. package/dist/modules/embed/embed-link.d.ts +58 -0
  112. package/dist/modules/embed/embed-link.d.ts.map +1 -0
  113. package/dist/modules/embed/embed-link.js +160 -0
  114. package/dist/modules/embed/embed-link.js.map +1 -0
  115. package/dist/modules/embed/embed.errors.d.ts +30 -0
  116. package/dist/modules/embed/embed.errors.d.ts.map +1 -0
  117. package/dist/modules/embed/embed.errors.js +42 -0
  118. package/dist/modules/embed/embed.errors.js.map +1 -0
  119. package/dist/modules/embed/embed.interface.d.ts +204 -0
  120. package/dist/modules/embed/embed.interface.d.ts.map +1 -0
  121. package/dist/modules/embed/embed.interface.js +2 -0
  122. package/dist/modules/embed/embed.interface.js.map +1 -0
  123. package/dist/modules/embed/embed.routes.d.ts +59 -0
  124. package/dist/modules/embed/embed.routes.d.ts.map +1 -0
  125. package/dist/modules/embed/embed.routes.js +343 -0
  126. package/dist/modules/embed/embed.routes.js.map +1 -0
  127. package/dist/modules/embed/embed.service.d.ts +169 -0
  128. package/dist/modules/embed/embed.service.d.ts.map +1 -0
  129. package/dist/modules/embed/embed.service.js +627 -0
  130. package/dist/modules/embed/embed.service.js.map +1 -0
  131. package/dist/modules/embed/embed.tools.d.ts +51 -0
  132. package/dist/modules/embed/embed.tools.d.ts.map +1 -0
  133. package/dist/modules/embed/embed.tools.js +170 -0
  134. package/dist/modules/embed/embed.tools.js.map +1 -0
  135. package/dist/modules/embed/mcp-app.d.ts +64 -0
  136. package/dist/modules/embed/mcp-app.d.ts.map +1 -0
  137. package/dist/modules/embed/mcp-app.js +147 -0
  138. package/dist/modules/embed/mcp-app.js.map +1 -0
  139. package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
  140. package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
  141. package/dist/modules/embed/mcp-app.routes.js +36 -0
  142. package/dist/modules/embed/mcp-app.routes.js.map +1 -0
  143. package/dist/modules/github-app/github-app.connection.d.ts +15 -0
  144. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
  145. package/dist/modules/github-app/github-app.connection.js +34 -3
  146. package/dist/modules/github-app/github-app.connection.js.map +1 -1
  147. package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
  148. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  149. package/dist/modules/kb-fs/locking-filesystem.js +300 -0
  150. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  151. package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
  152. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
  153. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
  154. package/dist/modules/kb-sync/kb-sync.service.js +6 -0
  155. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
  156. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  157. package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
  158. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  159. package/dist/modules/mcp/mcp.service.d.ts +57 -3
  160. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  161. package/dist/modules/mcp/mcp.service.js +177 -24
  162. package/dist/modules/mcp/mcp.service.js.map +1 -1
  163. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
  164. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  165. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
  166. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  167. package/dist/modules/mcp/tool-schema-guard.d.ts +105 -0
  168. package/dist/modules/mcp/tool-schema-guard.d.ts.map +1 -0
  169. package/dist/modules/mcp/tool-schema-guard.js +171 -0
  170. package/dist/modules/mcp/tool-schema-guard.js.map +1 -0
  171. package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
  172. package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
  173. package/dist/modules/onboarding/onboarding.contract.js +7 -0
  174. package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
  175. package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
  176. package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
  177. package/dist/modules/onboarding/onboarding.routes.js +81 -0
  178. package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
  179. package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
  180. package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
  181. package/dist/modules/onboarding/starter-pack.service.js +492 -0
  182. package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
  183. package/dist/modules/onboarding/starter-packs.d.ts +78 -0
  184. package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
  185. package/dist/modules/onboarding/starter-packs.js +156 -0
  186. package/dist/modules/onboarding/starter-packs.js.map +1 -0
  187. package/dist/modules/plugins/index.d.ts +1 -1
  188. package/dist/modules/plugins/index.d.ts.map +1 -1
  189. package/dist/modules/plugins/index.js +1 -1
  190. package/dist/modules/plugins/index.js.map +1 -1
  191. package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
  192. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  193. package/dist/modules/plugins/plugin-provision.service.js +18 -1
  194. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  195. package/dist/modules/plugins/plugins.routes.d.ts +0 -36
  196. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  197. package/dist/modules/plugins/plugins.routes.js +31 -0
  198. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  199. package/dist/modules/plugins/plugins.tools.d.ts +36 -2
  200. package/dist/modules/plugins/plugins.tools.d.ts.map +1 -1
  201. package/dist/modules/plugins/plugins.tools.js +71 -14
  202. package/dist/modules/plugins/plugins.tools.js.map +1 -1
  203. package/dist/modules/settings/deployment-settings.service.d.ts +34 -7
  204. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  205. package/dist/modules/settings/deployment-settings.service.js +148 -54
  206. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  207. package/dist/modules/settings/repository-source.d.ts +1 -0
  208. package/dist/modules/settings/repository-source.d.ts.map +1 -1
  209. package/dist/modules/settings/repository-source.js +3 -3
  210. package/dist/modules/settings/repository-source.js.map +1 -1
  211. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  212. package/dist/modules/settings/setup.routes.js +3 -6
  213. package/dist/modules/settings/setup.routes.js.map +1 -1
  214. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  215. package/dist/modules/skills/skills.tools.js +103 -43
  216. package/dist/modules/skills/skills.tools.js.map +1 -1
  217. package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
  218. package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
  219. package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
  220. package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
  221. package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
  222. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  223. package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
  224. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  225. package/dist/modules/tool-auth/external-api-key.service.js +59 -2
  226. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  227. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  228. package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
  229. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  230. package/dist/modules/tool-helpers/index.d.ts +28 -1
  231. package/dist/modules/tool-helpers/index.d.ts.map +1 -1
  232. package/dist/modules/tool-helpers/index.js +28 -1
  233. package/dist/modules/tool-helpers/index.js.map +1 -1
  234. package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
  235. package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
  236. package/dist/modules/tool-helpers/route-argument-check.js +132 -0
  237. package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
  238. package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
  239. package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
  240. package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
  241. package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
  242. package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
  243. package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
  244. package/dist/modules/tool-helpers/tool-def.js +70 -6
  245. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  246. package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
  247. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  248. package/dist/modules/tool-helpers/tool-handler.js +157 -8
  249. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  250. package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
  251. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  252. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  253. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +23 -4
  254. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  255. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
  256. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -0
  257. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  258. package/dist/modules/tool-manuals/tool-manuals.service.js +14 -0
  259. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  260. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +7 -0
  261. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  262. package/dist/modules/tool-manuals/tool-manuals.tools.js +77 -38
  263. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  264. package/dist/modules/tool-registry/description-length.d.ts +20 -16
  265. package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
  266. package/dist/modules/tool-registry/description-length.js +50 -30
  267. package/dist/modules/tool-registry/description-length.js.map +1 -1
  268. package/dist/modules/tool-registry/guide-first.d.ts +27 -0
  269. package/dist/modules/tool-registry/guide-first.d.ts.map +1 -0
  270. package/dist/modules/tool-registry/guide-first.js +40 -0
  271. package/dist/modules/tool-registry/guide-first.js.map +1 -0
  272. package/dist/modules/tool-registry/tool-registry.d.ts +6 -0
  273. package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -1
  274. package/dist/modules/tool-registry/tool-registry.js +9 -2
  275. package/dist/modules/tool-registry/tool-registry.js.map +1 -1
  276. package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts +449 -0
  277. package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts.map +1 -0
  278. package/dist/modules/workflow/agent-tools/change-request-read-shape.js +481 -0
  279. package/dist/modules/workflow/agent-tools/change-request-read-shape.js.map +1 -0
  280. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +126 -0
  281. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -0
  282. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +611 -0
  283. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -0
  284. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +12 -1
  285. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -1
  286. package/dist/modules/workflow/agent-tools/change-request-summary.js +5 -1
  287. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -1
  288. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
  289. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  290. package/dist/modules/workflow/agent-tools/workflow.tools.js +133 -37
  291. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  292. package/dist/modules/workflow/file-lock.service.d.ts +29 -5
  293. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  294. package/dist/modules/workflow/file-lock.service.js +62 -5
  295. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  296. package/dist/modules/workflow/git/git.service.d.ts +282 -14
  297. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  298. package/dist/modules/workflow/git/git.service.js +627 -126
  299. package/dist/modules/workflow/git/git.service.js.map +1 -1
  300. package/dist/modules/workflow/git/merge-commit.d.ts +73 -0
  301. package/dist/modules/workflow/git/merge-commit.d.ts.map +1 -0
  302. package/dist/modules/workflow/git/merge-commit.js +89 -0
  303. package/dist/modules/workflow/git/merge-commit.js.map +1 -0
  304. package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
  305. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  306. package/dist/modules/workflow/git/node-git-runner.js +78 -6
  307. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  308. package/dist/modules/workflow/git/pull-request.service.d.ts +94 -1
  309. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  310. package/dist/modules/workflow/git/pull-request.service.js +332 -37
  311. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  312. package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
  313. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
  314. package/dist/modules/workflow/pending-commits.service.js +25 -1
  315. package/dist/modules/workflow/pending-commits.service.js.map +1 -1
  316. package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
  317. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
  318. package/dist/modules/workflow/pending-commits.worker.js +23 -6
  319. package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
  320. package/dist/modules/workflow/recovery-bot.js +1 -1
  321. package/dist/modules/workflow/recovery-bot.js.map +1 -1
  322. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +35 -0
  323. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  324. package/dist/modules/workflow/review-workflow/review-workflow.service.js +180 -13
  325. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  326. package/dist/modules/workflow/sanitize-error.d.ts +16 -0
  327. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
  328. package/dist/modules/workflow/sanitize-error.js +45 -0
  329. package/dist/modules/workflow/sanitize-error.js.map +1 -1
  330. package/dist/modules/workflow/workflow.routes.d.ts +6 -2
  331. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  332. package/dist/modules/workflow/workflow.routes.js +7 -2
  333. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  334. package/dist/modules/workflow/workflow.service.d.ts +187 -15
  335. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  336. package/dist/modules/workflow/workflow.service.js +808 -106
  337. package/dist/modules/workflow/workflow.service.js.map +1 -1
  338. package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
  339. package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
  340. package/dist/modules/workspace/agent-download.builder.js +238 -0
  341. package/dist/modules/workspace/agent-download.builder.js.map +1 -0
  342. package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
  343. package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
  344. package/dist/modules/workspace/agent-download.routes.js +156 -0
  345. package/dist/modules/workspace/agent-download.routes.js.map +1 -0
  346. package/dist/modules/workspace/agent-download.store.d.ts +225 -0
  347. package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
  348. package/dist/modules/workspace/agent-download.store.js +423 -0
  349. package/dist/modules/workspace/agent-download.store.js.map +1 -0
  350. package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
  351. package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
  352. package/dist/modules/workspace/agent-upload.store.js +16 -6
  353. package/dist/modules/workspace/agent-upload.store.js.map +1 -1
  354. package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
  355. package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
  356. package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
  357. package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
  358. package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
  359. package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
  360. package/dist/modules/workspace/file-readers/text-reader.js +5 -8
  361. package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
  362. package/dist/modules/workspace/first-run.d.ts +111 -0
  363. package/dist/modules/workspace/first-run.d.ts.map +1 -0
  364. package/dist/modules/workspace/first-run.js +181 -0
  365. package/dist/modules/workspace/first-run.js.map +1 -0
  366. package/dist/modules/workspace/move-links.d.ts +92 -0
  367. package/dist/modules/workspace/move-links.d.ts.map +1 -0
  368. package/dist/modules/workspace/move-links.js +173 -0
  369. package/dist/modules/workspace/move-links.js.map +1 -0
  370. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  371. package/dist/modules/workspace/startup/steps/seed-tree.js +24 -39
  372. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  373. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +58 -52
  374. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  375. package/dist/modules/workspace/startup/steps/template-files.step.js +209 -223
  376. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  377. package/dist/modules/workspace/startup/steps/template-source.d.ts +5 -3
  378. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  379. package/dist/modules/workspace/startup/steps/template-source.js +5 -3
  380. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  381. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  382. package/dist/modules/workspace/workspace.routes.js +63 -22
  383. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  384. package/dist/modules/workspace/workspace.service.d.ts +75 -2
  385. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  386. package/dist/modules/workspace/workspace.service.js +197 -24
  387. package/dist/modules/workspace/workspace.service.js.map +1 -1
  388. package/dist/modules/workspace/workspace.tools.d.ts +68 -1
  389. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  390. package/dist/modules/workspace/workspace.tools.js +642 -71
  391. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  392. package/dist/modules/write-access/write-access.d.ts.map +1 -1
  393. package/dist/modules/write-access/write-access.js +9 -0
  394. package/dist/modules/write-access/write-access.js.map +1 -1
  395. package/dist/shared/domain-errors.d.ts +84 -1
  396. package/dist/shared/domain-errors.d.ts.map +1 -1
  397. package/dist/shared/domain-errors.js +100 -12
  398. package/dist/shared/domain-errors.js.map +1 -1
  399. package/dist/shared/git.contract.d.ts +10 -1
  400. package/dist/shared/git.contract.d.ts.map +1 -1
  401. package/dist/shared/git.contract.js.map +1 -1
  402. package/dist/shared/hidden-tools.d.ts +44 -0
  403. package/dist/shared/hidden-tools.d.ts.map +1 -0
  404. package/dist/shared/hidden-tools.js +13 -0
  405. package/dist/shared/hidden-tools.js.map +1 -0
  406. package/dist/shared/utf8-text.d.ts +15 -0
  407. package/dist/shared/utf8-text.d.ts.map +1 -0
  408. package/dist/shared/utf8-text.js +20 -0
  409. package/dist/shared/utf8-text.js.map +1 -0
  410. package/dist/tenancy/static-tenant-source.d.ts +2 -1
  411. package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
  412. package/dist/tenancy/static-tenant-source.js +9 -2
  413. package/dist/tenancy/static-tenant-source.js.map +1 -1
  414. package/kb-template/.bevelignore +0 -5
  415. package/mcp-app/page.html +349 -0
  416. package/migrations/0017_atlassian_account_links.sql +37 -0
  417. package/migrations/meta/0017_snapshot.json +2396 -0
  418. package/migrations/meta/_journal.json +8 -1
  419. package/package.json +6 -3
  420. package/src/__tests__/kb-layout-config.test.ts +10 -100
  421. package/src/__tests__/packaged-assets-ship.test.ts +66 -0
  422. package/src/assets.ts +38 -2
  423. package/src/core/__tests__/catalog-revision.test.ts +40 -0
  424. package/src/core/__tests__/gated-boot.test.ts +1 -0
  425. package/src/core/__tests__/lifecycle.test.ts +14 -0
  426. package/src/core/catalog-revision.ts +15 -2
  427. package/src/core/core-ports.ts +11 -0
  428. package/src/core/create-core-server.ts +78 -3
  429. package/src/core/create-core-services.ts +168 -10
  430. package/src/core/lifecycle.ts +28 -2
  431. package/src/core-config.ts +24 -1
  432. package/src/index.ts +2 -2
  433. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +58 -0
  434. package/src/modules/access/__tests__/access-control.platform-restore.test.ts +8 -7
  435. package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
  436. package/src/modules/access/__tests__/access-personal-plugin.test.ts +1 -18
  437. package/src/modules/access/access-control.interface.ts +28 -0
  438. package/src/modules/access/access-control.service.ts +37 -0
  439. package/src/modules/access/directory-sync-bot.ts +1 -1
  440. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +386 -0
  441. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +207 -0
  442. package/src/modules/agent-guide/agent-guide.tools.ts +122 -0
  443. package/src/modules/agent-guide/agent-guide.ts +300 -0
  444. package/src/modules/agent-guide/index.ts +22 -0
  445. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +39 -118
  446. package/src/modules/agent-instructions/agent-instructions.routes.ts +3 -2
  447. package/src/modules/agent-instructions/compose.ts +26 -8
  448. package/src/modules/agent-instructions/index.ts +0 -3
  449. package/src/modules/agent-instructions/shared-file-rules.ts +50 -112
  450. package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
  451. package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
  452. package/src/modules/audit/agent-audit.service.ts +58 -3
  453. package/src/modules/audit/agent-connection.routes.ts +56 -0
  454. package/src/modules/audit/audit.contract.ts +26 -0
  455. package/src/modules/audit/index.ts +1 -0
  456. package/src/modules/auth/__tests__/account.routes.test.ts +5 -3
  457. package/src/modules/auth/account.routes.ts +10 -3
  458. package/src/modules/auth/auth.middleware.ts +1 -1
  459. package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
  460. package/src/modules/code-mode/code-mode.tool.ts +13 -2
  461. package/src/modules/database/core-schema.ts +33 -3
  462. package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
  463. package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
  464. package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
  465. package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
  466. package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
  467. package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
  468. package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
  469. package/src/modules/embed/account-link.service.ts +76 -0
  470. package/src/modules/embed/embed-link.ts +180 -0
  471. package/src/modules/embed/embed.errors.ts +43 -0
  472. package/src/modules/embed/embed.interface.ts +203 -0
  473. package/src/modules/embed/embed.routes.ts +363 -0
  474. package/src/modules/embed/embed.service.ts +705 -0
  475. package/src/modules/embed/embed.tools.ts +206 -0
  476. package/src/modules/embed/mcp-app.routes.ts +41 -0
  477. package/src/modules/embed/mcp-app.ts +173 -0
  478. package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
  479. package/src/modules/github-app/github-app.connection.ts +38 -4
  480. package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
  481. package/src/modules/kb-fs/locking-filesystem.ts +287 -0
  482. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
  483. package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
  484. package/src/modules/kb-sync/kb-sync.service.ts +6 -0
  485. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
  486. package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +14 -3
  487. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
  488. package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
  489. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +252 -0
  490. package/src/modules/mcp/__tests__/mcp.service.test.ts +42 -24
  491. package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
  492. package/src/modules/mcp/__tests__/tool-schema-guard.test.ts +266 -0
  493. package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
  494. package/src/modules/mcp/mcp.service.ts +218 -31
  495. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
  496. package/src/modules/mcp/tool-schema-guard.ts +196 -0
  497. package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
  498. package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
  499. package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
  500. package/src/modules/onboarding/onboarding.contract.ts +85 -0
  501. package/src/modules/onboarding/onboarding.routes.ts +85 -0
  502. package/src/modules/onboarding/starter-pack.service.ts +580 -0
  503. package/src/modules/onboarding/starter-packs.ts +182 -0
  504. package/src/modules/plugins/__tests__/plugins.tools.test.ts +177 -4
  505. package/src/modules/plugins/index.ts +1 -0
  506. package/src/modules/plugins/plugin-provision.service.ts +22 -1
  507. package/src/modules/plugins/plugins.routes.ts +29 -0
  508. package/src/modules/plugins/plugins.tools.ts +75 -15
  509. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +176 -56
  510. package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
  511. package/src/modules/settings/deployment-settings.service.ts +144 -55
  512. package/src/modules/settings/repository-source.ts +4 -4
  513. package/src/modules/settings/setup.routes.ts +3 -6
  514. package/src/modules/skills/__tests__/skills.tools.description.test.ts +95 -0
  515. package/src/modules/skills/skills.tools.ts +112 -46
  516. package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
  517. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
  518. package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
  519. package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
  520. package/src/modules/tool-auth/external-api-key.service.ts +61 -4
  521. package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
  522. package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
  523. package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
  524. package/src/modules/tool-helpers/index.ts +34 -1
  525. package/src/modules/tool-helpers/route-argument-check.ts +149 -0
  526. package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
  527. package/src/modules/tool-helpers/tool-def.ts +132 -7
  528. package/src/modules/tool-helpers/tool-handler.ts +196 -8
  529. package/src/modules/tool-helpers/tool.contract.ts +9 -0
  530. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +57 -0
  531. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +73 -4
  532. package/src/modules/tool-manuals/tool-manuals.contract.ts +24 -4
  533. package/src/modules/tool-manuals/tool-manuals.service.ts +17 -0
  534. package/src/modules/tool-manuals/tool-manuals.tools.ts +86 -38
  535. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +173 -0
  536. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +81 -66
  537. package/src/modules/tool-registry/description-length.ts +57 -32
  538. package/src/modules/tool-registry/guide-first.ts +41 -0
  539. package/src/modules/tool-registry/tool-registry.ts +9 -2
  540. package/src/modules/workflow/__tests__/apply-failure.test.ts +6 -1
  541. package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
  542. package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
  543. package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
  544. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
  545. package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
  546. package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
  547. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
  548. package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
  549. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
  550. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
  551. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
  552. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
  553. package/src/modules/workflow/agent-tools/__tests__/change-request-read-shape.test.ts +705 -0
  554. package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +1554 -0
  555. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +150 -4
  556. package/src/modules/workflow/agent-tools/change-request-read-shape.ts +712 -0
  557. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +759 -0
  558. package/src/modules/workflow/agent-tools/change-request-summary.ts +5 -1
  559. package/src/modules/workflow/agent-tools/workflow.tools.ts +137 -35
  560. package/src/modules/workflow/file-lock.service.ts +67 -5
  561. package/src/modules/workflow/git/__tests__/git.service.appliedChange.test.ts +285 -0
  562. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
  563. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
  564. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +334 -0
  565. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
  566. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
  567. package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +72 -2
  568. package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +24 -2
  569. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +620 -1
  570. package/src/modules/workflow/git/git.service.ts +714 -133
  571. package/src/modules/workflow/git/merge-commit.ts +88 -0
  572. package/src/modules/workflow/git/node-git-runner.ts +82 -7
  573. package/src/modules/workflow/git/pull-request.service.ts +380 -54
  574. package/src/modules/workflow/pending-commits.service.ts +29 -1
  575. package/src/modules/workflow/pending-commits.worker.ts +24 -6
  576. package/src/modules/workflow/recovery-bot.ts +1 -1
  577. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +7 -1
  578. package/src/modules/workflow/review-workflow/__tests__/merge-records-own-commit.test.ts +407 -0
  579. package/src/modules/workflow/review-workflow/review-workflow.service.ts +191 -12
  580. package/src/modules/workflow/sanitize-error.ts +50 -0
  581. package/src/modules/workflow/workflow.routes.ts +7 -2
  582. package/src/modules/workflow/workflow.service.ts +886 -112
  583. package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
  584. package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +4 -3
  585. package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
  586. package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
  587. package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
  588. package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
  589. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
  590. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
  591. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
  592. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
  593. package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
  594. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +21 -10
  595. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
  596. package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
  597. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +33 -55
  598. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
  599. package/src/modules/workspace/__tests__/workspace.tools.test.ts +664 -34
  600. package/src/modules/workspace/agent-download.builder.ts +308 -0
  601. package/src/modules/workspace/agent-download.routes.ts +183 -0
  602. package/src/modules/workspace/agent-download.store.ts +533 -0
  603. package/src/modules/workspace/agent-upload.store.ts +23 -7
  604. package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
  605. package/src/modules/workspace/file-readers/text-reader.ts +5 -8
  606. package/src/modules/workspace/first-run.ts +222 -0
  607. package/src/modules/workspace/move-links.ts +237 -0
  608. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +191 -489
  609. package/src/modules/workspace/startup/steps/seed-tree.ts +23 -38
  610. package/src/modules/workspace/startup/steps/template-files.step.ts +217 -249
  611. package/src/modules/workspace/startup/steps/template-source.ts +5 -3
  612. package/src/modules/workspace/workspace.routes.ts +64 -22
  613. package/src/modules/workspace/workspace.service.ts +202 -25
  614. package/src/modules/workspace/workspace.tools.ts +707 -68
  615. package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
  616. package/src/modules/write-access/write-access.ts +9 -0
  617. package/src/shared/domain-errors.ts +123 -11
  618. package/src/shared/git.contract.ts +8 -1
  619. package/src/shared/hidden-tools.ts +45 -0
  620. package/src/shared/utf8-text.ts +21 -0
  621. package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
  622. package/src/tenancy/static-tenant-source.ts +10 -2
  623. package/starter-packs/README.md +103 -0
  624. package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
  625. package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
  626. package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
  627. package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
  628. package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
  629. package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
  630. package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
  631. package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
  632. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
  633. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
  634. package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
  635. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
  636. package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
  637. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
  638. package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
  639. package/starter-packs/engineering/pack.yaml +11 -0
  640. package/starter-packs/general/KnowledgeBase/About us.md +9 -0
  641. package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
  642. package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
  643. package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
  644. package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
  645. package/starter-packs/general/Plugins/general-starter/access.md +19 -0
  646. package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
  647. package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
  648. package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
  649. package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
  650. package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
  651. package/starter-packs/general/pack.yaml +10 -0
  652. package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
  653. package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
  654. package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
  655. package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
  656. package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
  657. package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
  658. package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
  659. package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
  660. package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
  661. package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
  662. package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
  663. package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
  664. package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
  665. package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
  666. package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
  667. package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
  668. package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
  669. package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
  670. package/starter-packs/operations/pack.yaml +12 -0
  671. package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
  672. package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
  673. package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
  674. package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
  675. package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
  676. package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
  677. package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
  678. package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
  679. package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
  680. package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
  681. package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
  682. package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
  683. package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
  684. package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
  685. package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
  686. package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
  687. package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
  688. package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
  689. package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
  690. package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
  691. package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
  692. package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
  693. package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
  694. package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
  695. package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
  696. package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
  697. package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
  698. package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
  699. package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
  700. package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
  701. package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
  702. package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
  703. package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
  704. package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
  705. package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
  706. package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
  707. package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
  708. package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
  709. package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
  710. package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
  711. package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
  712. package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
  713. package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
  714. package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
  715. package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
  716. package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
  717. package/starter-packs/sales/pack.yaml +13 -0
  718. package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
  719. package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
  720. package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
  721. package/starter-packs/support/KnowledgeBase/Products.md +9 -0
  722. package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
  723. package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
  724. package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
  725. package/starter-packs/support/Plugins/support-starter/access.md +19 -0
  726. package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
  727. package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
  728. package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
  729. package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
  730. package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
  731. package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
  732. package/starter-packs/support/pack.yaml +12 -0
  733. package/kb-template/AGENTS.md +0 -730
@@ -0,0 +1,705 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { timingSafeStringEqual } from '../auth/password-hash.js';
3
+ import { ACCOUNT_DEACTIVATED_MESSAGE } from '../auth/account-admission.js';
4
+ import jwt from 'jsonwebtoken';
5
+ import type { AuthUser, IGitService, IWorkflowService } from '@bevel-software/platform-shared';
6
+ import { suggestionsBranchPrefixFor } from '@bevel-software/platform-shared';
7
+ import { logger } from '../../shared/logging.js';
8
+ import { workspaceIdForBranch } from '../../shared/workspace-id.js';
9
+ import type { KbContext } from '../../shared/kb-context.js';
10
+ import type { IAccessControl } from '../access/access-control.interface.js';
11
+ import { changeRequestLink, changeRequestLinkBase } from '../workflow/git/change-request-link.js';
12
+ import type { FileReaderRegistry } from '../workspace/file-readers/file-reader.js';
13
+ import type { WriteValidator } from '../kb-fs/locking-filesystem.js';
14
+ import type {
15
+ EmbedAuthPort,
16
+ EmbedFileView,
17
+ EmbedLinkedAccount,
18
+ EmbedLockResult,
19
+ EmbedNodeIdResolver,
20
+ EmbedWorkspacePort,
21
+ EmbedProposalResult,
22
+ EmbedSubject,
23
+ EmbedTokenResult,
24
+ IEmbedService,
25
+ } from './embed.interface.js';
26
+ import {
27
+ EmbedAccessError,
28
+ EmbedLockedError,
29
+ EmbedNodeNotFoundError,
30
+ EmbedTokenError,
31
+ } from './embed.errors.js';
32
+ import { parseEmbedRef, EmbedRefParseError, isSafeRepoRelativeEmbedPath } from './embed-link.js';
33
+ import type { IAccountLinkService } from './account-link.service.js';
34
+
35
+ const log = logger('embed');
36
+
37
+ /**
38
+ * Token lifetime — long enough for a view left open in a chat, short enough
39
+ * that a token copied out of a transcript stops working on its own. The
40
+ * token rides in `open_page`'s result, so it sits in the chat transcript, and
41
+ * whoever holds it reads and edits that one file as the token's user until
42
+ * it expires. One hour (Razvan, 2026-10-09; the embed used two before): a
43
+ * page left open in a chat is still covered, and the window is halved. A
44
+ * view older than that shows the expired sentence and the agent opens the
45
+ * page again.
46
+ */
47
+ export const EMBED_TOKEN_TTL_SECONDS = 60 * 60;
48
+
49
+ /** What the embed's config needs from the deployment's. */
50
+ export interface EmbedConfig {
51
+ readonly jwtSecret: string;
52
+ readonly embedSharedSecret: string;
53
+ readonly publicFrontendUrl: string;
54
+ readonly kbDirName: string;
55
+ }
56
+
57
+ /**
58
+ * Signed claims carried by an embed token. Deliberately pseudonymous — the
59
+ * token travels as an iframe URL parameter, so it must never carry a direct
60
+ * identifier (an address, a name). An id is the whole of the identity, and
61
+ * everything human-readable is resolved server-side from it.
62
+ */
63
+ interface EmbedClaims {
64
+ scope: 'embed';
65
+ /** Which kind of id `sub` is. */
66
+ kind: 'user' | 'atlassian';
67
+ /** A Hexis user id, or an outside account id. */
68
+ sub: string;
69
+ repoRelative: string;
70
+ /** Heading anchor the view should open at; absent = the top of the file. */
71
+ slug?: string;
72
+ }
73
+
74
+ /** Resolved viewer identity for an embed request. */
75
+ interface ResolvedIdentity {
76
+ /** Whether the token's id resolved to a user at all. */
77
+ linked: boolean;
78
+ user: AuthUser | null;
79
+ /** Read access on the token's file — always false when unresolved (default-deny). */
80
+ canRead: boolean;
81
+ canWrite: boolean;
82
+ }
83
+
84
+ export class EmbedService implements IEmbedService {
85
+ /**
86
+ * Embed tokens are signed with a key DERIVED from the JWT secret (HMAC with
87
+ * a fixed label) rather than the secret itself — domain separation, so an
88
+ * embed token can never be replayed as a session JWT, nor the reverse. The
89
+ * label is part of the token format: changing it invalidates every live
90
+ * token, so it does not change.
91
+ */
92
+ private readonly signingKey: string;
93
+
94
+ constructor(
95
+ private readonly config: EmbedConfig,
96
+ private readonly kb: KbContext,
97
+ private readonly workspaceService: EmbedWorkspacePort,
98
+ private readonly accessControl: IAccessControl,
99
+ private readonly authService: EmbedAuthPort,
100
+ private readonly workflowService: IWorkflowService,
101
+ private readonly gitService: IGitService,
102
+ private readonly accountLinks: IAccountLinkService,
103
+ private readonly readers: FileReaderRegistry,
104
+ /**
105
+ * The pre-disk write gate every other write surface runs — the file
106
+ * editor's and the agent tools' `roles.yaml` validity check. An embed
107
+ * save or proposal writes through `workspaceService.writeFile`, which
108
+ * puts bytes on disk without one, so the gate is asked here first.
109
+ */
110
+ private readonly validateWrite: WriteValidator,
111
+ /** See {@link EmbedNodeIdResolver} — core has none, so core refuses an id reference. */
112
+ private readonly resolveNodeId: EmbedNodeIdResolver | null = null,
113
+ ) {
114
+ this.signingKey = createHmac('sha256', config.jwtSecret)
115
+ .update('bevel-embed-token-v1')
116
+ .digest('hex');
117
+ }
118
+
119
+ // ── mint ───────────────────────────────────────────────────────────────────
120
+
121
+ sharedSecretConfigured(): boolean {
122
+ return this.config.embedSharedSecret.length > 0;
123
+ }
124
+
125
+ verifySharedSecret(secret: string | undefined): boolean {
126
+ const expected = this.config.embedSharedSecret;
127
+ if (!expected || !secret) return false;
128
+ // The one constant-time comparison the platform has (the password hash's).
129
+ return timingSafeStringEqual(secret, expected);
130
+ }
131
+
132
+ async mintForUser(input: { userId: string; reference: string }): Promise<EmbedTokenResult> {
133
+ if (!input.userId) throw new EmbedAccessError('Missing user id');
134
+ return this.mint({ kind: 'user', userId: input.userId }, input.reference);
135
+ }
136
+
137
+ async mintToken(input: {
138
+ accountId: string;
139
+ email?: string;
140
+ reference: string;
141
+ }): Promise<EmbedTokenResult> {
142
+ if (!input.accountId) throw new EmbedAccessError('Missing Atlassian account id');
143
+ // The optional allowed-email-domains guard: a viewer whose domain may not
144
+ // reach the knowledge base must not receive a token either. With a guard
145
+ // configured and no email sent we fail CLOSED — we then cannot confirm the
146
+ // viewer is allowed. (Unchanged from the connector's contract.)
147
+ const email = input.email?.trim().toLowerCase() || undefined;
148
+ if (!this.authService.isEmailDomainAllowed(email ?? '')) {
149
+ throw new EmbedAccessError('Your email domain is not permitted to view this content');
150
+ }
151
+ return this.mint({ kind: 'atlassian', accountId: input.accountId }, input.reference);
152
+ }
153
+
154
+ /**
155
+ * The one mint. Resolves the reference to a path on the default branch,
156
+ * confirms the FILE EXISTS, and signs the claims.
157
+ *
158
+ * Access is NOT checked here and must not be: the two callers check it
159
+ * their own way — `open_page` has already answered the caller the way
160
+ * `read_file` would (refusal and all) before it asks for a token, and the
161
+ * connector's mint is for an account that may not be linked to anyone yet.
162
+ * The load path gates content on the resolved identity's read access, which
163
+ * is the gate that decides what anybody sees.
164
+ */
165
+ private async mint(subject: EmbedSubject, reference: string): Promise<EmbedTokenResult> {
166
+ const ref = parseEmbedRef(reference, this.config.kbDirName);
167
+ let repoRelative: string;
168
+ if ('nodeId' in ref) {
169
+ // One dot-less segment is an id by the copy-link's shape — and ALSO a
170
+ // legal root-level file name (`readme`, `LICENSE`). A file that is
171
+ // actually there wins: it is what the caller named, and `open_page`
172
+ // has just read it by that very path.
173
+ if (isSafeRepoRelativeEmbedPath(ref.nodeId) && (await this.fileExists(ref.nodeId))) {
174
+ repoRelative = ref.nodeId;
175
+ } else {
176
+ // A bare id is the app's copy-link form. Resolving it needs a node
177
+ // graph, which core does not have; a deployment that does registers a
178
+ // resolver (see `EmbedNodeIdResolver`).
179
+ const resolved = this.resolveNodeId ? await this.resolveNodeId(ref.nodeId) : null;
180
+ if (!resolved) throw new EmbedRefParseError(`No file at the reference '${ref.nodeId}'`);
181
+ repoRelative = resolved;
182
+ }
183
+ } else {
184
+ repoRelative = ref.repoRelative;
185
+ }
186
+ // Existence at mint time, so a dead reference fails where the caller can
187
+ // still say something about it rather than inside a rendered iframe. A
188
+ // stat, not a read: a deck or a workbook can be hundreds of megabytes,
189
+ // and the view fetches its bytes later, from `/raw`, if at all.
190
+ await this.assertExists(repoRelative);
191
+ const claims: EmbedClaims = {
192
+ scope: 'embed',
193
+ kind: subject.kind,
194
+ sub: subject.kind === 'user' ? subject.userId : subject.accountId,
195
+ repoRelative,
196
+ slug: ref.slug,
197
+ };
198
+ const token = jwt.sign(claims, this.signingKey, { expiresIn: EMBED_TOKEN_TTL_SECONDS });
199
+ return { token, embedUrl: this.embedUrlFor(token) };
200
+ }
201
+
202
+ /** The view's address — what a host frames. */
203
+ embedUrlFor(token: string): string {
204
+ return `${this.config.publicFrontendUrl}/embed?token=${encodeURIComponent(token)}`;
205
+ }
206
+
207
+ /** A file's address in the app — what a link opens in a new tab. */
208
+ appUrlFor(repoRelative: string, slug?: string): string {
209
+ const wsPath = [this.config.kbDirName, ...repoRelative.split('/')]
210
+ .map(encodeURIComponent)
211
+ .join('/');
212
+ const branch = encodeURIComponent(this.kb.defaultBranch);
213
+ const anchor = slug ? `#${encodeURIComponent(slug)}` : '';
214
+ return `${this.config.publicFrontendUrl}/workspace/${branch}/${wsPath}${anchor}`;
215
+ }
216
+
217
+ // ── read ───────────────────────────────────────────────────────────────────
218
+
219
+ async loadFile(token: string): Promise<EmbedFileView> {
220
+ const claims = this.verifyToken(token);
221
+ const { linked, canRead, canWrite } = await this.resolveIdentity(claims);
222
+ const reader = this.readers.readerFor(claims.repoRelative);
223
+
224
+ const base: EmbedFileView = {
225
+ nodeName: nodeNameFor(claims.repoRelative),
226
+ repoRelative: claims.repoRelative,
227
+ workspacePath: this.wsPathFor(claims.repoRelative),
228
+ kbDirName: this.config.kbDirName,
229
+ branch: this.kb.defaultBranch,
230
+ appUrl: this.appUrlFor(claims.repoRelative, claims.slug),
231
+ ...(claims.slug ? { heading: claims.slug } : {}),
232
+ content: '',
233
+ contentIsText: reader.textEditable,
234
+ linked,
235
+ canRead,
236
+ canWrite,
237
+ linkUrl: `${this.config.publicFrontendUrl}/embed/link?token=${encodeURIComponent(token)}`,
238
+ };
239
+
240
+ // Default-deny, exactly as the app does: no content leaves the backend
241
+ // until the token's identity resolves AND may read this file. The gated
242
+ // view still carries the status so the page can show the right prompt.
243
+ if (!linked || !canRead) return base;
244
+ // A renderer that reads BYTES is handed none here — it fetches them from
245
+ // `/api/embed/raw` under this same token, which keeps a document or an
246
+ // image out of a JSON payload and off the base64 round trip.
247
+ if (!reader.textEditable) return base;
248
+ const bytes = await this.readFileBytes(claims.repoRelative);
249
+ const result = await reader.read(bytes, claims.repoRelative);
250
+ // A refusal IS the file's honest textual answer (unreadable binary under
251
+ // an extension the fallback reader took), and the app shows it as text —
252
+ // but it is not the file's text, so the view neither claims it is nor
253
+ // offers to save over the bytes it stands for. `save` holds the same line.
254
+ const isText = result.kind === 'text';
255
+ const content = isText ? result.text : result.kind === 'refusal' ? result.message : '';
256
+ return { ...base, content, contentIsText: isText, canWrite: canWrite && isText };
257
+ }
258
+
259
+ /**
260
+ * Refuse to write text over a file whose bytes are not text: the reader
261
+ * that would show it answers a refusal, not the content, and a save would
262
+ * replace a binary with the viewer's draft.
263
+ */
264
+ /** A file a page draws rather than a page: bytes no reader edits as text, or an SVG. */
265
+ private isAsset(repoRelative: string): boolean {
266
+ return !this.readers.readerFor(repoRelative).textEditable || /\.svg$/i.test(repoRelative);
267
+ }
268
+
269
+ private async assertTextEditable(repoRelative: string): Promise<void> {
270
+ const reader = this.readers.readerFor(repoRelative);
271
+ if (!reader.textEditable) throw new EmbedAccessError('This file is not editable as text');
272
+ const result = await reader.read(await this.readFileBytes(repoRelative), repoRelative);
273
+ if (result.kind !== 'text') throw new EmbedAccessError('This file is not editable as text');
274
+ }
275
+
276
+ async readBytes(token: string, path?: string): Promise<{ bytes: Buffer; path: string }> {
277
+ const claims = this.verifyToken(token);
278
+ const { linked, user } = await this.resolveIdentity(claims);
279
+ if (!linked || !user) throw new EmbedAccessError('This view is not linked to an account');
280
+ // A path beside the embedded file — the pictures a markdown page shows.
281
+ // Resolved relative to the embedded file's own folder, then gated on the
282
+ // viewer's read access for THAT file: the token scopes the view to one
283
+ // page, never to one page's read permissions.
284
+ const target = path ? resolveBeside(claims.repoRelative, path) : claims.repoRelative;
285
+ if (target === null) throw new EmbedAccessError('That path is not inside this knowledge base');
286
+ // ONE page, as the token promises: beside the page only an ASSET it draws
287
+ // is served — a file no reader edits as text, or an SVG, the one text
288
+ // format a page shows as a picture. Another page's text never comes
289
+ // through this token; that page opens in the app, under its own view.
290
+ if (target !== claims.repoRelative && !this.isAsset(target)) {
291
+ throw new EmbedAccessError('This view shows one page; another page opens in the app');
292
+ }
293
+ const workspaceId = this.defaultWorkspaceId();
294
+ if (!(await this.accessControl.canRead(workspaceId, user.email, target))) {
295
+ throw new EmbedAccessError(`You don't have permission to read "${target}".`);
296
+ }
297
+ return { bytes: await this.readFileBytes(target), path: target };
298
+ }
299
+
300
+ // ── write ──────────────────────────────────────────────────────────────────
301
+
302
+ async acquireLock(token: string): Promise<EmbedLockResult> {
303
+ const { user, wsPath } = await this.requireEditor(token);
304
+ const result = await this.workflowService.acquireLock(
305
+ this.defaultWorkspaceId(),
306
+ this.kb.defaultBranch,
307
+ wsPath,
308
+ user,
309
+ );
310
+ return result.acquired
311
+ ? { acquired: true }
312
+ : { acquired: false, holderName: result.lock.holderName };
313
+ }
314
+
315
+ async heartbeat(token: string): Promise<void> {
316
+ const claims = this.verifyToken(token);
317
+ const { user, canWrite } = await this.resolveIdentity(claims);
318
+ if (!user) return; // nothing held — nothing to keep alive
319
+ const workspaceId = this.defaultWorkspaceId();
320
+ const wsPath = this.wsPathFor(claims.repoRelative);
321
+ // Write access is asked again on every renewal: the token outlives the
322
+ // permission it was minted under, and a writer who lost it must not keep
323
+ // a lock alive that shuts out the people who still have it. The lock
324
+ // goes HERE, not only when the view hears the refusal: a frame that is
325
+ // gone, or a host that never delivers the 403, would otherwise leave the
326
+ // file shut to the writers who still have access until the TTL. Letting
327
+ // go takes no access — the workflow releases only a lock this user holds.
328
+ if (!canWrite) {
329
+ await this.workflowService.releaseLockNoCommit(workspaceId, this.kb.defaultBranch, wsPath, user);
330
+ throw new EmbedAccessError();
331
+ }
332
+ await this.workflowService.heartbeatLock(workspaceId, this.kb.defaultBranch, wsPath, user);
333
+ }
334
+
335
+ async cancel(token: string): Promise<void> {
336
+ const claims = this.verifyToken(token);
337
+ const { user } = await this.resolveIdentity(claims);
338
+ if (!user) return;
339
+ await this.workflowService.releaseLockNoCommit(
340
+ this.defaultWorkspaceId(),
341
+ this.kb.defaultBranch,
342
+ this.wsPathFor(claims.repoRelative),
343
+ user,
344
+ );
345
+ }
346
+
347
+ async save(token: string, content: string): Promise<void> {
348
+ const { claims, user, wsPath } = await this.requireEditor(token);
349
+ await this.assertTextEditable(claims.repoRelative);
350
+ // The write gate before the lock: a text the gate refuses (a `roles.yaml`
351
+ // that would not parse) never takes a lock it has nothing to write under.
352
+ await this.validateWrite(wsPath, content);
353
+ const workspaceId = this.defaultWorkspaceId();
354
+ // Bytes reach the disk ONLY under a lock this viewer holds. ASK who holds
355
+ // it rather than acquiring again: `acquire` is strict and refuses a live
356
+ // lock even to its own holder (so an agent sharing the user id cannot
357
+ // steal a human's edit), which turned every Save into a 409 against the
358
+ // writer's own Edit lock. This is the check the app's file routes make
359
+ // (`workspace.routes` `withLock`). A viewer whose lock lapsed — a frame
360
+ // hidden past the TTL — takes it again if it is free, and is refused if
361
+ // somebody else took it: writing first and finding out at the release
362
+ // would leave their text on disk under the other editor's lock.
363
+ await this.ensureHoldsLock(workspaceId, wsPath, user);
364
+ try {
365
+ await this.workspaceService.writeFile(workspaceId, wsPath, content);
366
+ // Release commits + pushes the on-disk bytes through the background
367
+ // queue — the same path the app's file page takes out of edit mode.
368
+ await this.workflowService.releaseLock(workspaceId, this.kb.defaultBranch, wsPath, user);
369
+ } catch (err) {
370
+ await this.workflowService
371
+ .releaseLockNoCommit(workspaceId, this.kb.defaultBranch, wsPath, user)
372
+ .catch((cleanupErr) => {
373
+ // Best-effort cleanup failed — say so, so a stuck lock is
374
+ // diagnosable, then rethrow the save's own error below.
375
+ log.error(
376
+ `releaseLockNoCommit failed during save cleanup ` +
377
+ `(workspace=${workspaceId} path=${wsPath} user=${user.id})`,
378
+ cleanupErr,
379
+ );
380
+ return undefined;
381
+ });
382
+ throw err;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * File the edit as a change request authored by the viewer, on their own
388
+ * personal suggestions branch — the same branch and the same one-request
389
+ * bundle the app's "Propose changes" uses, so a reader who proposes from a
390
+ * chat and from the app lands in one place.
391
+ *
392
+ * No lock: nothing touches the default branch, and the suggestions branch
393
+ * is this person's own.
394
+ *
395
+ * The write is COMMITTED here, synchronously, before the request is opened.
396
+ * `workspaceService.writeFile` only puts bytes on disk — the app's file
397
+ * routes commit by releasing the lock they took, which enqueues the commit
398
+ * for the background worker. Neither is usable here: without a commit the
399
+ * branch head never moves, and the request opens against a tree identical
400
+ * to its base — `changedFiles: 0`, and a reviewer told "this pull request
401
+ * has no file changes to approve" about a proposal that is sitting
402
+ * uncommitted on disk. An ENQUEUED commit has the same problem with a race
403
+ * on top of it. `commitChanges` commits and pushes before returning, which
404
+ * is what the one other server-side propose flow (the plugin join request)
405
+ * does for exactly this reason.
406
+ */
407
+ async propose(token: string, content: string): Promise<EmbedProposalResult> {
408
+ const claims = this.verifyToken(token);
409
+ const { user, canRead } = await this.resolveIdentity(claims);
410
+ if (!user) throw new EmbedAccessError('Link your account to propose a change');
411
+ // Read access is the floor for proposing: a change request quotes the
412
+ // file's new text beside its old, so proposing on a file you may not read
413
+ // would publish what you were not allowed to see.
414
+ if (!canRead) throw new EmbedAccessError(`You don't have permission to read "${claims.repoRelative}".`);
415
+ // The guard a save runs: a proposal is text, so what it proposes over
416
+ // has to be text too — a PDF, an image or bytes under a markdown name
417
+ // are refused here as they are on Save, not committed as text.
418
+ await this.assertTextEditable(claims.repoRelative);
419
+ const wsPath = this.wsPathFor(claims.repoRelative);
420
+ // The same write gate as a save, before a branch is made for the text: a
421
+ // proposal is a commit, and a commit of an unparseable `roles.yaml` is
422
+ // exactly what the gate exists to keep out of the repository.
423
+ await this.validateWrite(wsPath, content);
424
+ const branch = `${suggestionsBranchPrefixFor({ email: user.email, id: user.id })}knowledge`;
425
+ // Create the branch when it is not there yet; an existing one is reused,
426
+ // which is what bundles a person's proposals into one request. Asked of
427
+ // ORIGIN first: a second proposal finds the branch already pushed, and
428
+ // creating it again would fail as a non-fast-forward push, not as
429
+ // "already exists" — the race between the two is still caught below.
430
+ // Created through the workflow service, under its branch-lifecycle lock,
431
+ // as every branch the app creates is: creating one is the one operation
432
+ // that can bring a "gone" branch back under a retirement's feet.
433
+ if (!(await this.gitService.remoteBranchExists(this.defaultWorkspaceId(), branch))) {
434
+ await this.workflowService
435
+ .createBranch(this.defaultWorkspaceId(), branch, this.kb.defaultBranch)
436
+ .catch((err: unknown) => {
437
+ if (err instanceof Error && /already exists|non-fast-forward|fetch first/i.test(err.message)) return;
438
+ throw err;
439
+ });
440
+ }
441
+ const workspace = await this.workspaceService.getOrCreateForBranch(branch);
442
+ // One proposal at a time per file on this branch: two from the same
443
+ // person landing together would otherwise write over each other before
444
+ // either commit staged the file, and one would commit the other's text.
445
+ // The workspace service's own per-path turn is that queue — the one the
446
+ // app's file routes take around a write whose decision spans more than
447
+ // one call — and the write inside takes the same turn, re-entrantly.
448
+ await this.workspaceService.withPathTurn(workspace.id, wsPath, async () => {
449
+ await this.workspaceService.writeFile(workspace.id, wsPath, content);
450
+ // Scoped to the one path this proposal is about: the suggestions branch
451
+ // is shared by everything this person has proposed, and a bare commit
452
+ // would sweep in another in-flight write of theirs under this message.
453
+ await this.workflowService.commitChanges(
454
+ workspace.id,
455
+ user,
456
+ `Propose changes to ${claims.repoRelative}`,
457
+ [wsPath],
458
+ );
459
+ });
460
+ try {
461
+ const created = await this.workflowService.openChangeRequest(workspace.id, user, {
462
+ sourceBranch: branch,
463
+ targetBranch: this.kb.defaultBranch,
464
+ title: `Changes from ${user.name}. Knowledge`,
465
+ });
466
+ return {
467
+ branch,
468
+ ...(typeof created?.number === 'number'
469
+ ? { number: created.number, url: this.changeRequestUrl(created.number) }
470
+ : {}),
471
+ };
472
+ } catch (err) {
473
+ // The person's one open Knowledge request already covers this branch —
474
+ // which is the state this method is trying to reach, not a failure. The
475
+ // refusal names it, so point at it.
476
+ const existing = duplicateRequestNumber(err);
477
+ if (existing === null) throw err;
478
+ return { branch, number: existing, url: this.changeRequestUrl(existing) };
479
+ }
480
+ }
481
+
482
+ // ── account links ──────────────────────────────────────────────────────────
483
+
484
+ async linkAccount(token: string, userId: string): Promise<void> {
485
+ const claims = this.verifyToken(token);
486
+ // Only an outside account has anything to link; a token already minted
487
+ // for a Hexis user names its person and must not re-point at another.
488
+ if (claims.kind !== 'atlassian') {
489
+ throw new EmbedAccessError('This view is already signed in');
490
+ }
491
+ await this.accountLinks.link(claims.sub, userId);
492
+ }
493
+
494
+ async listLinkedAccounts(userId: string): Promise<EmbedLinkedAccount[]> {
495
+ const rows = await this.accountLinks.listForUser(userId);
496
+ return rows.map((r) => ({
497
+ atlassianAccountId: r.atlassianAccountId,
498
+ createdAt: r.createdAt.getTime(),
499
+ }));
500
+ }
501
+
502
+ async unlinkAccount(userId: string, accountId: string): Promise<boolean> {
503
+ return this.accountLinks.unlink(userId, accountId);
504
+ }
505
+
506
+ // ── internals ──────────────────────────────────────────────────────────────
507
+
508
+ /**
509
+ * Make sure `user` holds an EDIT lock on `wsPath` on the default branch:
510
+ * keep it when they already do, take it when nobody does (an expired row
511
+ * reads as none), refuse with the holder's name when somebody else does.
512
+ */
513
+ private async ensureHoldsLock(workspaceId: string, wsPath: string, user: AuthUser): Promise<void> {
514
+ const branch = this.kb.defaultBranch;
515
+ const current = await this.workflowService.getLock(workspaceId, branch, wsPath);
516
+ if (current) {
517
+ // A coordination hold grants no write authority, so it is not "ours"
518
+ // for a save even when the id matches.
519
+ if (current.holderUserId === user.id && current.mode !== 'coordination') return;
520
+ throw new EmbedLockedError(current.holderName);
521
+ }
522
+ const taken = await this.workflowService.acquireLock(workspaceId, branch, wsPath, user);
523
+ if (!taken.acquired) throw new EmbedLockedError(taken.lock.holderName);
524
+ }
525
+
526
+ private defaultWorkspaceId(): string {
527
+ return workspaceIdForBranch(this.kb.defaultBranch);
528
+ }
529
+
530
+ private wsPathFor(repoRelative: string): string {
531
+ return `${this.config.kbDirName}/${repoRelative}`;
532
+ }
533
+
534
+ /**
535
+ * A change request's address, through the same builder the workflow tools
536
+ * use — so a proposal made from a chat and one made by an agent point at
537
+ * the same place, path prefix of a proxied deployment included.
538
+ */
539
+ private changeRequestUrl(number: number): string {
540
+ return changeRequestLink(number, changeRequestLinkBase(this.config.publicFrontendUrl)).url;
541
+ }
542
+
543
+ /**
544
+ * Resolve, authorize and locate an edit. Throws `EmbedAccessError` when the
545
+ * identity does not resolve or may not write. Returns the user (for lock
546
+ * attribution) and the workspace-relative path.
547
+ */
548
+ private async requireEditor(
549
+ token: string,
550
+ ): Promise<{ claims: EmbedClaims; user: AuthUser; wsPath: string }> {
551
+ const claims = this.verifyToken(token);
552
+ const { user, canWrite } = await this.resolveIdentity(claims);
553
+ if (!user) throw new EmbedAccessError('Link your account to edit');
554
+ if (!canWrite) throw new EmbedAccessError();
555
+ await this.workspaceService.getOrCreateForBranch(this.kb.defaultBranch);
556
+ return { claims, user, wsPath: this.wsPathFor(claims.repoRelative) };
557
+ }
558
+
559
+ /** Resolve the token's identity to a user + read/write verdicts. */
560
+ private async resolveIdentity(claims: EmbedClaims): Promise<ResolvedIdentity> {
561
+ const unresolved: ResolvedIdentity = { linked: false, user: null, canRead: false, canWrite: false };
562
+ const userId =
563
+ claims.kind === 'user' ? claims.sub : await this.accountLinks.getUserId(claims.sub);
564
+ if (!userId) return unresolved;
565
+ const user = await this.authService.getUserById(userId);
566
+ if (!user) return unresolved;
567
+ // The gate every credential passes: a token minted for an account an
568
+ // admin has since switched off reads, saves and proposes nothing, and is
569
+ // told what every other door tells that person.
570
+ if (!(await this.authService.isActive(userId))) throw new EmbedAccessError(ACCOUNT_DEACTIVATED_MESSAGE);
571
+ const workspaceId = this.defaultWorkspaceId();
572
+ const canRead = await this.accessControl.canRead(workspaceId, user.email, claims.repoRelative);
573
+ // Write implies read in the access resolver; the `canRead &&` guard pins
574
+ // that here, so no caller can see canWrite without canRead.
575
+ const canWrite =
576
+ canRead && (await this.accessControl.canWrite(workspaceId, user.email, claims.repoRelative));
577
+ return { linked: true, user, canRead, canWrite };
578
+ }
579
+
580
+ /** Whether `repoRelative` is a file on the default branch — a stat, never a read. */
581
+ private async fileExists(repoRelative: string): Promise<boolean> {
582
+ try {
583
+ await this.assertExists(repoRelative);
584
+ return true;
585
+ } catch (err) {
586
+ if (err instanceof EmbedNodeNotFoundError) return false;
587
+ throw err;
588
+ }
589
+ }
590
+
591
+ /** The typed 404 of {@link readFileBytes}, for a reference, without reading a byte. */
592
+ private async assertExists(repoRelative: string): Promise<void> {
593
+ const workspaceId = this.defaultWorkspaceId();
594
+ await this.workspaceService.getOrCreateForBranch(this.kb.defaultBranch);
595
+ if (!(await this.workspaceService.isFile(workspaceId, this.wsPathFor(repoRelative)))) {
596
+ throw new EmbedNodeNotFoundError(`${repoRelative} doesn't exist on ${this.kb.defaultBranch}`);
597
+ }
598
+ }
599
+
600
+ private async readFileBytes(repoRelative: string): Promise<Buffer> {
601
+ const workspaceId = this.defaultWorkspaceId();
602
+ await this.workspaceService.getOrCreateForBranch(this.kb.defaultBranch);
603
+ try {
604
+ const read = await this.workspaceService.readFileBinary(workspaceId, this.wsPathFor(repoRelative));
605
+ return Buffer.isBuffer(read) ? read : Buffer.from(read);
606
+ } catch (err) {
607
+ // A missing file is a bad or stale reference, not a server fault —
608
+ // surface it as a typed 404 so the view says what is wrong instead of
609
+ // showing a 500. Everything else (EACCES, EIO, …) stays unexpected.
610
+ const code = (err as NodeJS.ErrnoException)?.code;
611
+ if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EISDIR') {
612
+ throw new EmbedNodeNotFoundError(
613
+ `${repoRelative} doesn't exist on ${this.kb.defaultBranch}`,
614
+ );
615
+ }
616
+ throw err;
617
+ }
618
+ }
619
+
620
+ private verifyToken(token: string): EmbedClaims {
621
+ let decoded: unknown;
622
+ try {
623
+ decoded = jwt.verify(token, this.signingKey);
624
+ } catch {
625
+ throw new EmbedTokenError();
626
+ }
627
+ const c = decoded as Partial<EmbedClaims> & { accountId?: unknown };
628
+ if (c?.scope !== 'embed' || typeof c.repoRelative !== 'string' || !c.repoRelative) {
629
+ throw new EmbedTokenError();
630
+ }
631
+ // A token minted by the release BEFORE the subject was generalised keyed
632
+ // the identity on `accountId` alone. Those were minted for two hours, so an upgrade
633
+ // would otherwise expire every open Atlassian panel on the spot; reading
634
+ // the old spelling costs one branch and keeps them working.
635
+ if (typeof c.accountId === 'string' && c.accountId) {
636
+ return { scope: 'embed', kind: 'atlassian', sub: c.accountId, repoRelative: c.repoRelative, slug: c.slug };
637
+ }
638
+ if ((c.kind !== 'user' && c.kind !== 'atlassian') || typeof c.sub !== 'string' || !c.sub) {
639
+ throw new EmbedTokenError();
640
+ }
641
+ return { scope: 'embed', kind: c.kind, sub: c.sub, repoRelative: c.repoRelative, slug: c.slug };
642
+ }
643
+ }
644
+
645
+ /** Display name = file basename without its extension. */
646
+ function nodeNameFor(repoRelative: string): string {
647
+ const base = repoRelative.split('/').pop() ?? repoRelative;
648
+ const dot = base.lastIndexOf('.');
649
+ return dot > 0 ? base.slice(0, dot) : base;
650
+ }
651
+
652
+ /**
653
+ * A path written relative to `from`'s folder, resolved to a repo-relative
654
+ * path — or null when it escapes the repository or is otherwise unsafe.
655
+ * A path with a leading slash (`/x`) is taken from the repository root:
656
+ * that is the form the embed view sends, translating the workspace path a
657
+ * renderer hands it.
658
+ */
659
+ export function resolveBeside(from: string, path: string): string | null {
660
+ // eslint-disable-next-line no-control-regex
661
+ if (/[\x00-\x1f\x7f]/.test(path)) return null;
662
+ let decoded = path;
663
+ try {
664
+ decoded = decodeURIComponent(path);
665
+ } catch {
666
+ return null;
667
+ }
668
+ // Judged again decoded: `%0a` is a newline once the escape is undone.
669
+ // eslint-disable-next-line no-control-regex
670
+ if (/[\x00-\x1f\x7f]/.test(decoded)) return null;
671
+ const absolute = decoded.startsWith('/');
672
+ const base = absolute ? [] : from.split('/').slice(0, -1);
673
+ const parts = [...base];
674
+ for (const seg of decoded.replace(/^\/+/, '').split('/')) {
675
+ if (seg === '' || seg === '.') continue;
676
+ if (seg === '..') {
677
+ // Out of the repository — never a path this view may serve.
678
+ if (parts.length === 0) return null;
679
+ parts.pop();
680
+ continue;
681
+ }
682
+ parts.push(seg);
683
+ }
684
+ return parts.length > 0 ? parts.join('/') : null;
685
+ }
686
+
687
+ /**
688
+ * The number of the open request the server says already covers this branch
689
+ * pair, or null when the failure was something else. The refusal carries the
690
+ * number precisely so a caller can point at the request instead of treating
691
+ * the state as broken.
692
+ */
693
+ function duplicateRequestNumber(err: unknown): number | null {
694
+ const detail = (err as { details?: { existingNumber?: unknown }; existingNumber?: unknown } | null) ?? null;
695
+ const candidate = detail?.details?.existingNumber ?? detail?.existingNumber;
696
+ if (typeof candidate === 'number') return candidate;
697
+ if (err instanceof Error) {
698
+ // The refusal's own sentence ("An open change request already exists
699
+ // from … to … (#12)"), and only that shape: a count or a numeric token
700
+ // after some other "already" is not a request.
701
+ const match = /change request already exists[^#]*#(\d+)/i.exec(err.message);
702
+ if (match) return Number(match[1]);
703
+ }
704
+ return null;
705
+ }