@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,885 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { join } from 'node:path';
3
+ import type { Router, RequestHandler } from 'express';
4
+ import type { LocalFilesystem } from '@mastra/core/workspace';
5
+ import type { IToolRegistry, JsonSchema } from '../tool-registry/tool.contract.js';
6
+ import { ToolError, type ToolContext, type ToolHandler } from '../tool-helpers/tool.contract.js';
7
+ import { BRANCH_INPUT, toolDef } from '../tool-helpers/tool-def.js';
8
+ import {
9
+ recordOntologyRead,
10
+ assertOntologyWriteAllowed,
11
+ assertShellAllowedWithinOntology,
12
+ ONTOLOGY_BOUNDARY_NOTE,
13
+ SESSION_ID_INPUT,
14
+ type SessionOntologyGate,
15
+ } from './session-ontology.gate.js';
16
+ import type { IRoutineWritePolicy } from './routine-write-policy.js';
17
+ import type { ToolHandlerFactory } from '../tool-helpers/tool-handler.js';
18
+ import { requireInternalSource, requireExternalSource } from '../tool-auth/tool-auth.middleware.js';
19
+ import { workspaceIdForBranch } from './workspace.service.js';
20
+ // Leaf-level shared primitive (same exception `workspace.service.ts` already
21
+ // relies on) — not a workflow service, so this stays inside the module boundary.
22
+ import { assertValidBranchName } from '../workflow/git/branch-name.js';
23
+ import type { ISessionSink } from './session-sink.js';
24
+ import type { IAccessControl } from '../access/access-control.interface.js';
25
+ import { toKbRelative, resolveReadableMap } from '../access/kb-read-filter.js';
26
+ import type { SpillStore } from './spill-store.js';
27
+
28
+ /** A directory entry as returned by `LocalFilesystem.readdir`. */
29
+ interface DirEntry {
30
+ name: string;
31
+ type: 'file' | 'directory';
32
+ size?: number;
33
+ }
34
+
35
+ /**
36
+ * Per-call read-permission gate. Built inside each read handler from the
37
+ * caller's identity (`ctx.user.email`) and the branch they targeted, then
38
+ * threaded into the filesystem walk so `read_file` / `list_files` / `grep`
39
+ * never surface a KB node the user may not read.
40
+ */
41
+ interface ReadGate {
42
+ accessControl: IAccessControl;
43
+ kbDirName: string;
44
+ workspaceId: string;
45
+ userEmail: string;
46
+ }
47
+
48
+ /** Throw a 403 ToolError if the gate denies reading `wsPath` (a KB node). */
49
+ async function assertCanRead(gate: ReadGate, wsPath: string): Promise<void> {
50
+ const rel = toKbRelative(wsPath, gate.kbDirName);
51
+ if (rel === null) return; // not a KB node — not governed by read rules
52
+ const ok = await gate.accessControl.canRead(gate.workspaceId, gate.userEmail, rel);
53
+ if (!ok) throw new ToolError(`You don't have permission to read "${wsPath}".`, 403);
54
+ }
55
+
56
+ /**
57
+ * Drop the file entries the caller may not read from a directory listing.
58
+ * Directory entries and non-KB files are always kept (a directory itself has
59
+ * no `read:` verdict; its restricted children are filtered when read/listed) —
60
+ * this is the AGENT's inclusion policy and differs from the explorer tree,
61
+ * which hides restricted directories outright. Uses the FULL `canReadBatch`
62
+ * (per-node frontmatter honoured), via the shared `resolveReadableMap`. One
63
+ * batched ACL load per directory.
64
+ */
65
+ async function filterReadableEntries(
66
+ gate: ReadGate,
67
+ dir: string,
68
+ entries: DirEntry[],
69
+ ): Promise<DirEntry[]> {
70
+ const fileWsPaths: string[] = [];
71
+ for (const e of entries) {
72
+ if (e.type === 'directory') continue;
73
+ fileWsPaths.push(dir ? `${dir}/${e.name}` : e.name);
74
+ }
75
+ if (fileWsPaths.length === 0) return entries;
76
+ const verdict = await resolveReadableMap(
77
+ (wid, email, rels) => gate.accessControl.canReadBatch(wid, email, rels),
78
+ gate.workspaceId,
79
+ gate.userEmail,
80
+ gate.kbDirName,
81
+ fileWsPaths,
82
+ );
83
+ return entries.filter((e) => {
84
+ if (e.type === 'directory') return true;
85
+ const wsPath = dir ? `${dir}/${e.name}` : e.name;
86
+ return verdict.get(wsPath) === true;
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Appended (centrally, in `mount`) to EVERY workspace tool description. A KB
92
+ * author can drop a `CLAUDE.md` at the workspace root to document conventions
93
+ * for that knowledge base; agents (ours and external) should consult it before
94
+ * touching files. It rides on every entrypoint — reads (grep/list_files/
95
+ * file_stat) included — because any of them can be a session's first touch.
96
+ */
97
+ const KB_CONVENTIONS_NOTE =
98
+ ' Before your first read or change in a workspace, read `CLAUDE.md` at the KB root (if it exists) — it holds the author\'s conventions for this knowledge base, and you should follow them.';
99
+
100
+ const int = (description: string): JsonSchema => ({ type: 'integer', description });
101
+
102
+ const str = (description: string): JsonSchema => ({ type: 'string', description });
103
+
104
+ function asText(content: string | Buffer): string {
105
+ return typeof content === 'string' ? content : content.toString('utf8');
106
+ }
107
+
108
+ /** JS grep over the workspace tree (read methods only) — bounded by match + depth caps. */
109
+ async function grepWalk(
110
+ fs: LocalFilesystem,
111
+ dir: string,
112
+ re: RegExp,
113
+ out: { path: string; line: number; text: string }[],
114
+ max: number,
115
+ depth: number,
116
+ gate: ReadGate,
117
+ recordOntologyRead: (path: string) => Promise<void>,
118
+ ): Promise<void> {
119
+ if (out.length >= max || depth > 12) return;
120
+ let entries;
121
+ try {
122
+ entries = (await fs.readdir(dir || '.')) as DirEntry[];
123
+ } catch {
124
+ return;
125
+ }
126
+ // Filter unreadable KB nodes out of the walk so grep never opens — or leaks
127
+ // a line from — a file the caller may not read.
128
+ entries = await filterReadableEntries(gate, dir, entries);
129
+ for (const e of entries) {
130
+ if (out.length >= max) return;
131
+ if (e.name === '.git' || e.name === 'node_modules') continue;
132
+ const p = dir ? `${dir}/${e.name}` : e.name;
133
+ if (e.type === 'directory') {
134
+ await grepWalk(fs, p, re, out, max, depth + 1, gate, recordOntologyRead);
135
+ } else {
136
+ // Opening a file under a named ontology is a read of that ontology — even
137
+ // for a root-level grep that resolves to a neutral root. Record it so a
138
+ // cross-ontology grep poisons later writes (closes the read-leak).
139
+ await recordOntologyRead(p);
140
+ let content;
141
+ try {
142
+ content = asText(await fs.readFile(p));
143
+ } catch {
144
+ continue;
145
+ }
146
+ if (content.includes(String.fromCharCode(0))) continue; // skip binary (NUL)
147
+ const lines = content.split('\n');
148
+ for (let i = 0; i < lines.length && out.length < max; i++) {
149
+ re.lastIndex = 0;
150
+ if (re.test(lines[i])) out.push({ path: p, line: i + 1, text: lines[i].slice(0, 300) });
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Workspace domain tools: the file primitives (replacing Mastra's auto-injected
158
+ * Workspace tools) + unzip. Most just re-expose the SAME `LocalFilesystem`
159
+ * methods Mastra's tools call (via `ctx.getFilesystem(a.branch as string)`), so behaviour is
160
+ * identical and write-side ops still flow through the lock/commit pipeline.
161
+ * `edit_file`, `grep`, and `execute_command` are tool-level (no filesystem
162
+ * method), so they're implemented here. File ops are `both`; `execute_command`
163
+ * is INTERNAL-only (arbitrary shell as the caller is too dangerous to expose).
164
+ */
165
+ export function registerWorkspaceTools(
166
+ registry: IToolRegistry,
167
+ router: Router,
168
+ toolAuth: RequestHandler,
169
+ toolHandler: ToolHandlerFactory,
170
+ spillStore: SpillStore,
171
+ accessControl: IAccessControl,
172
+ kbDirName: string,
173
+ sessionOntologyGate: SessionOntologyGate,
174
+ writePolicy: IRoutineWritePolicy,
175
+ sessionSink: ISessionSink,
176
+ ): void {
177
+ /** Build the per-call read gate from the tool's branch input + caller identity. */
178
+ const readGateFor = (branch: string, ctx: ToolContext): ReadGate => ({
179
+ accessControl,
180
+ kbDirName,
181
+ workspaceId: workspaceIdForBranch(branch),
182
+ userEmail: ctx.user.email,
183
+ });
184
+
185
+ const mount = (spec: {
186
+ name: string;
187
+ description: string;
188
+ inputs: JsonSchema;
189
+ outputs?: JsonSchema;
190
+ write: boolean;
191
+ internalOnly?: boolean;
192
+ handler: ToolHandler;
193
+ }): void => {
194
+ const path = `/api/agent/tools/${spec.name}`;
195
+ const def = toolDef({
196
+ name: spec.name,
197
+ // Every workspace entrypoint carries the CLAUDE.md reminder, appended once
198
+ // here so no tool (especially the read-only ones a session hits first) can
199
+ // miss it.
200
+ description: spec.description + KB_CONVENTIONS_NOTE,
201
+ path,
202
+ inputs: spec.inputs,
203
+ outputs: spec.outputs,
204
+ tags: spec.write ? ['workspace', 'write'] : ['workspace'],
205
+ });
206
+ registry.registerInternalTool(def);
207
+ if (!spec.internalOnly) registry.registerExternalTool(def);
208
+ // Internal-only tools (e.g. `execute_command`) keep their route mounted —
209
+ // our agent calls it over the same loopback — but gate it to internal-source
210
+ // callers so an external connection key can't invoke it by name.
211
+ router.post(
212
+ path.slice('/api'.length),
213
+ toolAuth,
214
+ ...(spec.internalOnly ? [requireInternalSource] : []),
215
+ toolHandler(spec.handler, { write: spec.write }),
216
+ );
217
+ };
218
+
219
+ // ── session bootstrap (external agents) ─────────────────────────────────
220
+ // Every read/write tool below scopes the ontology-session boundary off a
221
+ // `sessionId`. The in-process agent carries its thread id, but an external
222
+ // agent has no ambient run id and so cannot satisfy the gate until it has
223
+ // one. This mints that id up front (called ONCE); the MCP proxy then threads
224
+ // it onto every later gated call via its sessionId-output continuity
225
+ // convention. EXTERNAL-ONLY (not registered internal): the in-process agent
226
+ // already supplies its session id and ignores any body value.
227
+ //
228
+ // WHAT the minted id is backed by is the `ISessionSink` port's business
229
+ // (session-sink.ts). In the enterprise app it is a REAL chat-thread id, so
230
+ // the SAME id works end to end: KB reads scope the ontology boundary under
231
+ // it, AND `ask` accepts it (its sessionId IS a chat thread, resolved via
232
+ // getThread) — that unification is what stops a caller reading from one
233
+ // ontology and then having `ask` write into another. In a core-only
234
+ // deployment (no chat/ask) the default sink mints a bare id, which is all
235
+ // the ontology gate needs.
236
+ const startSessionDef = toolDef({
237
+ name: 'start_session',
238
+ description:
239
+ 'Mint the KnowledgeBase session id this run needs to read or write the knowledge ontologies. Call this ONCE, before any other KnowledgeBase tool, and only once per run — every gated tool needs the `sessionId` it returns to enforce the one-ontology-per-conversation boundary, and minting a new id mid-run resets that boundary. The id is also a chat session in the app, so you can hand the SAME id to the `ask` tool: reads and ask then share one ontology boundary. Pass the returned id explicitly as `sessionId` on every subsequent KnowledgeBase tool call (direct MCP calls and inside `call_tool_chain` alike). Returns `{ sessionId }`.',
240
+ path: '/api/agent/tools/start_session',
241
+ inputs: { type: 'object', properties: {}, additionalProperties: false },
242
+ outputs: {
243
+ type: 'object',
244
+ properties: { sessionId: str('The minted session id — pass it as `sessionId` on subsequent KnowledgeBase tool calls and to `ask`.') },
245
+ required: ['sessionId'],
246
+ },
247
+ tags: ['workspace'],
248
+ });
249
+ registry.registerExternalTool(startSessionDef);
250
+ // Mint the session id via the sink and return it (see comment above: one id
251
+ // spans start_session -> reads -> ask, closing the ontology-pollution gap).
252
+ router.post(
253
+ '/agent/tools/start_session',
254
+ toolAuth,
255
+ // External-only: an internal token already carries its run's sessionId, so
256
+ // minting a new thread mid-run would reset the ontology boundary. Note
257
+ // "external" includes the MCP proxy's `externalProxy` loopback tokens
258
+ // (OAuth/JWT MCP sessions) — the verifier resolves those to
259
+ // `source: 'external'`, and one such session may legitimately mint several
260
+ // per-chat sessionIds over its lifetime.
261
+ requireExternalSource,
262
+ toolHandler(async (_args, ctx) => {
263
+ const { sessionId } = await sessionSink.createSession(ctx.user.id, new Date());
264
+ return { sessionId };
265
+ }),
266
+ );
267
+
268
+ // ── reads ──────────────────────────────────────────────────────────────
269
+ mount({
270
+ name: 'read_file',
271
+ description:
272
+ 'Read a workspace file as text. Returns `{ path, content }`. Optional `offset`/`limit` slice the content (characters for a file, bytes for a `__tool_chain_spill__/…` ref) — use them to page through large files or a `call_tool_chain` spill rather than reading multi-MB in full. A spill ref is workspace-independent: `branch` is ignored for it.' +
273
+ ONTOLOGY_BOUNDARY_NOTE,
274
+ inputs: {
275
+ type: 'object',
276
+ properties: {
277
+ branch: BRANCH_INPUT,
278
+ path: str('Workspace-relative path, or a `__tool_chain_spill__/…` ref from a truncated `call_tool_chain`.'),
279
+ offset: int('Start character index (default 0).'),
280
+ limit: int('Max characters to return from `offset`.'),
281
+ sessionId: SESSION_ID_INPUT,
282
+ },
283
+ required: ['branch', 'path'],
284
+ additionalProperties: false,
285
+ },
286
+ outputs: {
287
+ type: 'object',
288
+ properties: { path: str('The path that was read (echoes the input).'), content: str('File (or spill) content, sliced if offset/limit were given.') },
289
+ required: ['path', 'content'],
290
+ },
291
+ write: false,
292
+ handler: async (a, ctx: ToolContext) => {
293
+ const p = a.path as string;
294
+ const offset = typeof a.offset === 'number' ? a.offset : undefined;
295
+ const limit = typeof a.limit === 'number' ? a.limit : undefined;
296
+ if (spillStore.isSpillRef(p)) {
297
+ return { path: p, content: await spillStore.read(p, offset, limit) };
298
+ }
299
+ await recordOntologyRead(sessionOntologyGate, ctx, p);
300
+ await assertCanRead(readGateFor(a.branch as string, ctx), p);
301
+ const fs = await ctx.getFilesystem(a.branch as string);
302
+ const content = asText(await fs.readFile(p));
303
+ const start = offset && offset > 0 ? offset : 0;
304
+ const sliced = offset !== undefined || limit !== undefined
305
+ ? content.slice(start, limit !== undefined ? start + limit : undefined)
306
+ : content;
307
+ return { path: p, content: sliced };
308
+ },
309
+ });
310
+
311
+ mount({
312
+ name: 'list_files',
313
+ description:
314
+ 'List a directory. Returns `{ path, entries: [{ name, type, size? }] }`. Omit `path` for the workspace root.' +
315
+ ONTOLOGY_BOUNDARY_NOTE,
316
+ inputs: {
317
+ type: 'object',
318
+ properties: {
319
+ branch: BRANCH_INPUT,
320
+ path: str('Workspace-relative directory (default: root).'),
321
+ sessionId: SESSION_ID_INPUT,
322
+ },
323
+ required: ['branch'],
324
+ additionalProperties: false,
325
+ },
326
+ outputs: {
327
+ type: 'object',
328
+ properties: {
329
+ path: str('The directory listed (empty string for the root).'),
330
+ entries: {
331
+ type: 'array',
332
+ description: 'Directory entries.',
333
+ items: {
334
+ type: 'object',
335
+ properties: { name: str('Entry name.'), type: str('`file` or `directory`.'), size: int('Size in bytes (files only).') },
336
+ required: ['name', 'type'],
337
+ },
338
+ },
339
+ },
340
+ required: ['path', 'entries'],
341
+ },
342
+ write: false,
343
+ handler: async (a, ctx: ToolContext) => {
344
+ const dir = (a.path as string) || '';
345
+ await recordOntologyRead(sessionOntologyGate, ctx, dir);
346
+ const fs = await ctx.getFilesystem(a.branch as string);
347
+ const entries = (await fs.readdir(dir || '.')) as DirEntry[];
348
+ const filtered = await filterReadableEntries(readGateFor(a.branch as string, ctx), dir, entries);
349
+ return { path: a.path ?? '', entries: filtered };
350
+ },
351
+ });
352
+
353
+ mount({
354
+ name: 'file_stat',
355
+ description:
356
+ 'Get a file/directory\'s metadata (name, type, size, …) without reading content.' +
357
+ ONTOLOGY_BOUNDARY_NOTE,
358
+ inputs: {
359
+ type: 'object',
360
+ properties: {
361
+ branch: BRANCH_INPUT,
362
+ path: str('Workspace-relative path.'),
363
+ sessionId: SESSION_ID_INPUT,
364
+ },
365
+ required: ['branch', 'path'],
366
+ additionalProperties: false,
367
+ },
368
+ outputs: {
369
+ type: 'object',
370
+ description: "The filesystem entry's metadata.",
371
+ properties: { name: str('Entry name.'), type: str('`file` or `directory`.'), size: int('Size in bytes.') },
372
+ additionalProperties: true,
373
+ },
374
+ write: false,
375
+ handler: async (a, ctx: ToolContext) => {
376
+ const p = a.path as string;
377
+ await recordOntologyRead(sessionOntologyGate, ctx, p);
378
+ await assertCanRead(readGateFor(a.branch as string, ctx), p);
379
+ return (await ctx.getFilesystem(a.branch as string)).stat(p);
380
+ },
381
+ });
382
+
383
+ mount({
384
+ name: 'grep',
385
+ description:
386
+ 'Regex content search across the workspace. Returns `{ matches: [{ path, line, text }] }` (capped). Use to find where something is defined/referenced.' +
387
+ ONTOLOGY_BOUNDARY_NOTE,
388
+ inputs: {
389
+ type: 'object',
390
+ properties: {
391
+ branch: BRANCH_INPUT,
392
+ pattern: str('JavaScript regular expression.'),
393
+ path: str('Subtree to search (default: whole workspace).'),
394
+ ignore_case: { type: 'boolean', description: 'Case-insensitive match.' },
395
+ max_results: { type: 'integer', minimum: 1, maximum: 1000, description: 'Cap on matches (default 200).' },
396
+ sessionId: SESSION_ID_INPUT,
397
+ },
398
+ required: ['branch', 'pattern'],
399
+ additionalProperties: false,
400
+ },
401
+ outputs: {
402
+ type: 'object',
403
+ properties: {
404
+ matches: {
405
+ type: 'array',
406
+ description: 'Matching lines (capped by `max_results`).',
407
+ items: {
408
+ type: 'object',
409
+ properties: { path: str('Workspace-relative file path.'), line: int('1-based line number.'), text: str('The matching line (truncated to 300 chars).') },
410
+ required: ['path', 'line', 'text'],
411
+ },
412
+ },
413
+ truncated: { type: 'boolean', description: 'True if the match cap was hit and results may be incomplete.' },
414
+ },
415
+ required: ['matches', 'truncated'],
416
+ },
417
+ write: false,
418
+ handler: async (a, ctx: ToolContext) => {
419
+ let re: RegExp;
420
+ try {
421
+ re = new RegExp(a.pattern as string, a.ignore_case ? 'i' : '');
422
+ } catch (err) {
423
+ throw new ToolError(`Invalid regex: ${(err as Error).message}`, 400);
424
+ }
425
+ const searchRoot = typeof a.path === 'string' ? a.path : '';
426
+ // The search root itself is checked here (fail-closed for an agent grep on
427
+ // a named subtree with no sessionId); each file the walk actually opens is
428
+ // recorded per-file below, so a root-level grep that reaches into multiple
429
+ // ontologies still records each one (and can poison later writes).
430
+ await recordOntologyRead(sessionOntologyGate, ctx, searchRoot);
431
+ const out: { path: string; line: number; text: string }[] = [];
432
+ const max = typeof a.max_results === 'number' ? Math.min(a.max_results, 1000) : 200;
433
+ await grepWalk(
434
+ await ctx.getFilesystem(a.branch as string),
435
+ searchRoot,
436
+ re,
437
+ out,
438
+ max,
439
+ 0,
440
+ readGateFor(a.branch as string, ctx),
441
+ (p) => recordOntologyRead(sessionOntologyGate, ctx, p),
442
+ );
443
+ return { matches: out, truncated: out.length >= max };
444
+ },
445
+ });
446
+
447
+ // ── writes (through the lock/commit pipeline) ───────────────────────────
448
+ mount({
449
+ name: 'write_file',
450
+ description:
451
+ 'Write (create or overwrite) a workspace file. The change is committed + pushed as you. Returns `{ path, bytes }`.' +
452
+ ONTOLOGY_BOUNDARY_NOTE,
453
+ inputs: {
454
+ type: 'object',
455
+ properties: {
456
+ branch: BRANCH_INPUT,
457
+ path: str('Workspace-relative path.'),
458
+ content: str('Full file content.'),
459
+ sessionId: SESSION_ID_INPUT,
460
+ },
461
+ required: ['branch', 'path', 'content'],
462
+ additionalProperties: false,
463
+ },
464
+ outputs: {
465
+ type: 'object',
466
+ properties: { path: str('The path written (echoes the input).'), bytes: int('Number of bytes written.') },
467
+ required: ['path', 'bytes'],
468
+ },
469
+ write: true,
470
+ handler: async (a, ctx: ToolContext) => {
471
+ // NB: this is a no-op for chat + `ontology_ingest` — it only bites when a
472
+ // routine executor has explicitly restricted THIS session's `ctx.sessionId`
473
+ // (today only `watchlist_check`, to `.html`). Unrestricted sessions pass straight
474
+ // through (see `assertPathWritable`), so it does not limit other agents.
475
+ writePolicy.assertPathWritable(ctx.sessionId, a.path as string);
476
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.path as string);
477
+ const fs = await ctx.getFilesystem(a.branch as string);
478
+ await fs.writeFile(a.path as string, a.content as string);
479
+ return { path: a.path, bytes: Buffer.byteLength(a.content as string, 'utf8') };
480
+ },
481
+ });
482
+
483
+ mount({
484
+ name: 'write_files',
485
+ description:
486
+ 'Batch-write many files in ONE commit — far faster than calling write_file once per file when ' +
487
+ 'creating many files at once (e.g. seeding a knowledge base). Each entry is `{ path, content }`; all ' +
488
+ 'are created/overwritten and committed + pushed together as you. Prefer this over many write_file ' +
489
+ 'calls. All files must be in the SAME ontology (the boundary below applies to the batch). Returns `{ count }`.' +
490
+ ONTOLOGY_BOUNDARY_NOTE,
491
+ inputs: {
492
+ type: 'object',
493
+ properties: {
494
+ branch: BRANCH_INPUT,
495
+ files: {
496
+ type: 'array',
497
+ description: 'Files to write; each created or overwritten.',
498
+ items: {
499
+ type: 'object',
500
+ properties: { path: str('Workspace-relative path.'), content: str('Full file content.') },
501
+ required: ['path', 'content'],
502
+ additionalProperties: false,
503
+ },
504
+ },
505
+ sessionId: SESSION_ID_INPUT,
506
+ },
507
+ required: ['branch', 'files'],
508
+ additionalProperties: false,
509
+ },
510
+ outputs: {
511
+ type: 'object',
512
+ properties: { count: int('Number of files written.') },
513
+ required: ['count'],
514
+ },
515
+ write: true,
516
+ handler: async (a, ctx: ToolContext) => {
517
+ const files = (a.files as Array<{ path: string; content: string }>) ?? [];
518
+ if (files.length === 0) return { count: 0 };
519
+ // Gate every path first (records ontology touches; a cross-ontology batch
520
+ // is blocked exactly like the per-file write tools).
521
+ for (const f of files) writePolicy.assertPathWritable(ctx.sessionId, f.path);
522
+ for (const f of files) await assertOntologyWriteAllowed(sessionOntologyGate, ctx, f.path);
523
+ // `write: true` guarantees a LockingFilesystem here; `writeFiles` lands the
524
+ // whole batch as one commit. Structural cast avoids a workflow-internal import.
525
+ const fs = (await ctx.getFilesystem(a.branch as string)) as unknown as {
526
+ writeFiles(writes: { path: string; content: string }[], summary: string): Promise<void>;
527
+ };
528
+ await fs.writeFiles(
529
+ files.map((f) => ({ path: f.path, content: f.content })),
530
+ `Write ${files.length} file(s)`,
531
+ );
532
+ return { count: files.length };
533
+ },
534
+ });
535
+
536
+ mount({
537
+ name: 'edit_file',
538
+ description:
539
+ 'Replace an exact string in a workspace file. `old_string` must appear exactly once unless `replace_all`. Committed + pushed as you.' +
540
+ ONTOLOGY_BOUNDARY_NOTE,
541
+ inputs: {
542
+ type: 'object',
543
+ properties: {
544
+ branch: BRANCH_INPUT,
545
+ path: str('Workspace-relative path.'),
546
+ old_string: str('Exact text to replace (include enough context to be unique).'),
547
+ new_string: str('Replacement text.'),
548
+ replace_all: { type: 'boolean', description: 'Replace every occurrence instead of requiring a unique match.' },
549
+ sessionId: SESSION_ID_INPUT,
550
+ },
551
+ required: ['branch', 'path', 'old_string', 'new_string'],
552
+ additionalProperties: false,
553
+ },
554
+ outputs: {
555
+ type: 'object',
556
+ properties: { path: str('The path edited (echoes the input).'), replaced: int('Number of occurrences replaced.') },
557
+ required: ['path', 'replaced'],
558
+ },
559
+ write: true,
560
+ handler: async (a, ctx: ToolContext) => {
561
+ writePolicy.assertPathWritable(ctx.sessionId, a.path as string);
562
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.path as string);
563
+ const fs = await ctx.getFilesystem(a.branch as string);
564
+ const path = a.path as string;
565
+ const oldStr = a.old_string as string;
566
+ const newStr = a.new_string as string;
567
+ const content = asText(await fs.readFile(path));
568
+ const count = oldStr ? content.split(oldStr).length - 1 : 0;
569
+ if (count === 0) throw new ToolError('old_string not found in the file.', 400);
570
+ if (count > 1 && a.replace_all !== true) {
571
+ throw new ToolError(`old_string appears ${count} times — add more context to make it unique, or set replace_all.`, 400);
572
+ }
573
+ const updated = a.replace_all === true ? content.split(oldStr).join(newStr) : content.replace(oldStr, newStr);
574
+ await fs.writeFile(path, updated);
575
+ return { path, replaced: a.replace_all === true ? count : 1 };
576
+ },
577
+ });
578
+
579
+ mount({
580
+ name: 'delete_file',
581
+ description: 'Delete a workspace file. Committed + pushed as you.' + ONTOLOGY_BOUNDARY_NOTE,
582
+ inputs: {
583
+ type: 'object',
584
+ properties: {
585
+ branch: BRANCH_INPUT,
586
+ path: str('Workspace-relative path.'),
587
+ sessionId: SESSION_ID_INPUT,
588
+ },
589
+ required: ['branch', 'path'],
590
+ additionalProperties: false,
591
+ },
592
+ outputs: {
593
+ type: 'object',
594
+ properties: { path: str('The path deleted (echoes the input).'), deleted: { type: 'boolean', description: 'Always true on success.' } },
595
+ required: ['path', 'deleted'],
596
+ },
597
+ write: true,
598
+ handler: async (a, ctx: ToolContext) => {
599
+ // A delete propagates no cross-ontology information (it removes a node, it
600
+ // doesn't carry bytes from elsewhere), so it is NOT ontology-write-gated — it
601
+ // only records the ontology it touched, like a read. The extension policy
602
+ // DOES apply though: a dashboard-only run must not delete graph `.md` nodes.
603
+ writePolicy.assertPathWritable(ctx.sessionId, a.path as string);
604
+ await recordOntologyRead(sessionOntologyGate, ctx, a.path as string);
605
+ await (await ctx.getFilesystem(a.branch as string)).deleteFile(a.path as string);
606
+ return { path: a.path, deleted: true };
607
+ },
608
+ });
609
+
610
+ mount({
611
+ name: 'mkdir',
612
+ description: 'Create a directory (recursive). An empty dir gets a `.gitkeep` so it persists in git.' + ONTOLOGY_BOUNDARY_NOTE,
613
+ inputs: {
614
+ type: 'object',
615
+ properties: {
616
+ branch: BRANCH_INPUT,
617
+ path: str('Workspace-relative directory.'),
618
+ sessionId: SESSION_ID_INPUT,
619
+ },
620
+ required: ['branch', 'path'],
621
+ additionalProperties: false,
622
+ },
623
+ outputs: {
624
+ type: 'object',
625
+ properties: { path: str('The directory created (echoes the input).'), created: { type: 'boolean', description: 'Always true on success.' } },
626
+ required: ['path', 'created'],
627
+ },
628
+ write: true,
629
+ handler: async (a, ctx: ToolContext) => {
630
+ writePolicy.assertPathWritable(ctx.sessionId, a.path as string);
631
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.path as string);
632
+ await (await ctx.getFilesystem(a.branch as string)).mkdir(a.path as string, { recursive: true });
633
+ return { path: a.path, created: true };
634
+ },
635
+ });
636
+
637
+ mount({
638
+ name: 'move_file',
639
+ description: 'Move/rename a workspace file. Lands as a delete + create. Committed + pushed as you.' + ONTOLOGY_BOUNDARY_NOTE,
640
+ inputs: {
641
+ type: 'object',
642
+ properties: {
643
+ branch: BRANCH_INPUT,
644
+ src: str('Source path.'),
645
+ dest: str('Destination path.'),
646
+ sessionId: SESSION_ID_INPUT,
647
+ },
648
+ required: ['branch', 'src', 'dest'],
649
+ additionalProperties: false,
650
+ },
651
+ outputs: {
652
+ type: 'object',
653
+ properties: { src: str('Source path (echoes the input).'), dest: str('Destination path (echoes the input).'), moved: { type: 'boolean', description: 'Always true on success.' } },
654
+ required: ['src', 'dest', 'moved'],
655
+ },
656
+ write: true,
657
+ handler: async (a, ctx: ToolContext) => {
658
+ // A move CARRIES the source content into the destination — a genuine
659
+ // cross-ontology flow if the two differ — so BOTH endpoints are write-gated
660
+ // (unlike a plain delete, which moves no content). Check both BEFORE
661
+ // touching disk so a blocked endpoint can't leave the source already deleted.
662
+ writePolicy.assertPathWritable(ctx.sessionId, a.src as string);
663
+ writePolicy.assertPathWritable(ctx.sessionId, a.dest as string);
664
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.src as string);
665
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.dest as string);
666
+ await (await ctx.getFilesystem(a.branch as string)).moveFile(a.src as string, a.dest as string);
667
+ return { src: a.src, dest: a.dest, moved: true };
668
+ },
669
+ });
670
+
671
+ mount({
672
+ name: 'copy_file',
673
+ description: 'Copy a workspace file to a new path. Committed + pushed as you.' + ONTOLOGY_BOUNDARY_NOTE,
674
+ inputs: {
675
+ type: 'object',
676
+ properties: {
677
+ branch: BRANCH_INPUT,
678
+ src: str('Source path.'),
679
+ dest: str('Destination path.'),
680
+ sessionId: SESSION_ID_INPUT,
681
+ },
682
+ required: ['branch', 'src', 'dest'],
683
+ additionalProperties: false,
684
+ },
685
+ outputs: {
686
+ type: 'object',
687
+ properties: { src: str('Source path (echoes the input).'), dest: str('Destination path (echoes the input).'), copied: { type: 'boolean', description: 'Always true on success.' } },
688
+ required: ['src', 'dest', 'copied'],
689
+ },
690
+ write: true,
691
+ handler: async (a, ctx: ToolContext) => {
692
+ // A copy CARRIES the source content into the destination — a genuine
693
+ // cross-ontology flow if the two differ — so BOTH endpoints are write-gated.
694
+ // Check both before touching disk.
695
+ writePolicy.assertPathWritable(ctx.sessionId, a.src as string);
696
+ writePolicy.assertPathWritable(ctx.sessionId, a.dest as string);
697
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.src as string);
698
+ await assertOntologyWriteAllowed(sessionOntologyGate, ctx, a.dest as string);
699
+ await (await ctx.getFilesystem(a.branch as string)).copyFile(a.src as string, a.dest as string);
700
+ return { src: a.src, dest: a.dest, copied: true };
701
+ },
702
+ });
703
+
704
+ mount({
705
+ name: 'unzip',
706
+ description:
707
+ 'Extract a .zip already in the workspace (defaults to the zip\'s parent). Returns extracted files + skipped entries. Existing files are overwritten.' +
708
+ ONTOLOGY_BOUNDARY_NOTE,
709
+ inputs: {
710
+ type: 'object',
711
+ properties: {
712
+ branch: BRANCH_INPUT,
713
+ path: str('Workspace-relative .zip path.'),
714
+ destination: str("Directory to extract into (default: the zip's parent)."),
715
+ sessionId: SESSION_ID_INPUT,
716
+ },
717
+ required: ['branch', 'path'],
718
+ additionalProperties: false,
719
+ },
720
+ outputs: {
721
+ type: 'object',
722
+ properties: {
723
+ destination: str('Directory the archive was extracted into.'),
724
+ extracted: { type: 'array', items: { type: 'string' }, description: 'Workspace-relative paths of the files written.' },
725
+ skipped: {
726
+ type: 'array',
727
+ description: 'Entries that were not extracted, with the reason.',
728
+ items: {
729
+ type: 'object',
730
+ properties: { path: str('Entry path inside the archive.'), reason: str('Why it was skipped (e.g. unsafe path, size cap).') },
731
+ required: ['path', 'reason'],
732
+ },
733
+ },
734
+ },
735
+ required: ['destination', 'extracted', 'skipped'],
736
+ },
737
+ write: true,
738
+ handler: async (a, ctx: ToolContext) => {
739
+ const zipPath = a.path as string;
740
+ // Reading the source archive pins/records the source ontology, so a session
741
+ // can't unzip from ontology A into ontology B without the A read counting.
742
+ await recordOntologyRead(sessionOntologyGate, ctx, zipPath);
743
+ return ctx.workspaceService.unzipFile(
744
+ workspaceIdForBranch(a.branch as string),
745
+ zipPath,
746
+ typeof a.destination === 'string' ? a.destination : undefined,
747
+ // Each extracted file is a write: a cross-ontology or write-blocked entry
748
+ // is skipped (not extracted), so an archive can't bypass the boundary — the
749
+ // extension policy applies per entry too, so a restricted run can't unzip a
750
+ // `.md` into the graph.
751
+ (wsRelPath) => {
752
+ writePolicy.assertPathWritable(ctx.sessionId, wsRelPath);
753
+ return assertOntologyWriteAllowed(sessionOntologyGate, ctx, wsRelPath);
754
+ },
755
+ );
756
+ },
757
+ });
758
+
759
+ // ── shell (internal-only) ───────────────────────────────────────────────
760
+ mount({
761
+ name: 'execute_command',
762
+ description:
763
+ 'Run a shell command in the workspace directory. Returns `{ stdout, stderr, exitCode }` (output capped). Use for git status/log, grep/rg, build/test commands.' +
764
+ ONTOLOGY_BOUNDARY_NOTE,
765
+ internalOnly: true,
766
+ inputs: {
767
+ type: 'object',
768
+ properties: {
769
+ branch: BRANCH_INPUT,
770
+ command: str('The shell command line to run.'),
771
+ timeout_ms: { type: 'integer', minimum: 1000, maximum: 120000, description: 'Timeout in ms (default 30000).' },
772
+ sessionId: SESSION_ID_INPUT,
773
+ },
774
+ required: ['branch', 'command'],
775
+ additionalProperties: false,
776
+ },
777
+ outputs: {
778
+ type: 'object',
779
+ properties: {
780
+ stdout: str('Captured standard output (truncated to 50,000 chars).'),
781
+ stderr: str('Captured standard error (truncated to 50,000 chars).'),
782
+ exitCode: int('Process exit code; -1 if it was killed (timeout/abort) or failed to spawn.'),
783
+ },
784
+ required: ['stdout', 'stderr', 'exitCode'],
785
+ },
786
+ write: true,
787
+ handler: async (a, ctx: ToolContext) => {
788
+ // Refuse a missing branch FIRST — before the policy gates below — so a
789
+ // malformed call always gets the 400 that names what is missing, rather
790
+ // than a 403 from a restricted session masking it. Unlike the file tools
791
+ // (which route through `getOrCreateForUser` and fall back to the default
792
+ // branch), execute_command turns `branch` straight into a workspace — so
793
+ // an absent value would `encodeURIComponent(undefined)` to the string
794
+ // "undefined", then try to CLONE a branch literally named "undefined",
795
+ // 500ing and leaving an `<workspaces>/undefined/` shell behind. Fail with
796
+ // a clear 4xx naming the missing context instead.
797
+ const branch = a.branch;
798
+ if (typeof branch !== 'string' || branch.trim().length === 0) {
799
+ throw new ToolError(
800
+ 'execute_command requires a `branch`: pass the branch (draft) whose workspace to run the command in — the one you are currently working on.',
801
+ 400,
802
+ );
803
+ }
804
+ // Then the SHAPE of the value, via the one canonical validator every other
805
+ // branch path uses — no hand-maintained list of suspicious literals here,
806
+ // which would both miss malformed refs (`..`, `-x`, `foo/.lock`) and
807
+ // reserve names git considers perfectly valid. Note this runs on the RAW
808
+ // string: a whitespace-padded `" main "` is refused rather than silently
809
+ // trimmed into a different branch than the caller passed.
810
+ try {
811
+ assertValidBranchName(branch);
812
+ } catch (err) {
813
+ throw new ToolError(
814
+ `execute_command got an invalid \`branch\`: ${(err as Error).message} — ` +
815
+ 'pass the exact branch (draft) whose workspace to run the command in.',
816
+ 400,
817
+ );
818
+ }
819
+ // Shell is a write path with no single target path to check, so enforce the
820
+ // boundary at the session level: refuse once the run is already write-blocked,
821
+ // or when the run is restricted to a file type (shell could write anything).
822
+ writePolicy.assertUnrestricted(ctx.sessionId);
823
+ await assertShellAllowedWithinOntology(sessionOntologyGate, ctx);
824
+ // Canonical per-branch bootstrap entry point — it owns the workspace-id
825
+ // encoding and the single-flight clone, so the shell never derives a
826
+ // workspace path by hand.
827
+ const cwd = (await ctx.workspaceService.getOrCreateForBranch(branch)).absolutePath;
828
+ const timeoutMs = typeof a.timeout_ms === 'number' ? a.timeout_ms : 30_000;
829
+ // cwd is the workspace ROOT so shell paths match the workspace-relative
830
+ // paths every other file tool uses — but the git clone lives one level
831
+ // deeper, at <workspace>/<kbDirName>/.git. For a bare `git status` /
832
+ // `git diff` (what the agent prompt teaches) point git there explicitly so
833
+ // it resolves the KB clone instead of failing with "not a git repository".
834
+ //
835
+ // Because the command runs under `shell: true`, GIT_DIR/GIT_WORK_TREE
836
+ // apply to the WHOLE shell, so restrict the override to a *standalone* bare
837
+ // `git …` invocation. Otherwise the KB repo context would (a) leak into a
838
+ // chained step that spawns its own git — `git commit && npm ci` — and (b)
839
+ // override a caller's explicit `git -C …` / `--git-dir` / `--work-tree`.
840
+ const command = (a.command as string).trim();
841
+ const isStandaloneGit =
842
+ /^git(\s|$)/.test(command) &&
843
+ !/[;&|`\n]|\$\(/.test(command) &&
844
+ !/(?:^|\s)(?:-C|--git-dir|--work-tree)(?:[=\s]|$)/.test(command);
845
+ const env = isStandaloneGit
846
+ ? {
847
+ ...process.env,
848
+ GIT_DIR: join(cwd, kbDirName, '.git'),
849
+ GIT_WORK_TREE: join(cwd, kbDirName),
850
+ }
851
+ : process.env;
852
+ return new Promise((resolve) => {
853
+ const child = spawn(a.command as string, { cwd, shell: true, env });
854
+ let stdout = '';
855
+ let stderr = '';
856
+ const timer = setTimeout(() => child.kill('SIGKILL'), timeoutMs);
857
+ // If the caller disconnects (request aborted), kill the child instead of
858
+ // letting it run to the timeout; the resulting `close`/`error` settles
859
+ // the promise through `finish`.
860
+ const onAbort = () => child.kill('SIGKILL');
861
+ ctx.abortSignal.addEventListener('abort', onAbort, { once: true });
862
+ const finish = (value: unknown) => {
863
+ clearTimeout(timer);
864
+ ctx.abortSignal.removeEventListener('abort', onAbort);
865
+ resolve(value);
866
+ };
867
+ // Cap accumulation while streaming so a verbose command can't exhaust
868
+ // memory before `close`; the final slice keeps the output bound.
869
+ const OUTPUT_CAP = 50_000;
870
+ child.stdout.on('data', (d) => {
871
+ if (stdout.length < OUTPUT_CAP) stdout += d.toString();
872
+ });
873
+ child.stderr.on('data', (d) => {
874
+ if (stderr.length < OUTPUT_CAP) stderr += d.toString();
875
+ });
876
+ child.on('close', (code) => {
877
+ finish({ stdout: stdout.slice(0, 50_000), stderr: stderr.slice(0, 50_000), exitCode: code ?? -1 });
878
+ });
879
+ child.on('error', (err) => {
880
+ finish({ stdout, stderr: `${stderr}\n${String(err)}`.slice(0, 50_000), exitCode: -1 });
881
+ });
882
+ });
883
+ },
884
+ });
885
+ }