@bevel-software/platform-core-backend 0.1.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 (720) hide show
  1. package/LICENSE +202 -0
  2. package/dist/assets.d.ts +5 -0
  3. package/dist/assets.d.ts.map +1 -0
  4. package/dist/assets.js +23 -0
  5. package/dist/assets.js.map +1 -0
  6. package/dist/core/core-ports.d.ts +83 -0
  7. package/dist/core/core-ports.d.ts.map +1 -0
  8. package/dist/core/core-ports.js +12 -0
  9. package/dist/core/core-ports.js.map +1 -0
  10. package/dist/core/create-core-server.d.ts +62 -0
  11. package/dist/core/create-core-server.d.ts.map +1 -0
  12. package/dist/core/create-core-server.js +204 -0
  13. package/dist/core/create-core-server.js.map +1 -0
  14. package/dist/core/create-core-services.d.ts +95 -0
  15. package/dist/core/create-core-services.d.ts.map +1 -0
  16. package/dist/core/create-core-services.js +352 -0
  17. package/dist/core/create-core-services.js.map +1 -0
  18. package/dist/core-config.d.ts +119 -0
  19. package/dist/core-config.d.ts.map +1 -0
  20. package/dist/core-config.js +240 -0
  21. package/dist/core-config.js.map +1 -0
  22. package/dist/index.d.ts +36 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +36 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/modules/access/access-control.interface.d.ts +348 -0
  27. package/dist/modules/access/access-control.interface.d.ts.map +1 -0
  28. package/dist/modules/access/access-control.interface.js +2 -0
  29. package/dist/modules/access/access-control.interface.js.map +1 -0
  30. package/dist/modules/access/access-control.service.d.ts +323 -0
  31. package/dist/modules/access/access-control.service.d.ts.map +1 -0
  32. package/dist/modules/access/access-control.service.js +1431 -0
  33. package/dist/modules/access/access-control.service.js.map +1 -0
  34. package/dist/modules/access/access-errors.d.ts +34 -0
  35. package/dist/modules/access/access-errors.d.ts.map +1 -0
  36. package/dist/modules/access/access-errors.js +40 -0
  37. package/dist/modules/access/access-errors.js.map +1 -0
  38. package/dist/modules/access/access-mutation.service.d.ts +176 -0
  39. package/dist/modules/access/access-mutation.service.d.ts.map +1 -0
  40. package/dist/modules/access/access-mutation.service.js +307 -0
  41. package/dist/modules/access/access-mutation.service.js.map +1 -0
  42. package/dist/modules/access/access-splice.d.ts +98 -0
  43. package/dist/modules/access/access-splice.d.ts.map +1 -0
  44. package/dist/modules/access/access-splice.js +346 -0
  45. package/dist/modules/access/access-splice.js.map +1 -0
  46. package/dist/modules/access/access.routes.d.ts +10 -0
  47. package/dist/modules/access/access.routes.d.ts.map +1 -0
  48. package/dist/modules/access/access.routes.js +757 -0
  49. package/dist/modules/access/access.routes.js.map +1 -0
  50. package/dist/modules/access/creator-access.d.ts +113 -0
  51. package/dist/modules/access/creator-access.d.ts.map +1 -0
  52. package/dist/modules/access/creator-access.js +212 -0
  53. package/dist/modules/access/creator-access.js.map +1 -0
  54. package/dist/modules/access/kb-read-filter.d.ts +41 -0
  55. package/dist/modules/access/kb-read-filter.d.ts.map +1 -0
  56. package/dist/modules/access/kb-read-filter.js +60 -0
  57. package/dist/modules/access/kb-read-filter.js.map +1 -0
  58. package/dist/modules/access/roles-admin.service.d.ts +240 -0
  59. package/dist/modules/access/roles-admin.service.d.ts.map +1 -0
  60. package/dist/modules/access/roles-admin.service.js +630 -0
  61. package/dist/modules/access/roles-admin.service.js.map +1 -0
  62. package/dist/modules/access/roles-edit.d.ts +91 -0
  63. package/dist/modules/access/roles-edit.d.ts.map +1 -0
  64. package/dist/modules/access/roles-edit.js +232 -0
  65. package/dist/modules/access/roles-edit.js.map +1 -0
  66. package/dist/modules/access/roles-yaml-guard.d.ts +56 -0
  67. package/dist/modules/access/roles-yaml-guard.d.ts.map +1 -0
  68. package/dist/modules/access/roles-yaml-guard.js +79 -0
  69. package/dist/modules/access/roles-yaml-guard.js.map +1 -0
  70. package/dist/modules/admin/admin-access.routes.d.ts +18 -0
  71. package/dist/modules/admin/admin-access.routes.d.ts.map +1 -0
  72. package/dist/modules/admin/admin-access.routes.js +26 -0
  73. package/dist/modules/admin/admin-access.routes.js.map +1 -0
  74. package/dist/modules/admin/admin-access.service.d.ts +24 -0
  75. package/dist/modules/admin/admin-access.service.d.ts.map +1 -0
  76. package/dist/modules/admin/admin-access.service.js +41 -0
  77. package/dist/modules/admin/admin-access.service.js.map +1 -0
  78. package/dist/modules/admin/admin.interface.d.ts +11 -0
  79. package/dist/modules/admin/admin.interface.d.ts.map +1 -0
  80. package/dist/modules/admin/admin.interface.js +2 -0
  81. package/dist/modules/admin/admin.interface.js.map +1 -0
  82. package/dist/modules/auth/account-erasure.service.d.ts +80 -0
  83. package/dist/modules/auth/account-erasure.service.d.ts.map +1 -0
  84. package/dist/modules/auth/account-erasure.service.js +91 -0
  85. package/dist/modules/auth/account-erasure.service.js.map +1 -0
  86. package/dist/modules/auth/auth.middleware.d.ts +23 -0
  87. package/dist/modules/auth/auth.middleware.d.ts.map +1 -0
  88. package/dist/modules/auth/auth.middleware.js +68 -0
  89. package/dist/modules/auth/auth.middleware.js.map +1 -0
  90. package/dist/modules/auth/auth.routes.d.ts +25 -0
  91. package/dist/modules/auth/auth.routes.d.ts.map +1 -0
  92. package/dist/modules/auth/auth.routes.js +72 -0
  93. package/dist/modules/auth/auth.routes.js.map +1 -0
  94. package/dist/modules/auth/auth.service.d.ts +76 -0
  95. package/dist/modules/auth/auth.service.d.ts.map +1 -0
  96. package/dist/modules/auth/auth.service.js +168 -0
  97. package/dist/modules/auth/auth.service.js.map +1 -0
  98. package/dist/modules/code-mode/code-mode-names.d.ts +16 -0
  99. package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -0
  100. package/dist/modules/code-mode/code-mode-names.js +30 -0
  101. package/dist/modules/code-mode/code-mode-names.js.map +1 -0
  102. package/dist/modules/code-mode/code-mode.tool.d.ts +13 -0
  103. package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -0
  104. package/dist/modules/code-mode/code-mode.tool.js +112 -0
  105. package/dist/modules/code-mode/code-mode.tool.js.map +1 -0
  106. package/dist/modules/code-mode/index.d.ts +3 -0
  107. package/dist/modules/code-mode/index.d.ts.map +1 -0
  108. package/dist/modules/code-mode/index.js +3 -0
  109. package/dist/modules/code-mode/index.js.map +1 -0
  110. package/dist/modules/database/connection.d.ts +6 -0
  111. package/dist/modules/database/connection.d.ts.map +1 -0
  112. package/dist/modules/database/connection.js +12 -0
  113. package/dist/modules/database/connection.js.map +1 -0
  114. package/dist/modules/database/core-schema.d.ts +2273 -0
  115. package/dist/modules/database/core-schema.d.ts.map +1 -0
  116. package/dist/modules/database/core-schema.js +413 -0
  117. package/dist/modules/database/core-schema.js.map +1 -0
  118. package/dist/modules/database/migrate.d.ts +10 -0
  119. package/dist/modules/database/migrate.d.ts.map +1 -0
  120. package/dist/modules/database/migrate.js +40 -0
  121. package/dist/modules/database/migrate.js.map +1 -0
  122. package/dist/modules/database/schema.d.ts +12 -0
  123. package/dist/modules/database/schema.d.ts.map +1 -0
  124. package/dist/modules/database/schema.js +12 -0
  125. package/dist/modules/database/schema.js.map +1 -0
  126. package/dist/modules/diff/diff-paths.d.ts +2 -0
  127. package/dist/modules/diff/diff-paths.d.ts.map +1 -0
  128. package/dist/modules/diff/diff-paths.js +9 -0
  129. package/dist/modules/diff/diff-paths.js.map +1 -0
  130. package/dist/modules/diff/diff.config.d.ts +8 -0
  131. package/dist/modules/diff/diff.config.d.ts.map +1 -0
  132. package/dist/modules/diff/diff.config.js +13 -0
  133. package/dist/modules/diff/diff.config.js.map +1 -0
  134. package/dist/modules/diff/diff.interface.d.ts +73 -0
  135. package/dist/modules/diff/diff.interface.d.ts.map +1 -0
  136. package/dist/modules/diff/diff.interface.js +2 -0
  137. package/dist/modules/diff/diff.interface.js.map +1 -0
  138. package/dist/modules/diff/diff.routes.d.ts +35 -0
  139. package/dist/modules/diff/diff.routes.d.ts.map +1 -0
  140. package/dist/modules/diff/diff.routes.js +228 -0
  141. package/dist/modules/diff/diff.routes.js.map +1 -0
  142. package/dist/modules/diff/diff.service.d.ts +51 -0
  143. package/dist/modules/diff/diff.service.d.ts.map +1 -0
  144. package/dist/modules/diff/diff.service.js +489 -0
  145. package/dist/modules/diff/diff.service.js.map +1 -0
  146. package/dist/modules/diff/line-diff.d.ts +16 -0
  147. package/dist/modules/diff/line-diff.d.ts.map +1 -0
  148. package/dist/modules/diff/line-diff.js +67 -0
  149. package/dist/modules/diff/line-diff.js.map +1 -0
  150. package/dist/modules/mcp/mcp-auth.middleware.d.ts +27 -0
  151. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -0
  152. package/dist/modules/mcp/mcp-auth.middleware.js +116 -0
  153. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -0
  154. package/dist/modules/mcp/mcp-session-store.d.ts +74 -0
  155. package/dist/modules/mcp/mcp-session-store.d.ts.map +1 -0
  156. package/dist/modules/mcp/mcp-session-store.js +131 -0
  157. package/dist/modules/mcp/mcp-session-store.js.map +1 -0
  158. package/dist/modules/mcp/mcp.routes.d.ts +24 -0
  159. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -0
  160. package/dist/modules/mcp/mcp.routes.js +229 -0
  161. package/dist/modules/mcp/mcp.routes.js.map +1 -0
  162. package/dist/modules/mcp/mcp.service.d.ts +198 -0
  163. package/dist/modules/mcp/mcp.service.d.ts.map +1 -0
  164. package/dist/modules/mcp/mcp.service.js +840 -0
  165. package/dist/modules/mcp/mcp.service.js.map +1 -0
  166. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +76 -0
  167. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -0
  168. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +291 -0
  169. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -0
  170. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +18 -0
  171. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -0
  172. package/dist/modules/mcp/oauth/oauth-consent.routes.js +52 -0
  173. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -0
  174. package/dist/modules/mcp/oauth/oauth-state.d.ts +32 -0
  175. package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -0
  176. package/dist/modules/mcp/oauth/oauth-state.js +34 -0
  177. package/dist/modules/mcp/oauth/oauth-state.js.map +1 -0
  178. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +92 -0
  179. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -0
  180. package/dist/modules/secrets-vault/db-secrets-vault.service.js +728 -0
  181. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -0
  182. package/dist/modules/secrets-vault/index.d.ts +7 -0
  183. package/dist/modules/secrets-vault/index.d.ts.map +1 -0
  184. package/dist/modules/secrets-vault/index.js +6 -0
  185. package/dist/modules/secrets-vault/index.js.map +1 -0
  186. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts +63 -0
  187. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -0
  188. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +269 -0
  189. package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -0
  190. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +46 -0
  191. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -0
  192. package/dist/modules/secrets-vault/secrets-variable-loader.js +87 -0
  193. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -0
  194. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +210 -0
  195. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -0
  196. package/dist/modules/secrets-vault/secrets-vault.contract.js +90 -0
  197. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -0
  198. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +30 -0
  199. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -0
  200. package/dist/modules/secrets-vault/secrets-vault.routes.js +490 -0
  201. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -0
  202. package/dist/modules/skills/index.d.ts +5 -0
  203. package/dist/modules/skills/index.d.ts.map +1 -0
  204. package/dist/modules/skills/index.js +4 -0
  205. package/dist/modules/skills/index.js.map +1 -0
  206. package/dist/modules/skills/skills.contract.d.ts +58 -0
  207. package/dist/modules/skills/skills.contract.d.ts.map +1 -0
  208. package/dist/modules/skills/skills.contract.js +10 -0
  209. package/dist/modules/skills/skills.contract.js.map +1 -0
  210. package/dist/modules/skills/skills.routes.d.ts +15 -0
  211. package/dist/modules/skills/skills.routes.d.ts.map +1 -0
  212. package/dist/modules/skills/skills.routes.js +34 -0
  213. package/dist/modules/skills/skills.routes.js.map +1 -0
  214. package/dist/modules/skills/skills.service.d.ts +21 -0
  215. package/dist/modules/skills/skills.service.d.ts.map +1 -0
  216. package/dist/modules/skills/skills.service.js +202 -0
  217. package/dist/modules/skills/skills.service.js.map +1 -0
  218. package/dist/modules/skills/skills.tools.d.ts +15 -0
  219. package/dist/modules/skills/skills.tools.d.ts.map +1 -0
  220. package/dist/modules/skills/skills.tools.js +96 -0
  221. package/dist/modules/skills/skills.tools.js.map +1 -0
  222. package/dist/modules/tool-auth/external-api-key.errors.d.ts +14 -0
  223. package/dist/modules/tool-auth/external-api-key.errors.d.ts.map +1 -0
  224. package/dist/modules/tool-auth/external-api-key.errors.js +23 -0
  225. package/dist/modules/tool-auth/external-api-key.errors.js.map +1 -0
  226. package/dist/modules/tool-auth/external-api-key.interface.d.ts +90 -0
  227. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -0
  228. package/dist/modules/tool-auth/external-api-key.interface.js +2 -0
  229. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -0
  230. package/dist/modules/tool-auth/external-api-key.service.d.ts +25 -0
  231. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -0
  232. package/dist/modules/tool-auth/external-api-key.service.js +164 -0
  233. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -0
  234. package/dist/modules/tool-auth/internal-token.service.d.ts +62 -0
  235. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -0
  236. package/dist/modules/tool-auth/internal-token.service.js +85 -0
  237. package/dist/modules/tool-auth/internal-token.service.js.map +1 -0
  238. package/dist/modules/tool-auth/llm-usage-meter.d.ts +16 -0
  239. package/dist/modules/tool-auth/llm-usage-meter.d.ts.map +1 -0
  240. package/dist/modules/tool-auth/llm-usage-meter.js +6 -0
  241. package/dist/modules/tool-auth/llm-usage-meter.js.map +1 -0
  242. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +101 -0
  243. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -0
  244. package/dist/modules/tool-auth/tool-auth.middleware.js +166 -0
  245. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -0
  246. package/dist/modules/tool-helpers/index.d.ts +19 -0
  247. package/dist/modules/tool-helpers/index.d.ts.map +1 -0
  248. package/dist/modules/tool-helpers/index.js +18 -0
  249. package/dist/modules/tool-helpers/index.js.map +1 -0
  250. package/dist/modules/tool-helpers/tool-context.d.ts +35 -0
  251. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -0
  252. package/dist/modules/tool-helpers/tool-context.js +66 -0
  253. package/dist/modules/tool-helpers/tool-context.js.map +1 -0
  254. package/dist/modules/tool-helpers/tool-def.d.ts +33 -0
  255. package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -0
  256. package/dist/modules/tool-helpers/tool-def.js +57 -0
  257. package/dist/modules/tool-helpers/tool-def.js.map +1 -0
  258. package/dist/modules/tool-helpers/tool-handler.d.ts +19 -0
  259. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -0
  260. package/dist/modules/tool-helpers/tool-handler.js +84 -0
  261. package/dist/modules/tool-helpers/tool-handler.js.map +1 -0
  262. package/dist/modules/tool-helpers/tool.contract.d.ts +64 -0
  263. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -0
  264. package/dist/modules/tool-helpers/tool.contract.js +24 -0
  265. package/dist/modules/tool-helpers/tool.contract.js.map +1 -0
  266. package/dist/modules/tool-helpers/validate-token.d.ts +31 -0
  267. package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -0
  268. package/dist/modules/tool-helpers/validate-token.js +20 -0
  269. package/dist/modules/tool-helpers/validate-token.js.map +1 -0
  270. package/dist/modules/tool-manuals/index.d.ts +5 -0
  271. package/dist/modules/tool-manuals/index.d.ts.map +1 -0
  272. package/dist/modules/tool-manuals/index.js +5 -0
  273. package/dist/modules/tool-manuals/index.js.map +1 -0
  274. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +182 -0
  275. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -0
  276. package/dist/modules/tool-manuals/tool-manuals.contract.js +19 -0
  277. package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -0
  278. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +24 -0
  279. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -0
  280. package/dist/modules/tool-manuals/tool-manuals.routes.js +102 -0
  281. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -0
  282. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +108 -0
  283. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -0
  284. package/dist/modules/tool-manuals/tool-manuals.service.js +710 -0
  285. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -0
  286. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +35 -0
  287. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -0
  288. package/dist/modules/tool-manuals/tool-manuals.tools.js +157 -0
  289. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -0
  290. package/dist/modules/tool-registry/manual.routes.d.ts +6 -0
  291. package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -0
  292. package/dist/modules/tool-registry/manual.routes.js +76 -0
  293. package/dist/modules/tool-registry/manual.routes.js.map +1 -0
  294. package/dist/modules/tool-registry/tool-registry.d.ts +23 -0
  295. package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -0
  296. package/dist/modules/tool-registry/tool-registry.js +46 -0
  297. package/dist/modules/tool-registry/tool-registry.js.map +1 -0
  298. package/dist/modules/tool-registry/tool.contract.d.ts +48 -0
  299. package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -0
  300. package/dist/modules/tool-registry/tool.contract.js +2 -0
  301. package/dist/modules/tool-registry/tool.contract.js.map +1 -0
  302. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +14 -0
  303. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -0
  304. package/dist/modules/workflow/agent-tools/workflow.tools.js +437 -0
  305. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -0
  306. package/dist/modules/workflow/event-bus.d.ts +107 -0
  307. package/dist/modules/workflow/event-bus.d.ts.map +1 -0
  308. package/dist/modules/workflow/event-bus.js +224 -0
  309. package/dist/modules/workflow/event-bus.js.map +1 -0
  310. package/dist/modules/workflow/events.routes.d.ts +80 -0
  311. package/dist/modules/workflow/events.routes.d.ts.map +1 -0
  312. package/dist/modules/workflow/events.routes.js +216 -0
  313. package/dist/modules/workflow/events.routes.js.map +1 -0
  314. package/dist/modules/workflow/file-change-notifier.d.ts +38 -0
  315. package/dist/modules/workflow/file-change-notifier.d.ts.map +1 -0
  316. package/dist/modules/workflow/file-change-notifier.js +22 -0
  317. package/dist/modules/workflow/file-change-notifier.js.map +1 -0
  318. package/dist/modules/workflow/file-lock.service.d.ts +57 -0
  319. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -0
  320. package/dist/modules/workflow/file-lock.service.js +202 -0
  321. package/dist/modules/workflow/file-lock.service.js.map +1 -0
  322. package/dist/modules/workflow/git/branch-name.d.ts +10 -0
  323. package/dist/modules/workflow/git/branch-name.d.ts.map +1 -0
  324. package/dist/modules/workflow/git/branch-name.js +76 -0
  325. package/dist/modules/workflow/git/branch-name.js.map +1 -0
  326. package/dist/modules/workflow/git/clone-config.d.ts +61 -0
  327. package/dist/modules/workflow/git/clone-config.d.ts.map +1 -0
  328. package/dist/modules/workflow/git/clone-config.js +69 -0
  329. package/dist/modules/workflow/git/clone-config.js.map +1 -0
  330. package/dist/modules/workflow/git/git-version.d.ts +34 -0
  331. package/dist/modules/workflow/git/git-version.d.ts.map +1 -0
  332. package/dist/modules/workflow/git/git-version.js +63 -0
  333. package/dist/modules/workflow/git/git-version.js.map +1 -0
  334. package/dist/modules/workflow/git/git.service.d.ts +427 -0
  335. package/dist/modules/workflow/git/git.service.d.ts.map +1 -0
  336. package/dist/modules/workflow/git/git.service.js +1987 -0
  337. package/dist/modules/workflow/git/git.service.js.map +1 -0
  338. package/dist/modules/workflow/git/mutex.d.ts +11 -0
  339. package/dist/modules/workflow/git/mutex.d.ts.map +1 -0
  340. package/dist/modules/workflow/git/mutex.js +23 -0
  341. package/dist/modules/workflow/git/mutex.js.map +1 -0
  342. package/dist/modules/workflow/git/pull-request.service.d.ts +124 -0
  343. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -0
  344. package/dist/modules/workflow/git/pull-request.service.js +328 -0
  345. package/dist/modules/workflow/git/pull-request.service.js.map +1 -0
  346. package/dist/modules/workflow/locking-filesystem.d.ts +133 -0
  347. package/dist/modules/workflow/locking-filesystem.d.ts.map +1 -0
  348. package/dist/modules/workflow/locking-filesystem.js +400 -0
  349. package/dist/modules/workflow/locking-filesystem.js.map +1 -0
  350. package/dist/modules/workflow/pending-commits.service.d.ts +221 -0
  351. package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -0
  352. package/dist/modules/workflow/pending-commits.service.js +364 -0
  353. package/dist/modules/workflow/pending-commits.service.js.map +1 -0
  354. package/dist/modules/workflow/pending-commits.worker.d.ts +148 -0
  355. package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -0
  356. package/dist/modules/workflow/pending-commits.worker.js +245 -0
  357. package/dist/modules/workflow/pending-commits.worker.js.map +1 -0
  358. package/dist/modules/workflow/read-only-filesystem.d.ts +24 -0
  359. package/dist/modules/workflow/read-only-filesystem.d.ts.map +1 -0
  360. package/dist/modules/workflow/read-only-filesystem.js +39 -0
  361. package/dist/modules/workflow/read-only-filesystem.js.map +1 -0
  362. package/dist/modules/workflow/recovery-bot.d.ts +31 -0
  363. package/dist/modules/workflow/recovery-bot.d.ts.map +1 -0
  364. package/dist/modules/workflow/recovery-bot.js +64 -0
  365. package/dist/modules/workflow/recovery-bot.js.map +1 -0
  366. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +137 -0
  367. package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -0
  368. package/dist/modules/workflow/review-workflow/review-workflow.interface.js +2 -0
  369. package/dist/modules/workflow/review-workflow/review-workflow.interface.js.map +1 -0
  370. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +26 -0
  371. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -0
  372. package/dist/modules/workflow/review-workflow/review-workflow.service.js +636 -0
  373. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -0
  374. package/dist/modules/workflow/sanitize-error.d.ts +29 -0
  375. package/dist/modules/workflow/sanitize-error.d.ts.map +1 -0
  376. package/dist/modules/workflow/sanitize-error.js +60 -0
  377. package/dist/modules/workflow/sanitize-error.js.map +1 -0
  378. package/dist/modules/workflow/session-ontology.policy.d.ts +52 -0
  379. package/dist/modules/workflow/session-ontology.policy.d.ts.map +1 -0
  380. package/dist/modules/workflow/session-ontology.policy.js +62 -0
  381. package/dist/modules/workflow/session-ontology.policy.js.map +1 -0
  382. package/dist/modules/workflow/session-ontology.service.d.ts +100 -0
  383. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -0
  384. package/dist/modules/workflow/session-ontology.service.js +143 -0
  385. package/dist/modules/workflow/session-ontology.service.js.map +1 -0
  386. package/dist/modules/workflow/workflow-hooks.d.ts +87 -0
  387. package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -0
  388. package/dist/modules/workflow/workflow-hooks.js +35 -0
  389. package/dist/modules/workflow/workflow-hooks.js.map +1 -0
  390. package/dist/modules/workflow/workflow.errors.d.ts +171 -0
  391. package/dist/modules/workflow/workflow.errors.d.ts.map +1 -0
  392. package/dist/modules/workflow/workflow.errors.js +260 -0
  393. package/dist/modules/workflow/workflow.errors.js.map +1 -0
  394. package/dist/modules/workflow/workflow.routes.d.ts +26 -0
  395. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -0
  396. package/dist/modules/workflow/workflow.routes.js +804 -0
  397. package/dist/modules/workflow/workflow.routes.js.map +1 -0
  398. package/dist/modules/workflow/workflow.service.d.ts +385 -0
  399. package/dist/modules/workflow/workflow.service.d.ts.map +1 -0
  400. package/dist/modules/workflow/workflow.service.js +1155 -0
  401. package/dist/modules/workflow/workflow.service.js.map +1 -0
  402. package/dist/modules/workspace/bevel-ignore.d.ts +25 -0
  403. package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -0
  404. package/dist/modules/workspace/bevel-ignore.js +57 -0
  405. package/dist/modules/workspace/bevel-ignore.js.map +1 -0
  406. package/dist/modules/workspace/kb-seed.interface.d.ts +36 -0
  407. package/dist/modules/workspace/kb-seed.interface.d.ts.map +1 -0
  408. package/dist/modules/workspace/kb-seed.interface.js +2 -0
  409. package/dist/modules/workspace/kb-seed.interface.js.map +1 -0
  410. package/dist/modules/workspace/kb-seed.service.d.ts +70 -0
  411. package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -0
  412. package/dist/modules/workspace/kb-seed.service.js +325 -0
  413. package/dist/modules/workspace/kb-seed.service.js.map +1 -0
  414. package/dist/modules/workspace/routine-write-policy.d.ts +49 -0
  415. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -0
  416. package/dist/modules/workspace/routine-write-policy.js +46 -0
  417. package/dist/modules/workspace/routine-write-policy.js.map +1 -0
  418. package/dist/modules/workspace/session-ontology.gate.d.ts +112 -0
  419. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -0
  420. package/dist/modules/workspace/session-ontology.gate.js +161 -0
  421. package/dist/modules/workspace/session-ontology.gate.js.map +1 -0
  422. package/dist/modules/workspace/session-sink.d.ts +25 -0
  423. package/dist/modules/workspace/session-sink.d.ts.map +1 -0
  424. package/dist/modules/workspace/session-sink.js +8 -0
  425. package/dist/modules/workspace/session-sink.js.map +1 -0
  426. package/dist/modules/workspace/spill-store.d.ts +24 -0
  427. package/dist/modules/workspace/spill-store.d.ts.map +1 -0
  428. package/dist/modules/workspace/spill-store.js +87 -0
  429. package/dist/modules/workspace/spill-store.js.map +1 -0
  430. package/dist/modules/workspace/workspace.routes.d.ts +25 -0
  431. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -0
  432. package/dist/modules/workspace/workspace.routes.js +1046 -0
  433. package/dist/modules/workspace/workspace.routes.js.map +1 -0
  434. package/dist/modules/workspace/workspace.service.d.ts +339 -0
  435. package/dist/modules/workspace/workspace.service.d.ts.map +1 -0
  436. package/dist/modules/workspace/workspace.service.js +1221 -0
  437. package/dist/modules/workspace/workspace.service.js.map +1 -0
  438. package/dist/modules/workspace/workspace.tools.d.ts +19 -0
  439. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -0
  440. package/dist/modules/workspace/workspace.tools.js +767 -0
  441. package/dist/modules/workspace/workspace.tools.js.map +1 -0
  442. package/dist/shared/frontmatter-id.d.ts +35 -0
  443. package/dist/shared/frontmatter-id.d.ts.map +1 -0
  444. package/dist/shared/frontmatter-id.js +68 -0
  445. package/dist/shared/frontmatter-id.js.map +1 -0
  446. package/dist/shared/fs-walk.d.ts +9 -0
  447. package/dist/shared/fs-walk.d.ts.map +1 -0
  448. package/dist/shared/fs-walk.js +34 -0
  449. package/dist/shared/fs-walk.js.map +1 -0
  450. package/dist/shared/hash-email.d.ts +11 -0
  451. package/dist/shared/hash-email.d.ts.map +1 -0
  452. package/dist/shared/hash-email.js +14 -0
  453. package/dist/shared/hash-email.js.map +1 -0
  454. package/dist/shared/kb-layout.d.ts +38 -0
  455. package/dist/shared/kb-layout.d.ts.map +1 -0
  456. package/dist/shared/kb-layout.js +102 -0
  457. package/dist/shared/kb-layout.js.map +1 -0
  458. package/dist/shared/kb-layout.test.d.ts +2 -0
  459. package/dist/shared/kb-layout.test.d.ts.map +1 -0
  460. package/dist/shared/kb-layout.test.js +67 -0
  461. package/dist/shared/kb-layout.test.js.map +1 -0
  462. package/dist/shared/ssrf.d.ts +20 -0
  463. package/dist/shared/ssrf.d.ts.map +1 -0
  464. package/dist/shared/ssrf.js +89 -0
  465. package/dist/shared/ssrf.js.map +1 -0
  466. package/dist/shared/token-crypto.d.ts +16 -0
  467. package/dist/shared/token-crypto.d.ts.map +1 -0
  468. package/dist/shared/token-crypto.js +49 -0
  469. package/dist/shared/token-crypto.js.map +1 -0
  470. package/dist/shared/ttl-cache.d.ts +16 -0
  471. package/dist/shared/ttl-cache.d.ts.map +1 -0
  472. package/dist/shared/ttl-cache.js +27 -0
  473. package/dist/shared/ttl-cache.js.map +1 -0
  474. package/dist/shared/utcp-namespace.d.ts +46 -0
  475. package/dist/shared/utcp-namespace.d.ts.map +1 -0
  476. package/dist/shared/utcp-namespace.js +82 -0
  477. package/dist/shared/utcp-namespace.js.map +1 -0
  478. package/dist/version.d.ts +17 -0
  479. package/dist/version.d.ts.map +1 -0
  480. package/dist/version.js +19 -0
  481. package/dist/version.js.map +1 -0
  482. package/kb-template/.bevelignore +21 -0
  483. package/kb-template/Agents/.gitkeep +0 -0
  484. package/kb-template/Agents/README.md +38 -0
  485. package/kb-template/CLAUDE.md +419 -0
  486. package/kb-template/Data/.gitkeep +0 -0
  487. package/kb-template/Data/README.md +39 -0
  488. package/kb-template/KnowledgeBase/.gitkeep +0 -0
  489. package/kb-template/KnowledgeBase/README.md +37 -0
  490. package/kb-template/Pipelines/.gitkeep +0 -0
  491. package/kb-template/Pipelines/README.md +38 -0
  492. package/kb-template/Skills/.gitkeep +0 -0
  493. package/kb-template/Skills/README.md +32 -0
  494. package/kb-template/Tools/.gitkeep +0 -0
  495. package/kb-template/Tools/README.md +42 -0
  496. package/kb-template/access.md +36 -0
  497. package/migrations/0000_core_init.sql +243 -0
  498. package/migrations/meta/0000_snapshot.json +1480 -0
  499. package/migrations/meta/_journal.json +13 -0
  500. package/package.json +80 -0
  501. package/src/assets.ts +25 -0
  502. package/src/core/core-ports.ts +91 -0
  503. package/src/core/create-core-server.ts +338 -0
  504. package/src/core/create-core-services.ts +501 -0
  505. package/src/core-config.ts +257 -0
  506. package/src/index.ts +69 -0
  507. package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -0
  508. package/src/modules/access/__tests__/access-control.read-batch.test.ts +132 -0
  509. package/src/modules/access/__tests__/access-control.service.test.ts +1011 -0
  510. package/src/modules/access/__tests__/access-mutation.service.test.ts +439 -0
  511. package/src/modules/access/__tests__/access-splice.test.ts +247 -0
  512. package/src/modules/access/__tests__/access.routes.revoke.test.ts +407 -0
  513. package/src/modules/access/__tests__/creator-access.test.ts +217 -0
  514. package/src/modules/access/__tests__/grant-sources.test.ts +204 -0
  515. package/src/modules/access/__tests__/kb-read-filter.test.ts +70 -0
  516. package/src/modules/access/__tests__/roles-admin.service.test.ts +469 -0
  517. package/src/modules/access/__tests__/roles-edit.test.ts +138 -0
  518. package/src/modules/access/__tests__/roles-yaml-guard.test.ts +70 -0
  519. package/src/modules/access/__tests__/roles.routes.test.ts +234 -0
  520. package/src/modules/access/access-control.interface.ts +383 -0
  521. package/src/modules/access/access-control.service.ts +1794 -0
  522. package/src/modules/access/access-errors.ts +60 -0
  523. package/src/modules/access/access-mutation.service.ts +369 -0
  524. package/src/modules/access/access-splice.ts +413 -0
  525. package/src/modules/access/access.routes.ts +894 -0
  526. package/src/modules/access/creator-access.ts +270 -0
  527. package/src/modules/access/kb-read-filter.ts +76 -0
  528. package/src/modules/access/roles-admin.service.ts +741 -0
  529. package/src/modules/access/roles-edit.ts +248 -0
  530. package/src/modules/access/roles-yaml-guard.ts +89 -0
  531. package/src/modules/admin/admin-access.routes.ts +29 -0
  532. package/src/modules/admin/admin-access.service.ts +38 -0
  533. package/src/modules/admin/admin.interface.ts +10 -0
  534. package/src/modules/auth/account-erasure.service.ts +183 -0
  535. package/src/modules/auth/auth.middleware.ts +78 -0
  536. package/src/modules/auth/auth.routes.ts +97 -0
  537. package/src/modules/auth/auth.service.ts +199 -0
  538. package/src/modules/code-mode/code-mode-names.ts +38 -0
  539. package/src/modules/code-mode/code-mode.tool.ts +117 -0
  540. package/src/modules/code-mode/index.ts +6 -0
  541. package/src/modules/database/connection.ts +15 -0
  542. package/src/modules/database/core-schema.ts +431 -0
  543. package/src/modules/database/migrate.ts +43 -0
  544. package/src/modules/database/schema.ts +11 -0
  545. package/src/modules/diff/__tests__/diff.routes.read-gate.test.ts +87 -0
  546. package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -0
  547. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +184 -0
  548. package/src/modules/diff/__tests__/diff.service.test.ts +202 -0
  549. package/src/modules/diff/diff-paths.ts +9 -0
  550. package/src/modules/diff/diff.config.ts +12 -0
  551. package/src/modules/diff/diff.interface.ts +78 -0
  552. package/src/modules/diff/diff.routes.ts +277 -0
  553. package/src/modules/diff/diff.service.ts +508 -0
  554. package/src/modules/diff/line-diff.ts +79 -0
  555. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +287 -0
  556. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +249 -0
  557. package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +253 -0
  558. package/src/modules/mcp/__tests__/mcp-session-store.test.ts +151 -0
  559. package/src/modules/mcp/__tests__/mcp.e2e.test.ts +150 -0
  560. package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +92 -0
  561. package/src/modules/mcp/__tests__/mcp.service.test.ts +414 -0
  562. package/src/modules/mcp/mcp-auth.middleware.ts +126 -0
  563. package/src/modules/mcp/mcp-session-store.ts +162 -0
  564. package/src/modules/mcp/mcp.routes.ts +263 -0
  565. package/src/modules/mcp/mcp.service.ts +973 -0
  566. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +401 -0
  567. package/src/modules/mcp/oauth/oauth-consent.routes.ts +60 -0
  568. package/src/modules/mcp/oauth/oauth-state.ts +61 -0
  569. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +277 -0
  570. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +206 -0
  571. package/src/modules/secrets-vault/__tests__/mcp-oauth-discovery.service.test.ts +178 -0
  572. package/src/modules/secrets-vault/__tests__/scopes-covered.test.ts +101 -0
  573. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +54 -0
  574. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -0
  575. package/src/modules/secrets-vault/db-secrets-vault.service.ts +792 -0
  576. package/src/modules/secrets-vault/index.ts +25 -0
  577. package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +289 -0
  578. package/src/modules/secrets-vault/secrets-variable-loader.ts +104 -0
  579. package/src/modules/secrets-vault/secrets-vault.contract.ts +270 -0
  580. package/src/modules/secrets-vault/secrets-vault.routes.ts +526 -0
  581. package/src/modules/skills/__tests__/skills.service.test.ts +176 -0
  582. package/src/modules/skills/index.ts +10 -0
  583. package/src/modules/skills/skills.contract.ts +54 -0
  584. package/src/modules/skills/skills.routes.ts +38 -0
  585. package/src/modules/skills/skills.service.ts +245 -0
  586. package/src/modules/skills/skills.tools.ts +121 -0
  587. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +358 -0
  588. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +79 -0
  589. package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +88 -0
  590. package/src/modules/tool-auth/external-api-key.errors.ts +25 -0
  591. package/src/modules/tool-auth/external-api-key.interface.ts +99 -0
  592. package/src/modules/tool-auth/external-api-key.service.ts +209 -0
  593. package/src/modules/tool-auth/internal-token.service.ts +124 -0
  594. package/src/modules/tool-auth/llm-usage-meter.ts +19 -0
  595. package/src/modules/tool-auth/tool-auth.middleware.ts +220 -0
  596. package/src/modules/tool-helpers/__tests__/phase0-loopback-spike.test.ts +129 -0
  597. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +95 -0
  598. package/src/modules/tool-helpers/__tests__/tools-surface.test.ts +146 -0
  599. package/src/modules/tool-helpers/__tests__/validate-token.test.ts +75 -0
  600. package/src/modules/tool-helpers/index.ts +18 -0
  601. package/src/modules/tool-helpers/tool-context.ts +98 -0
  602. package/src/modules/tool-helpers/tool-def.ts +73 -0
  603. package/src/modules/tool-helpers/tool-handler.ts +91 -0
  604. package/src/modules/tool-helpers/tool.contract.ts +85 -0
  605. package/src/modules/tool-helpers/validate-token.ts +50 -0
  606. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +200 -0
  607. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +499 -0
  608. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -0
  609. package/src/modules/tool-manuals/index.ts +12 -0
  610. package/src/modules/tool-manuals/tool-manuals.contract.ts +183 -0
  611. package/src/modules/tool-manuals/tool-manuals.routes.ts +110 -0
  612. package/src/modules/tool-manuals/tool-manuals.service.ts +781 -0
  613. package/src/modules/tool-manuals/tool-manuals.tools.ts +202 -0
  614. package/src/modules/tool-registry/__tests__/tool-registry.test.ts +64 -0
  615. package/src/modules/tool-registry/manual.routes.ts +87 -0
  616. package/src/modules/tool-registry/tool-registry.ts +55 -0
  617. package/src/modules/tool-registry/tool.contract.ts +52 -0
  618. package/src/modules/workflow/__tests__/event-bus.test.ts +322 -0
  619. package/src/modules/workflow/__tests__/file-change-notifier.test.ts +51 -0
  620. package/src/modules/workflow/__tests__/format-affected-owners.test.ts +67 -0
  621. package/src/modules/workflow/__tests__/locking-filesystem.test.ts +445 -0
  622. package/src/modules/workflow/__tests__/pending-commits.canonical.test.ts +36 -0
  623. package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +243 -0
  624. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +308 -0
  625. package/src/modules/workflow/__tests__/sanitize-error.test.ts +67 -0
  626. package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +62 -0
  627. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +200 -0
  628. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +232 -0
  629. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +380 -0
  630. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +340 -0
  631. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +192 -0
  632. package/src/modules/workflow/agent-tools/workflow.tools.ts +502 -0
  633. package/src/modules/workflow/event-bus.ts +262 -0
  634. package/src/modules/workflow/events.routes.ts +266 -0
  635. package/src/modules/workflow/file-change-notifier.ts +54 -0
  636. package/src/modules/workflow/file-lock.service.ts +269 -0
  637. package/src/modules/workflow/git/__tests__/branch-name.test.ts +86 -0
  638. package/src/modules/workflow/git/__tests__/branchAuthor.test.ts +80 -0
  639. package/src/modules/workflow/git/__tests__/clone-config.test.ts +15 -0
  640. package/src/modules/workflow/git/__tests__/git-test-helpers.ts +47 -0
  641. package/src/modules/workflow/git/__tests__/git-version.failure.test.ts +24 -0
  642. package/src/modules/workflow/git/__tests__/git-version.test.ts +49 -0
  643. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +389 -0
  644. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
  645. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +105 -0
  646. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +211 -0
  647. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +154 -0
  648. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +320 -0
  649. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +202 -0
  650. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +453 -0
  651. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +198 -0
  652. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +125 -0
  653. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +219 -0
  654. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +145 -0
  655. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +240 -0
  656. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +272 -0
  657. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +232 -0
  658. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +101 -0
  659. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +127 -0
  660. package/src/modules/workflow/git/__tests__/mutex.test.ts +78 -0
  661. package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +225 -0
  662. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +101 -0
  663. package/src/modules/workflow/git/branch-name.ts +70 -0
  664. package/src/modules/workflow/git/clone-config.ts +71 -0
  665. package/src/modules/workflow/git/git-version.ts +70 -0
  666. package/src/modules/workflow/git/git.service.ts +2248 -0
  667. package/src/modules/workflow/git/mutex.ts +22 -0
  668. package/src/modules/workflow/git/pull-request.service.ts +447 -0
  669. package/src/modules/workflow/locking-filesystem.ts +489 -0
  670. package/src/modules/workflow/pending-commits.service.ts +444 -0
  671. package/src/modules/workflow/pending-commits.worker.ts +361 -0
  672. package/src/modules/workflow/read-only-filesystem.ts +54 -0
  673. package/src/modules/workflow/recovery-bot.ts +73 -0
  674. package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +560 -0
  675. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +176 -0
  676. package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +258 -0
  677. package/src/modules/workflow/review-workflow/review-workflow.interface.ts +199 -0
  678. package/src/modules/workflow/review-workflow/review-workflow.service.ts +806 -0
  679. package/src/modules/workflow/sanitize-error.ts +62 -0
  680. package/src/modules/workflow/session-ontology.policy.ts +70 -0
  681. package/src/modules/workflow/session-ontology.service.ts +178 -0
  682. package/src/modules/workflow/workflow-hooks.ts +101 -0
  683. package/src/modules/workflow/workflow.errors.ts +285 -0
  684. package/src/modules/workflow/workflow.routes.ts +858 -0
  685. package/src/modules/workflow/workflow.service.ts +1555 -0
  686. package/src/modules/workspace/__tests__/kb-seed.service.test.ts +213 -0
  687. package/src/modules/workspace/__tests__/routine-write-policy.test.ts +68 -0
  688. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -0
  689. package/src/modules/workspace/__tests__/spill-store.test.ts +43 -0
  690. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +233 -0
  691. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +176 -0
  692. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +303 -0
  693. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +276 -0
  694. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +120 -0
  695. package/src/modules/workspace/__tests__/workspace.service.test.ts +565 -0
  696. package/src/modules/workspace/__tests__/workspace.tools.test.ts +504 -0
  697. package/src/modules/workspace/bevel-ignore.ts +62 -0
  698. package/src/modules/workspace/kb-seed.interface.ts +36 -0
  699. package/src/modules/workspace/kb-seed.service.ts +361 -0
  700. package/src/modules/workspace/routine-write-policy.ts +79 -0
  701. package/src/modules/workspace/session-ontology.gate.ts +189 -0
  702. package/src/modules/workspace/session-sink.ts +25 -0
  703. package/src/modules/workspace/spill-store.ts +87 -0
  704. package/src/modules/workspace/workspace.routes.ts +1114 -0
  705. package/src/modules/workspace/workspace.service.ts +1343 -0
  706. package/src/modules/workspace/workspace.tools.ts +885 -0
  707. package/src/shared/__tests__/frontmatter-id.test.ts +88 -0
  708. package/src/shared/__tests__/fs-walk.test.ts +42 -0
  709. package/src/shared/__tests__/ssrf.test.ts +53 -0
  710. package/src/shared/__tests__/utcp-namespace.test.ts +19 -0
  711. package/src/shared/frontmatter-id.ts +72 -0
  712. package/src/shared/fs-walk.ts +30 -0
  713. package/src/shared/hash-email.ts +14 -0
  714. package/src/shared/kb-layout.test.ts +88 -0
  715. package/src/shared/kb-layout.ts +101 -0
  716. package/src/shared/ssrf.ts +78 -0
  717. package/src/shared/token-crypto.ts +54 -0
  718. package/src/shared/ttl-cache.ts +27 -0
  719. package/src/shared/utcp-namespace.ts +88 -0
  720. package/src/version.ts +19 -0
@@ -0,0 +1,781 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import { parse as parseYaml } from 'yaml';
4
+ import '@utcp/http'; // side effect: register the 'http' call-template type (http + inline sub-manuals)
5
+ import '@utcp/mcp'; // side effect: register the 'mcp' call-template type (mcp `.tool` sources)
6
+ import {
7
+ UtcpManualSerializer,
8
+ CallTemplateSerializer,
9
+ DefaultVariableSubstitutor,
10
+ type CallTemplate,
11
+ } from '@utcp/sdk';
12
+ import { DEFAULT_BRANCH, TOOLS_DIR } from '@bevel-software/platform-shared';
13
+ import type { WorkspaceService } from '../workspace/workspace.service.js';
14
+ import { workspaceIdForBranch } from '../workspace/workspace.service.js';
15
+ import type { IAccessControl } from '../access/access-control.interface.js';
16
+ import { assertSafeFetchUrl } from '../../shared/ssrf.js';
17
+ import { extractFrontmatter, resolveDeclaredId, isValidId, dedupeById } from '../../shared/frontmatter-id.js';
18
+ import { walkFiles } from '../../shared/fs-walk.js';
19
+ import { TtlCache } from '../../shared/ttl-cache.js';
20
+ import {
21
+ utcpNamespacePrefix,
22
+ utcpNamespacedKey,
23
+ MCP_OAUTH_VAR,
24
+ INTERNAL_MANUAL_NAME,
25
+ } from '../../shared/utcp-namespace.js';
26
+ import {
27
+ EXTERNAL_KB_MANUAL_NAME,
28
+ type IToolManualService,
29
+ type ToolManualDescriptor,
30
+ type ToolManualSummary,
31
+ type UtcpManualDict,
32
+ type ToolManualPreview,
33
+ type ToolManualType,
34
+ type ToolVariable,
35
+ type ToolVariableScope,
36
+ type ToolVariableOAuth,
37
+ } from './tool-manuals.contract.js';
38
+
39
+ const CACHE_TTL_MS = 60_000;
40
+
41
+ /**
42
+ * UTCP namespaces a user `.tool` may NOT claim: they belong to built-in manuals
43
+ * whose loopback creds are pre-seeded on the agent's code-mode client (the
44
+ * internal `Bevel` manual carries the `source:'internal'` token + connector
45
+ * creds; the external KB manual carries the caller's bearer). A `.tool`
46
+ * reproducing one of these namespaces would resolve those seeded vars and could
47
+ * exfiltrate them, so the scanner refuses it. Compared case-insensitively —
48
+ * defensive, though the live collision is the mixed-case `Bevel` reachable via
49
+ * the `name` fallback (an explicit `id` must already be lowercase snake_case).
50
+ */
51
+ const RESERVED_TOOL_NAMESPACES = [INTERNAL_MANUAL_NAME, EXTERNAL_KB_MANUAL_NAME].map((n) => n.toLowerCase());
52
+
53
+ /**
54
+ * Variable names the platform seeds for its own (Bevel-hosted) manuals:
55
+ * `<ns>_API_URL` points a manual at the backend and `<ns>_CONNECTION_KEY`
56
+ * carries the platform bearer. No user `.tool` may REFERENCE them
57
+ * (`${API_URL}` / `$API_URL`), in any `.tool` type: the only seeded user
58
+ * namespace is an inline `.tool`'s (its discovery template is platform-served),
59
+ * and a reference inside author-written content would resolve platform creds
60
+ * into a request the author shaped. Refusing every `.tool` at the producing
61
+ * boundary makes "user tools never carry platform credentials" structural
62
+ * rather than dependent on which namespaces happen to be seeded.
63
+ */
64
+ const RESERVED_VARIABLE_NAMES: readonly string[] = ['API_URL', 'CONNECTION_KEY'];
65
+
66
+ /** The SDK substitutor's reference grammar, exactly: `${VAR}` or `$VAR`. */
67
+ const VARIABLE_REFERENCE_RE = /\$\{([a-zA-Z0-9_]+)\}|\$([a-zA-Z0-9_]+)/g;
68
+
69
+ /**
70
+ * A REFERENCE is reserved by SUFFIX, not by exact name: the substitutor looks a
71
+ * variable up under the manual's UTCP namespace first, so `${<ns>_CONNECTION_KEY}`
72
+ * resolves the very same seeded value the bare `${CONNECTION_KEY}` does. Matching
73
+ * the bare name only would let a `.tool` reach the platform bearer just by
74
+ * spelling the namespace out. Suffix matching also refuses harmless-looking
75
+ * near-misses (`MY_API_URL`) — deliberately fail-closed: a `.tool` author who
76
+ * wants their own base URL has the whole namespace minus two suffixes.
77
+ */
78
+ function isReservedVariableRef(varName: string): boolean {
79
+ return RESERVED_VARIABLE_NAMES.some((reserved) => varName.endsWith(reserved));
80
+ }
81
+
82
+ /** Throw if any string in the `.tool` document references a reserved variable. */
83
+ function assertNoReservedVariableRefs(doc: unknown, name: string): void {
84
+ const text = JSON.stringify(doc) ?? '';
85
+ for (const match of text.matchAll(VARIABLE_REFERENCE_RE)) {
86
+ const varName = match[1] ?? match[2];
87
+ if (isReservedVariableRef(varName)) {
88
+ throw new Error(
89
+ `\`.tool\` "${name}" references the reserved variable "${match[0]}" — ` +
90
+ 'API_URL and CONNECTION_KEY (bare or namespaced, e.g. `<namespace>_CONNECTION_KEY`) ' +
91
+ 'are seeded by the platform for its own manuals and may not appear anywhere in a `.tool`.',
92
+ );
93
+ }
94
+ }
95
+ }
96
+
97
+ const manualSerializer = new UtcpManualSerializer();
98
+ const callTemplateSerializer = new CallTemplateSerializer();
99
+ // `findRequiredVariables` is a pure walk — one shared instance is fine.
100
+ const variableSubstitutor = new DefaultVariableSubstitutor();
101
+
102
+ /**
103
+ * What the catalog needs from MCP OAuth auto-discovery (structurally satisfied
104
+ * by `McpOAuthDiscoveryService` in modules/secrets-vault). A local port keeps
105
+ * this module free of a secrets-vault import — the same decoupling discipline
106
+ * the vault applies in the other direction with `VariableScopeResolver`.
107
+ */
108
+ export interface McpAuthDiscoveryPort {
109
+ statusFor(
110
+ manualName: string,
111
+ mcpUrl: string,
112
+ ): Promise<
113
+ | { status: 'open' }
114
+ | {
115
+ status: 'oauth';
116
+ provider: { authorizationUrl: string; tokenUrl: string; clientId: string; scopes?: string[] };
117
+ }
118
+ | { status: 'unsupported'; reason: string }
119
+ >;
120
+ }
121
+
122
+ /**
123
+ * Reads `*.tool` manuals from the DEFAULT-branch workspace (never the caller's
124
+ * branch), so the catalog is one global, released set — same discipline as
125
+ * Skills. Results are cached briefly; drop via `invalidate()` after a merge to
126
+ * default. Any read failure degrades to an empty catalog: the MCP/UTCP endpoint
127
+ * must never break because a `.tool` can't be read.
128
+ */
129
+ export class ToolManualService implements IToolManualService {
130
+ private readonly cache: TtlCache<ToolManualDescriptor[]>;
131
+ private mcpAuthDiscovery?: McpAuthDiscoveryPort;
132
+
133
+ constructor(
134
+ private readonly workspaceService: WorkspaceService,
135
+ private readonly accessControl: IAccessControl,
136
+ private readonly kbDirName: string,
137
+ now: () => number = Date.now,
138
+ ) {
139
+ this.cache = new TtlCache(CACHE_TTL_MS, now);
140
+ }
141
+
142
+ /**
143
+ * Wire the MCP OAuth auto-discovery (setter injection: the discovery service
144
+ * depends on the vault, which is constructed after this service — the same
145
+ * reason the vault takes `scopeOfVariable` as a closure). Optional: without
146
+ * it, bare `type: mcp` tools simply aren't decorated.
147
+ */
148
+ setMcpAuthDiscovery(discovery: McpAuthDiscoveryPort): void {
149
+ this.mcpAuthDiscovery = discovery;
150
+ }
151
+
152
+ invalidate(): void {
153
+ this.cache.invalidate();
154
+ }
155
+
156
+ async listAccessible(userEmail: string): Promise<ToolManualSummary[]> {
157
+ const manuals = await this.accessibleManuals(userEmail);
158
+ return manuals.map((m) => ({
159
+ slug: m.slug,
160
+ name: m.name,
161
+ path: m.path,
162
+ type: m.type,
163
+ variables: m.variables,
164
+ remote: m.remote,
165
+ setup: m.setup,
166
+ }));
167
+ }
168
+
169
+ async listLocalOnly(userEmail: string): Promise<{ name: string; path: string }[]> {
170
+ const manuals = await this.accessibleManuals(userEmail);
171
+ return manuals.filter((m) => m.remote === false).map((m) => ({ name: m.name, path: m.path }));
172
+ }
173
+
174
+ async userScopedKeysForManual(
175
+ manualName: string,
176
+ ): Promise<
177
+ { key: string; name: string; label: string | null; oauth: boolean; oauthScopes?: string[] }[]
178
+ > {
179
+ // The per-user (`user`-scoped) variables a manual declares, keyed the SAME
180
+ // way the vault stores them — the UTCP-namespaced key (underscores in the
181
+ // manual name doubled), so a readiness check reads the exact rows `resolve`
182
+ // would. `oauth` lets the pre-check treat a not-yet-authorized sign-in as
183
+ // still-missing.
184
+ const manual = (await this.scan()).find((m) => m.name === manualName);
185
+ return (manual?.variables ?? [])
186
+ .filter((v) => v.scope === 'user')
187
+ .map((v) => ({
188
+ key: utcpNamespacedKey(manualName, v.name),
189
+ name: v.name,
190
+ label: v.label ?? null,
191
+ oauth: v.oauth != null,
192
+ // The permissions the tool declares RIGHT NOW — the pre-check compares these
193
+ // against the caller's granted scopes to catch a token that predates a scope
194
+ // addition. Read live so a `.tool` edit self-heals on the next call.
195
+ oauthScopes: v.oauth?.scopes,
196
+ }));
197
+ }
198
+
199
+ async scopeOfVariable(effectiveKey: string): Promise<ToolVariableScope> {
200
+ // UTCP looks up `<namespace-with-doubled-underscores>_<VAR>`. Find the manual
201
+ // whose namespace prefixes this key; the declared var is the remainder. Match
202
+ // the LONGEST prefix so a manual `a` can't shadow `a_b` when both exist. (A
203
+ // plain first-underscore split would mis-parse a snake_case manual name.)
204
+ let best: { manual: ToolManualDescriptor; varName: string; len: number } | null = null;
205
+ for (const m of await this.scan()) {
206
+ const prefix = utcpNamespacePrefix(m.name);
207
+ if (effectiveKey.startsWith(prefix) && (!best || prefix.length > best.len)) {
208
+ best = { manual: m, varName: effectiveKey.slice(prefix.length), len: prefix.length };
209
+ }
210
+ }
211
+ if (!best) return 'admin';
212
+ const declared = best.manual.variables?.find((v) => v.name === best!.varName);
213
+ return declared?.scope ?? 'admin';
214
+ }
215
+
216
+ async toManualCallTemplates(userEmail: string, opts?: { remoteOnly?: boolean }): Promise<CallTemplate[]> {
217
+ const manuals = await this.accessibleManuals(userEmail);
218
+ const out: CallTemplate[] = [];
219
+ for (const m of manuals) {
220
+ // Remote consumers (the hosted MCP proxy) don't get local-only manuals —
221
+ // they'd fail server-side. Discovery of them happens via `list_local_tools`.
222
+ if (opts?.remoteOnly && m.remote === false) continue;
223
+ try {
224
+ out.push(callTemplateSerializer.validateDict(this.buildCallTemplateDict(m)));
225
+ } catch (err) {
226
+ // A `.tool` that produces an invalid call-template is dropped here — at
227
+ // the producing boundary — so the served list is always valid.
228
+ console.warn(
229
+ `[tool-manuals] skipping "${m.path}": ${err instanceof Error ? err.message : String(err)}`,
230
+ );
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+
236
+ async resolveInlineManual(userEmail: string, slug: string): Promise<UtcpManualDict | null> {
237
+ const found = (await this.scan()).find((m) => m.slug === slug);
238
+ if (!found || found.type !== 'inline') return null;
239
+ const wsId = workspaceIdForBranch(DEFAULT_BRANCH);
240
+ if (!(await this.accessControl.canRead(wsId, userEmail, found.path))) return null;
241
+ try {
242
+ return manualSerializer.validateDict({
243
+ utcp_version: '1.1.0',
244
+ manual_version: '1.0.0',
245
+ tools: found.tools ?? [],
246
+ }) as unknown as UtcpManualDict;
247
+ } catch {
248
+ return null;
249
+ }
250
+ }
251
+
252
+ async preview(content: string): Promise<ToolManualPreview> {
253
+ let descriptor: ToolManualDescriptor;
254
+ try {
255
+ descriptor = normalizeToolManual('draft', 'Tools/draft.tool', content);
256
+ } catch (err) {
257
+ return { ok: false, errors: [err instanceof Error ? err.message : String(err)] };
258
+ }
259
+ if (descriptor.type === 'inline') {
260
+ try {
261
+ const manual = manualSerializer.validateDict({
262
+ utcp_version: '1.1.0',
263
+ manual_version: '1.0.0',
264
+ tools: descriptor.tools ?? [],
265
+ }) as { tools?: { name?: unknown; description?: unknown }[] };
266
+ const tools = (manual.tools ?? []).map((t) => ({
267
+ name: String(t.name ?? ''),
268
+ description: typeof t.description === 'string' ? t.description : undefined,
269
+ }));
270
+ return { ok: true, tools };
271
+ } catch (err) {
272
+ return { ok: false, errors: [err instanceof Error ? err.message : String(err)] };
273
+ }
274
+ }
275
+ // http / mcp resolve their tools at runtime (a network round-trip we don't
276
+ // perform in preview), but still validate the call-template the same way
277
+ // `toManualCallTemplates` does, so a draft that discovery would reject is
278
+ // reported here instead of appearing valid.
279
+ try {
280
+ callTemplateSerializer.validateDict(this.buildCallTemplateDict(descriptor));
281
+ } catch (err) {
282
+ return { ok: false, errors: [err instanceof Error ? err.message : String(err)] };
283
+ }
284
+ return { ok: true, tools: [] };
285
+ }
286
+
287
+ // --- internal --------------------------------------------------------------
288
+
289
+ /** Build the raw UTCP manual call-template dict for one descriptor (validated by the caller). */
290
+ private buildCallTemplateDict(m: ToolManualDescriptor): Record<string, unknown> {
291
+ if (m.type === 'inline') {
292
+ return {
293
+ name: m.name,
294
+ call_template_type: 'http',
295
+ http_method: 'GET',
296
+ url: `\${API_URL}/api/tools/${m.slug}/manual`,
297
+ content_type: 'application/json',
298
+ headers: { Authorization: 'Bearer ${CONNECTION_KEY}' },
299
+ };
300
+ }
301
+ if (m.type === 'mcp') {
302
+ // Remote (HTTP/streamable) MCP server. Exact plugin field shape is
303
+ // finalized in Phase 4 (native `@utcp/mcp`); the proxy try/catches
304
+ // registration so an unsupported template never breaks the session.
305
+ return {
306
+ name: m.name,
307
+ call_template_type: 'mcp',
308
+ config: {
309
+ mcpServers: {
310
+ [m.name]: {
311
+ transport: 'http',
312
+ url: m.url,
313
+ ...(m.headers ? { headers: m.headers } : {}),
314
+ },
315
+ },
316
+ },
317
+ };
318
+ }
319
+ // http: a URL returning a UTCP manual.
320
+ return {
321
+ name: m.name,
322
+ call_template_type: 'http',
323
+ http_method: m.httpMethod ?? 'GET',
324
+ url: m.url,
325
+ content_type: 'application/json',
326
+ ...(m.headers ? { headers: m.headers } : {}),
327
+ };
328
+ }
329
+
330
+ private async accessibleManuals(userEmail: string): Promise<ToolManualDescriptor[]> {
331
+ const manuals = await this.scan();
332
+ if (manuals.length === 0) return [];
333
+ const wsId = workspaceIdForBranch(DEFAULT_BRANCH);
334
+ const allowed = await this.accessControl.canReadBatch(
335
+ wsId,
336
+ userEmail,
337
+ manuals.map((m) => m.path),
338
+ );
339
+ // Fail closed: keep a manual only on an explicit `true` verdict (a missing
340
+ // entry is treated as denied, matching the KB's default-deny read model).
341
+ return manuals.filter((m) => allowed.get(m.path) === true);
342
+ }
343
+
344
+ private async scan(): Promise<ToolManualDescriptor[]> {
345
+ const cached = this.cache.get();
346
+ if (cached) return cached;
347
+ const manuals = await this.scanDisk();
348
+ await this.decorateMcpOAuth(manuals);
349
+ // AFTER the oauth decoration, so an injected `${MCP_OAUTH}` header ref is
350
+ // already declared and isn't re-surfaced as a bare admin key.
351
+ for (const m of manuals) this.surfaceReferencedVariables(m);
352
+ this.cache.set(manuals);
353
+ return manuals;
354
+ }
355
+
356
+ /**
357
+ * Auto-surface every `${VAR}` a manual actually references, for ANY tool
358
+ * type — using UTCP's own required-variables walk, so the surfaced list
359
+ * can't drift from what substitution will demand at registration/call time.
360
+ * Referenced-but-undeclared vars become `scope: admin` entries (matching
361
+ * `scopeOfVariable`'s default for undeclared keys), which puts them in the
362
+ * secrets UI without the author having to write a `variables:` block. The
363
+ * block stays the way to add metadata: `scope: user`, labels, oauth.
364
+ */
365
+ private surfaceReferencedVariables(m: ToolManualDescriptor): void {
366
+ let refs: string[];
367
+ try {
368
+ refs = variableSubstitutor.findRequiredVariables(this.buildCallTemplateDict(m), m.name);
369
+ // An inline manual's credential refs live in its embedded tools'
370
+ // templates, not the (Bevel-hosted) discovery template.
371
+ if (m.type === 'inline' && m.tools) {
372
+ refs.push(...variableSubstitutor.findRequiredVariables(m.tools, m.name));
373
+ }
374
+ } catch {
375
+ return; // a malformed template is reported elsewhere; never break the scan
376
+ }
377
+ const prefix = utcpNamespacePrefix(m.name);
378
+ const declared = new Set((m.variables ?? []).map((v) => v.name));
379
+ for (const ref of new Set(refs)) {
380
+ if (!ref.startsWith(prefix)) continue;
381
+ const name = ref.slice(prefix.length);
382
+ // Reserved names are seeded by the proxy per session — not credentials.
383
+ if (name === 'API_URL' || name === 'CONNECTION_KEY' || declared.has(name)) continue;
384
+ m.variables = [...(m.variables ?? []), { name, scope: 'admin' }];
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Zero-config OAuth for bare `type: mcp` manuals: when discovery finds the
390
+ * remote server demands OAuth (and the file doesn't configure auth itself),
391
+ * decorate the descriptor with a synthetic user-scoped `MCP_OAUTH` variable
392
+ * and an `Authorization: Bearer ${MCP_OAUTH}` header. Decorating HERE — the
393
+ * one place descriptors are built — means every consumer inherits it:
394
+ * /connect lists the sign-in, `scopeOfVariable` resolves it per-user,
395
+ * `userScopedKeysForManual` gates unauthorized callers, and the call
396
+ * template carries the header for the variable loader to fill.
397
+ */
398
+ private async decorateMcpOAuth(manuals: ToolManualDescriptor[]): Promise<void> {
399
+ const discovery = this.mcpAuthDiscovery;
400
+ if (!discovery) return;
401
+ const eligible = manuals.filter((m) => {
402
+ if (m.type !== 'mcp' || !m.url) return false;
403
+ // Local-only servers aren't probeable from here; templated URLs aren't
404
+ // resolvable without a caller. Both keep their file-declared behavior.
405
+ if (m.remote === false || m.url.includes('${')) return false;
406
+ // The file configures auth itself — explicit wins over discovery.
407
+ const hasAuthHeader = Object.keys(m.headers ?? {}).some((h) => h.toLowerCase() === 'authorization');
408
+ const hasOAuthVar = (m.variables ?? []).some((v) => v.oauth != null);
409
+ return !hasAuthHeader && !hasOAuthVar;
410
+ });
411
+ // Probe every eligible server CONCURRENTLY — a cold scan with several bare
412
+ // mcp tools shouldn't pay one network round-trip per tool in series. The
413
+ // mutation still happens per-manual after its own probe settles.
414
+ await Promise.all(
415
+ eligible.map(async (m) => {
416
+ try {
417
+ const found = await discovery.statusFor(m.name, m.url!);
418
+ // Record the setup requirement so the secrets UI can tell an admin
419
+ // whether anything needs configuring — especially the `unsupported`
420
+ // case, which otherwise only surfaced in server logs.
421
+ if (found.status === 'open') {
422
+ m.setup = { kind: 'open' };
423
+ return;
424
+ }
425
+ if (found.status === 'unsupported') {
426
+ m.setup = { kind: 'oauth-manual', reason: found.reason };
427
+ return;
428
+ }
429
+ m.setup = { kind: 'oauth-auto' };
430
+ m.headers = { ...(m.headers ?? {}), Authorization: `Bearer \${${MCP_OAUTH_VAR}}` };
431
+ m.variables = [
432
+ ...(m.variables ?? []),
433
+ {
434
+ name: MCP_OAUTH_VAR,
435
+ scope: 'user',
436
+ label: `${m.name} sign-in`,
437
+ // NO `scopes` here, deliberately. A variable's declared scopes are
438
+ // REQUIRED back from the token (needsReauth + the call-time gate),
439
+ // which is right for file-authored scopes but not for discovery's
440
+ // machine-guessed ones (the PRM's `scopes_supported`): providers
441
+ // don't reliably echo them (e.g. Granola's AS grants OIDC scopes
442
+ // instead), which would permanently flag-and-block an authorized
443
+ // sign-in. The shared provider row still carries them, so the
444
+ // authorize request itself is unchanged; an under-scoped token
445
+ // simply 401s at call time and re-enters auth there.
446
+ oauth: {
447
+ authorizationUrl: found.provider.authorizationUrl,
448
+ tokenUrl: found.provider.tokenUrl,
449
+ clientId: found.provider.clientId,
450
+ },
451
+ },
452
+ ];
453
+ } catch (err) {
454
+ // Discovery must never break the catalog — the tool just stays bare.
455
+ console.warn(
456
+ `[tool-manuals] mcp auth discovery failed for "${m.path}": ${err instanceof Error ? err.message : String(err)}`,
457
+ );
458
+ }
459
+ }),
460
+ );
461
+ }
462
+
463
+ private async scanDisk(): Promise<ToolManualDescriptor[]> {
464
+ let wsId: string;
465
+ try {
466
+ wsId = (await this.workspaceService.getOrCreateForBranch(DEFAULT_BRANCH)).id;
467
+ } catch {
468
+ return [];
469
+ }
470
+ const root = path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName, TOOLS_DIR);
471
+
472
+ const files = (await walkFiles(root, (n) => n.toLowerCase().endsWith('.tool'))).map((rel) => ({
473
+ abs: path.join(root, rel),
474
+ rel: `${TOOLS_DIR}/${rel}`,
475
+ }));
476
+
477
+ const parsed: ToolManualDescriptor[] = [];
478
+ for (const f of files) {
479
+ let content: string;
480
+ try {
481
+ content = await fs.readFile(f.abs, 'utf-8');
482
+ } catch {
483
+ continue;
484
+ }
485
+ let descriptor: ToolManualDescriptor;
486
+ try {
487
+ descriptor = normalizeToolManual(baseName(f.rel), f.rel, content);
488
+ } catch {
489
+ // A malformed `.tool` is skipped rather than breaking the catalog.
490
+ continue;
491
+ }
492
+ // The route slug IS the id (unique after dedup below, snake_case → URL-safe),
493
+ // so the URL a user sees matches the tool's declared identity.
494
+ descriptor.slug = descriptor.name;
495
+ parsed.push(descriptor);
496
+ }
497
+ // The manual name (= its id) is the UTCP variable namespace secrets bind to, so
498
+ // it must be unique. A collision is REFUSED — not auto-suffixed, which would
499
+ // silently rebind a configured secret to a different file. The winner is
500
+ // deterministic (files scanned in sorted path order); the shared `dedupeById`
501
+ // is the one dedup rule across tools and skills.
502
+ return dedupeById(parsed, (m) => m.name, (m, id) =>
503
+ console.warn(
504
+ `[tool-manuals] skipping "${m.path}": manual id "${id}" is already used by another ` +
505
+ '`.tool` — give it a unique `id` (the id is the secret-variable namespace and must be unique).',
506
+ ),
507
+ );
508
+ }
509
+ }
510
+
511
+ // --- helpers ------------------------------------------------------------------
512
+
513
+ function baseName(rel: string): string {
514
+ const base = rel.slice(rel.lastIndexOf('/') + 1);
515
+ return base.replace(/\.tool$/i, '');
516
+ }
517
+
518
+ /**
519
+ * Deterministically derive a `.tool`'s UTCP manual name — the variable namespace
520
+ * secrets bind to via `<manual>_<VAR>`. ALPHANUMERIC ONLY (no underscores) so the
521
+ * prefix splits cleanly on a single underscore, and a pure function of the file's
522
+ * own declared name: it never depends on what other `.tool`s exist, so a
523
+ * configured secret's namespace can't drift when the catalog changes. Collisions
524
+ * are refused by the scanner (see `scanDisk`), not silently suffixed. A name that
525
+ * strips to empty (or starts with a digit) is prefixed `tool`; because the KB
526
+ * manual name (`KNOWLEDGE_BASE`) contains an underscore, a user name can never
527
+ * collide with it after stripping.
528
+ */
529
+ function manualName(raw: string): string {
530
+ const name = raw.replace(/[^a-zA-Z0-9]/g, '');
531
+ if (!name || /^[0-9]/.test(name)) return `tool${name}`;
532
+ return name;
533
+ }
534
+
535
+ /**
536
+ * Parse + normalize a `.tool` file into a descriptor. THE TOOL IS THE
537
+ * FRONTMATTER: a fenced file's single `---` YAML block carries everything —
538
+ * `id`/`name`, access verbs (`read`/`write`/`owner`/`download`, read straight
539
+ * from the file by the access resolver, not here), and the config
540
+ * (`type`/`url`/`variables`/…). Text after the closing fence is free-form notes
541
+ * the parser ignores. A fence-less file is the legacy form (the whole file is
542
+ * the object — JSON can't carry fences). Throws on a structurally invalid
543
+ * object or a malformed explicit `id`. `slug` is provisional (the scanner
544
+ * dedups it); `name` is the FINAL UTCP manual namespace.
545
+ */
546
+ export function normalizeToolManual(
547
+ provisionalSlug: string,
548
+ repoPath: string,
549
+ content: string,
550
+ ): ToolManualDescriptor {
551
+ // THE TOOL IS THE FRONTMATTER. A fenced `.tool` is one `---` YAML block
552
+ // carrying everything — identity (`id`/`name`), access verbs, and config —
553
+ // and anything after the closing fence is free-form notes the parser ignores
554
+ // (like a SKILL.md body). One object serves every reader: the access resolver
555
+ // finds its verbs inside the fence, the id index finds `id`/`name`, and the
556
+ // config keys live in the same object. A fence-less file is the legacy form
557
+ // (the whole file is the object) — JSON `.tool`s can't carry fences.
558
+ const fm = extractFrontmatter(content);
559
+ const source = fm ? fm.frontmatter : content;
560
+ const parsed = parseYaml(source) as unknown;
561
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
562
+ throw new Error('`.tool` file must be a YAML/JSON object (in the `---` block when fenced)');
563
+ }
564
+ const obj = parsed as Record<string, unknown>;
565
+ const type = normalizeType(obj.type);
566
+
567
+ // Identity: an explicit `id` is used VERBATIM as the UTCP manual namespace, so
568
+ // it must be lowercase snake_case (UTCP's namespace grammar). With no explicit
569
+ // id, fall back to `name` then the filename and sanitize (legacy behaviour —
570
+ // keeps existing tools' namespaces).
571
+ const explicitId = typeof obj.id === 'string' ? obj.id.trim() : '';
572
+ let name: string;
573
+ if (explicitId) {
574
+ if (!isValidId(explicitId)) {
575
+ throw new Error(`tool id "${explicitId}" must be lowercase snake_case (letters, digits, underscores)`);
576
+ }
577
+ name = explicitId;
578
+ } else {
579
+ name = manualName(resolveDeclaredId(obj, provisionalSlug));
580
+ }
581
+
582
+ // A `.tool` may not reproduce a built-in manual's UTCP namespace — that would
583
+ // let it read the loopback creds seeded under that namespace on the agent's
584
+ // code-mode client (internal token, connector creds, KB bearer).
585
+ if (RESERVED_TOOL_NAMESPACES.includes(name.toLowerCase())) {
586
+ throw new Error(
587
+ `tool namespace "${name}" is reserved for a built-in manual — choose a different \`id\`/\`name\` ` +
588
+ "(a `.tool` sharing a built-in namespace could read that manual's seeded credentials).",
589
+ );
590
+ }
591
+
592
+ // Any `.tool` content — url, headers, inline tool templates, notes — may not
593
+ // reference the platform-seeded variables.
594
+ assertNoReservedVariableRefs(obj, name);
595
+
596
+ const descriptor: ToolManualDescriptor = {
597
+ slug: provisionalSlug,
598
+ name,
599
+ path: repoPath,
600
+ type,
601
+ };
602
+
603
+ const variables = normalizeVariables(obj.variables);
604
+ if (variables.length) descriptor.variables = variables;
605
+
606
+ descriptor.remote = normalizeRemote(obj.remote);
607
+
608
+ if (type === 'inline') {
609
+ const tools = Array.isArray(obj.tools) ? obj.tools : undefined;
610
+ if (!tools) throw new Error('inline `.tool` must have a `tools` array');
611
+ descriptor.tools = tools;
612
+ } else {
613
+ const url = typeof obj.url === 'string' ? obj.url.trim() : '';
614
+ if (!url) throw new Error(`${type} \`.tool\` must have a \`url\``);
615
+ descriptor.url = url;
616
+ // SSRF: a remote-capable `.tool` triggers a server-side discovery fetch to
617
+ // this URL (the MCP proxy AND now the in-process agent + headless routines),
618
+ // so a literal private/loopback/metadata host is refused at the producing
619
+ // boundary. Only a TEMPLATED HOSTNAME (resolved at call time) is
620
+ // uncheckable here — a `${...}` in the scheme, userinfo, port, path, or
621
+ // query still leaves a concrete network target (`${S}://169.254.169.254/x`
622
+ // and `http://${U}@169.254.169.254/x` target the metadata IP no matter what
623
+ // resolves), so the guard must still run against the literal host. The
624
+ // authority is therefore taken from `://` INDEPENDENT of the scheme being
625
+ // literal, and when the raw url can't parse (templated scheme or port) the
626
+ // check runs on a synthetic `<scheme-or-http>//host`. A BACKSLASH behaves as
627
+ // a slash for http(s) in WHATWG `new URL` — BOTH as the scheme separator
628
+ // (`${S}:\\169.254.169.254\\p` → the IP is the host) and inside the
629
+ // authority (`http://169.254.169.254\@${HOST}/x` fetches the IP, the `\@…`
630
+ // becoming path). So the authority separator is `:` + two `[\/]` (not just
631
+ // `://`), and `\` also terminates the authority alongside `/?#`; otherwise a
632
+ // literal internal host slips past as a templated scheme or userinfo.
633
+ // Local-only (`remote: false`) `.tool`s are never fetched server-side, so
634
+ // are exempt.
635
+ const literalScheme = /^([a-zA-Z][a-zA-Z0-9+.-]*:)[\\/]{2}/.exec(url)?.[1];
636
+ const sepMatch = /:[\\/]{2}/.exec(url);
637
+ const sepIdx = sepMatch ? sepMatch.index : -1;
638
+ const authority = sepIdx >= 0 ? url.slice(sepIdx + 3).split(/[/\\?#]/, 1)[0] : url;
639
+ const hostPort = authority.slice(authority.lastIndexOf('@') + 1);
640
+ const host = hostPort.startsWith('[') ? hostPort.slice(0, hostPort.indexOf(']') + 1) : hostPort.split(':')[0];
641
+ if (descriptor.remote !== false && !host.includes('${')) {
642
+ const checkUrl =
643
+ literalScheme && !authority.includes('${')
644
+ ? url // fully literal scheme + authority → validate the URL as-is
645
+ : sepIdx >= 0
646
+ ? `${literalScheme ?? 'http:'}//${host}` // templated scheme/userinfo/port, literal host
647
+ : url; // no authority shape at all → parse raw (refuses malformed)
648
+ assertSafeFetchUrl(checkUrl, { label: `\`.tool\` "${name}" url` });
649
+ }
650
+ if (obj.headers && typeof obj.headers === 'object' && !Array.isArray(obj.headers)) {
651
+ descriptor.headers = obj.headers as Record<string, string>;
652
+ }
653
+ if (type === 'http') {
654
+ const m = typeof obj.httpMethod === 'string' ? obj.httpMethod.toUpperCase() : 'GET';
655
+ descriptor.httpMethod = m === 'POST' ? 'POST' : 'GET';
656
+ }
657
+ }
658
+ return descriptor;
659
+ }
660
+
661
+ /**
662
+ * Parse the optional `variables:` block of a `.tool` file. Each entry names a
663
+ * `${VAR}` and who provisions it (`admin` default | `user`). Throws on a
664
+ * malformed entry so the whole file is skipped (never silently mis-scoped) — a
665
+ * mis-scoped variable is a security-relevant mistake, not a soft warning.
666
+ */
667
+ function normalizeVariables(raw: unknown): ToolVariable[] {
668
+ if (raw === undefined || raw === null) return [];
669
+ if (!Array.isArray(raw)) throw new Error('`variables` must be an array');
670
+ const seen = new Set<string>();
671
+ return raw.map((entry) => {
672
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
673
+ throw new Error('each `variables` entry must be an object');
674
+ }
675
+ const e = entry as Record<string, unknown>;
676
+ const name = typeof e.name === 'string' ? e.name.trim() : '';
677
+ if (!/^[A-Za-z0-9_]+$/.test(name)) {
678
+ throw new Error(`variable name "${name}" must match [A-Za-z0-9_]+`);
679
+ }
680
+ if (RESERVED_VARIABLE_NAMES.includes(name)) {
681
+ throw new Error(`variable name "${name}" is reserved for platform seeding and may not be declared by a \`.tool\``);
682
+ }
683
+ if (seen.has(name)) throw new Error(`duplicate variable "${name}"`);
684
+ seen.add(name);
685
+ const rawScope = typeof e.scope === 'string' ? e.scope.toLowerCase().trim() : '';
686
+ if (rawScope && rawScope !== 'admin' && rawScope !== 'user') {
687
+ throw new Error(`variable "${name}" has invalid scope "${e.scope}" (expected admin|user)`);
688
+ }
689
+ const scope: ToolVariableScope = rawScope === 'user' ? 'user' : 'admin';
690
+ const label = typeof e.label === 'string' && e.label.trim() ? e.label.trim() : undefined;
691
+ const oauth = normalizeVariableOAuth(name, e.oauth);
692
+ // OAuth is inherently per-caller — each user signs in for their own token. An
693
+ // admin-shared OAuth token would leak one user's token to all callers.
694
+ if (oauth && scope !== 'user') {
695
+ throw new Error(`variable "${name}" with oauth must be scope:user`);
696
+ }
697
+ return {
698
+ name,
699
+ scope,
700
+ ...(label ? { label } : {}),
701
+ ...(oauth ? { oauth } : {}),
702
+ };
703
+ });
704
+ }
705
+
706
+ /**
707
+ * Parse a variable's optional OAuth provider config. Carries PUBLIC config only —
708
+ * never a client secret. Both URLs are validated with the SAME SSRF-safe check the
709
+ * vault uses for OAuth endpoints (`assertSafeFetchUrl` with https required), so a
710
+ * `.tool` author can't aim a sign-in/token exchange at an internal host.
711
+ */
712
+ function normalizeVariableOAuth(name: string, raw: unknown): ToolVariableOAuth | undefined {
713
+ if (raw === undefined || raw === null) return undefined;
714
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
715
+ throw new Error(`variable "${name}" oauth must be an object`);
716
+ }
717
+ const o = raw as Record<string, unknown>;
718
+ // Confidential OAuth material is provisioned only through the protected
719
+ // client-secret route; reject it here so a plaintext `.tool` can't smuggle one in.
720
+ for (const forbidden of ['clientSecret', 'client_secret', 'secret']) {
721
+ if (o[forbidden] !== undefined) {
722
+ throw new Error(`variable "${name}" oauth.${forbidden} must be set through the protected client-secret route`);
723
+ }
724
+ }
725
+ const safeUrl = (v: unknown, field: string): string => {
726
+ const s = typeof v === 'string' ? v.trim() : '';
727
+ try {
728
+ assertSafeFetchUrl(s, { requireHttps: true, label: `${name} oauth.${field}` });
729
+ } catch (err) {
730
+ throw new Error(err instanceof Error ? err.message : `variable "${name}" oauth.${field} invalid`);
731
+ }
732
+ return s;
733
+ };
734
+ const authorizationUrl = safeUrl(o.authorizationUrl, 'authorizationUrl');
735
+ const tokenUrl = safeUrl(o.tokenUrl, 'tokenUrl');
736
+ const clientId = typeof o.clientId === 'string' && o.clientId.trim() ? o.clientId.trim() : '';
737
+ if (!clientId) throw new Error(`variable "${name}" oauth.clientId is required`);
738
+ let scopes: string[] | undefined;
739
+ if (o.scopes !== undefined) {
740
+ if (!Array.isArray(o.scopes) || !o.scopes.every((s) => typeof s === 'string')) {
741
+ throw new Error(`variable "${name}" oauth.scopes must be string[]`);
742
+ }
743
+ scopes = o.scopes as string[];
744
+ }
745
+ let authParams: Record<string, string> | undefined;
746
+ if (o.authParams !== undefined) {
747
+ if (typeof o.authParams !== 'object' || Array.isArray(o.authParams)) {
748
+ throw new Error(`variable "${name}" oauth.authParams must be an object of string values`);
749
+ }
750
+ const entries = Object.entries(o.authParams as Record<string, unknown>);
751
+ if (!entries.every(([, v]) => typeof v === 'string')) {
752
+ throw new Error(`variable "${name}" oauth.authParams values must be strings`);
753
+ }
754
+ authParams = Object.fromEntries(entries) as Record<string, string>;
755
+ }
756
+ return {
757
+ authorizationUrl,
758
+ tokenUrl,
759
+ clientId,
760
+ ...(scopes ? { scopes } : {}),
761
+ ...(authParams ? { authParams } : {}),
762
+ };
763
+ }
764
+
765
+ /**
766
+ * Parse the optional `remote` flag. Absent ⇒ `true` (remote-capable, the default).
767
+ * A non-boolean throws so the file is skipped rather than silently mis-classified.
768
+ */
769
+ function normalizeRemote(raw: unknown): boolean {
770
+ if (raw === undefined || raw === null) return true;
771
+ if (typeof raw !== 'boolean') throw new Error('`remote` must be a boolean');
772
+ return raw;
773
+ }
774
+
775
+ function normalizeType(raw: unknown): ToolManualType {
776
+ const t = typeof raw === 'string' ? raw.toLowerCase().trim() : '';
777
+ if (t === 'http') return 'http';
778
+ if (t === 'mcp') return 'mcp';
779
+ if (t === 'inline' || t === 'text' || t === '') return 'inline';
780
+ throw new Error(`unknown \`.tool\` type: ${raw}`);
781
+ }