@bevel-software/platform-core-backend 0.33.0 → 0.35.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 (602) hide show
  1. package/agent-guide/access-control.md +146 -289
  2. package/agent-guide/conventions.md +23 -25
  3. package/agent-guide/directory-structure.md +72 -120
  4. package/agent-guide/drafts-and-change-requests.md +92 -0
  5. package/agent-guide/for-git-users.md +47 -0
  6. package/agent-guide/glossary.md +36 -0
  7. package/agent-guide/how-agents-find-things.md +49 -0
  8. package/agent-guide/html-views.md +239 -8
  9. package/agent-guide/new-knowledge-base.md +9 -8
  10. package/agent-guide/skills.md +67 -42
  11. package/agent-guide/start-here.md +65 -0
  12. package/agent-guide/tool-manuals.md +133 -155
  13. package/agent-guide/tools.md +67 -0
  14. package/agent-guide/where-a-new-file-goes.md +45 -28
  15. package/dist/core/catalog-revision.d.ts +12 -1
  16. package/dist/core/catalog-revision.d.ts.map +1 -1
  17. package/dist/core/catalog-revision.js +23 -3
  18. package/dist/core/catalog-revision.js.map +1 -1
  19. package/dist/core/core-ports.d.ts +41 -0
  20. package/dist/core/core-ports.d.ts.map +1 -1
  21. package/dist/core/core-ports.js.map +1 -1
  22. package/dist/core/create-core-server.d.ts.map +1 -1
  23. package/dist/core/create-core-server.js +15 -8
  24. package/dist/core/create-core-server.js.map +1 -1
  25. package/dist/core/create-core-services.d.ts +22 -5
  26. package/dist/core/create-core-services.d.ts.map +1 -1
  27. package/dist/core/create-core-services.js +44 -16
  28. package/dist/core/create-core-services.js.map +1 -1
  29. package/dist/core/lifecycle.d.ts +16 -0
  30. package/dist/core/lifecycle.d.ts.map +1 -1
  31. package/dist/core/lifecycle.js +12 -2
  32. package/dist/core/lifecycle.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/modules/access/access-control.interface.d.ts +39 -9
  37. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  38. package/dist/modules/access/access-control.service.d.ts +15 -3
  39. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  40. package/dist/modules/access/access-control.service.js +239 -75
  41. package/dist/modules/access/access-control.service.js.map +1 -1
  42. package/dist/modules/access/access-mutation.service.d.ts +22 -24
  43. package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
  44. package/dist/modules/access/access-mutation.service.js +48 -51
  45. package/dist/modules/access/access-mutation.service.js.map +1 -1
  46. package/dist/modules/access/access-view.d.ts.map +1 -1
  47. package/dist/modules/access/access-view.js +12 -11
  48. package/dist/modules/access/access-view.js.map +1 -1
  49. package/dist/modules/access/access.routes.d.ts +3 -0
  50. package/dist/modules/access/access.routes.d.ts.map +1 -1
  51. package/dist/modules/access/access.routes.js +58 -33
  52. package/dist/modules/access/access.routes.js.map +1 -1
  53. package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
  54. package/dist/modules/access/groups-admin.routes.js +4 -2
  55. package/dist/modules/access/groups-admin.routes.js.map +1 -1
  56. package/dist/modules/access/groups-admin.service.d.ts +44 -13
  57. package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
  58. package/dist/modules/access/groups-admin.service.js +80 -38
  59. package/dist/modules/access/groups-admin.service.js.map +1 -1
  60. package/dist/modules/access/groups-edit.d.ts +1 -1
  61. package/dist/modules/access/groups-edit.d.ts.map +1 -1
  62. package/dist/modules/access/groups-edit.js +1 -1
  63. package/dist/modules/access/groups-edit.js.map +1 -1
  64. package/dist/modules/access/index.d.ts +1 -1
  65. package/dist/modules/access/index.d.ts.map +1 -1
  66. package/dist/modules/access/reference-scan.d.ts +116 -24
  67. package/dist/modules/access/reference-scan.d.ts.map +1 -1
  68. package/dist/modules/access/reference-scan.js +305 -44
  69. package/dist/modules/access/reference-scan.js.map +1 -1
  70. package/dist/modules/access/roles-admin.service.d.ts +17 -8
  71. package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
  72. package/dist/modules/access/roles-admin.service.js +32 -23
  73. package/dist/modules/access/roles-admin.service.js.map +1 -1
  74. package/dist/modules/access-model/access-grammar.d.ts +35 -10
  75. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  76. package/dist/modules/access-model/access-grammar.js +60 -20
  77. package/dist/modules/access-model/access-grammar.js.map +1 -1
  78. package/dist/modules/access-model/access-splice.d.ts +22 -18
  79. package/dist/modules/access-model/access-splice.d.ts.map +1 -1
  80. package/dist/modules/access-model/access-splice.js +20 -14
  81. package/dist/modules/access-model/access-splice.js.map +1 -1
  82. package/dist/modules/access-model/group-files.d.ts +1 -0
  83. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  84. package/dist/modules/access-model/group-files.js +5 -1
  85. package/dist/modules/access-model/group-files.js.map +1 -1
  86. package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
  87. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  88. package/dist/modules/agent-guide/agent-guide.js +103 -13
  89. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  90. package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
  91. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
  92. package/dist/modules/agent-guide/agent-guide.tools.js +46 -22
  93. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
  94. package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
  95. package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
  96. package/dist/modules/agent-guide/guide-budget.js +67 -0
  97. package/dist/modules/agent-guide/guide-budget.js.map +1 -0
  98. package/dist/modules/agent-guide/index.d.ts +2 -1
  99. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  100. package/dist/modules/agent-guide/index.js +2 -1
  101. package/dist/modules/agent-guide/index.js.map +1 -1
  102. package/dist/modules/agent-instructions/compose.d.ts +3 -1
  103. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  104. package/dist/modules/agent-instructions/compose.js +7 -1
  105. package/dist/modules/agent-instructions/compose.js.map +1 -1
  106. package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
  107. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  108. package/dist/modules/agent-instructions/shared-file-rules.js +26 -9
  109. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  110. package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
  111. package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
  112. package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
  113. package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
  114. package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
  115. package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
  116. package/dist/modules/agent-scope/connection-scope.js +165 -0
  117. package/dist/modules/agent-scope/connection-scope.js.map +1 -0
  118. package/dist/modules/agent-scope/index.d.ts +3 -0
  119. package/dist/modules/agent-scope/index.d.ts.map +1 -0
  120. package/dist/modules/agent-scope/index.js +3 -0
  121. package/dist/modules/agent-scope/index.js.map +1 -0
  122. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  123. package/dist/modules/audit/agent-audit.service.js +2 -0
  124. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  125. package/dist/modules/audit/audit.contract.d.ts +6 -0
  126. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  127. package/dist/modules/audit/audit.contract.js.map +1 -1
  128. package/dist/modules/auth/account-admission.d.ts +12 -0
  129. package/dist/modules/auth/account-admission.d.ts.map +1 -1
  130. package/dist/modules/auth/account-admission.js +15 -0
  131. package/dist/modules/auth/account-admission.js.map +1 -1
  132. package/dist/modules/auth/auth.middleware.d.ts +27 -1
  133. package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
  134. package/dist/modules/auth/auth.middleware.js +42 -4
  135. package/dist/modules/auth/auth.middleware.js.map +1 -1
  136. package/dist/modules/auth/auth.routes.d.ts +13 -9
  137. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  138. package/dist/modules/auth/auth.routes.js +35 -29
  139. package/dist/modules/auth/auth.routes.js.map +1 -1
  140. package/dist/modules/auth/auth.service.d.ts +55 -6
  141. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  142. package/dist/modules/auth/auth.service.js +128 -34
  143. package/dist/modules/auth/auth.service.js.map +1 -1
  144. package/dist/modules/auth/index.d.ts +1 -1
  145. package/dist/modules/auth/index.d.ts.map +1 -1
  146. package/dist/modules/auth/index.js +1 -1
  147. package/dist/modules/auth/index.js.map +1 -1
  148. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  149. package/dist/modules/auth/oidc-auth-provider.js +2 -9
  150. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  151. package/dist/modules/code-mode/code-mode.tool.js +2 -2
  152. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  153. package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
  154. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  155. package/dist/modules/connection-probe/connection-probe.service.js +1 -38
  156. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  157. package/dist/modules/database/core-schema.d.ts +68 -0
  158. package/dist/modules/database/core-schema.d.ts.map +1 -1
  159. package/dist/modules/database/core-schema.js +33 -0
  160. package/dist/modules/database/core-schema.js.map +1 -1
  161. package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
  162. package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
  163. package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
  164. package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
  165. package/dist/modules/embed/embed.tools.d.ts.map +1 -1
  166. package/dist/modules/embed/embed.tools.js +5 -0
  167. package/dist/modules/embed/embed.tools.js.map +1 -1
  168. package/dist/modules/kb-fs/index.d.ts +2 -0
  169. package/dist/modules/kb-fs/index.d.ts.map +1 -1
  170. package/dist/modules/kb-fs/index.js +1 -0
  171. package/dist/modules/kb-fs/index.js.map +1 -1
  172. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  173. package/dist/modules/kb-fs/locking-filesystem.js +7 -1
  174. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  175. package/dist/modules/kb-fs/repo-path.d.ts +26 -6
  176. package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
  177. package/dist/modules/kb-fs/repo-path.js +22 -7
  178. package/dist/modules/kb-fs/repo-path.js.map +1 -1
  179. package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
  180. package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
  181. package/dist/modules/kb-fs/tmp-ref.js +76 -0
  182. package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
  183. package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
  184. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  185. package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
  186. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  187. package/dist/modules/mcp/mcp.routes.d.ts +7 -1
  188. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  189. package/dist/modules/mcp/mcp.routes.js +39 -4
  190. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  191. package/dist/modules/mcp/mcp.service.d.ts +11 -0
  192. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  193. package/dist/modules/mcp/mcp.service.js +74 -48
  194. package/dist/modules/mcp/mcp.service.js.map +1 -1
  195. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
  196. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  197. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
  198. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  199. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
  200. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  201. package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
  202. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  203. package/dist/modules/plugins/index.d.ts +1 -1
  204. package/dist/modules/plugins/index.d.ts.map +1 -1
  205. package/dist/modules/plugins/index.js.map +1 -1
  206. package/dist/modules/plugins/plugin-links.d.ts +18 -2
  207. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  208. package/dist/modules/plugins/plugin-links.js.map +1 -1
  209. package/dist/modules/plugins/plugins.routes.d.ts +5 -1
  210. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  211. package/dist/modules/plugins/plugins.routes.js +32 -5
  212. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  213. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
  214. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  215. package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
  216. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  217. package/dist/modules/secrets-vault/index.d.ts +1 -1
  218. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  219. package/dist/modules/secrets-vault/index.js +1 -1
  220. package/dist/modules/secrets-vault/index.js.map +1 -1
  221. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
  222. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  223. package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
  224. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  225. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
  226. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  227. package/dist/modules/secrets-vault/secrets-vault.routes.js +100 -42
  228. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  229. package/dist/modules/settings/oidc-check.d.ts.map +1 -1
  230. package/dist/modules/settings/oidc-check.js +5 -17
  231. package/dist/modules/settings/oidc-check.js.map +1 -1
  232. package/dist/modules/skills/index.d.ts +3 -3
  233. package/dist/modules/skills/index.d.ts.map +1 -1
  234. package/dist/modules/skills/index.js +0 -2
  235. package/dist/modules/skills/index.js.map +1 -1
  236. package/dist/modules/skills/skills.contract.d.ts +16 -0
  237. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  238. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  239. package/dist/modules/skills/skills.service.js +6 -1
  240. package/dist/modules/skills/skills.service.js.map +1 -1
  241. package/dist/modules/skills/skills.tools.d.ts.map +1 -1
  242. package/dist/modules/skills/skills.tools.js +25 -13
  243. package/dist/modules/skills/skills.tools.js.map +1 -1
  244. package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
  245. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  246. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  247. package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
  248. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  249. package/dist/modules/tool-auth/external-api-key.service.js +4 -1
  250. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  251. package/dist/modules/tool-auth/index.d.ts +1 -1
  252. package/dist/modules/tool-auth/index.d.ts.map +1 -1
  253. package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
  254. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  255. package/dist/modules/tool-auth/internal-token.service.js +1 -0
  256. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  257. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
  258. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  259. package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
  260. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  261. package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
  262. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  263. package/dist/modules/tool-helpers/tool-context.js +3 -0
  264. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  265. package/dist/modules/tool-helpers/tool-def.js +1 -1
  266. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  267. package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
  268. package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
  269. package/dist/modules/tool-helpers/tool-handler.js +19 -0
  270. package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
  271. package/dist/modules/tool-helpers/tool.contract.d.ts +10 -0
  272. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  273. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  274. package/dist/modules/tool-helpers/validate-token.d.ts +3 -1
  275. package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
  276. package/dist/modules/tool-helpers/validate-token.js +1 -1
  277. package/dist/modules/tool-helpers/validate-token.js.map +1 -1
  278. package/dist/modules/tool-manuals/index.d.ts +3 -3
  279. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  280. package/dist/modules/tool-manuals/index.js +0 -2
  281. package/dist/modules/tool-manuals/index.js.map +1 -1
  282. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
  283. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  284. package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
  285. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  286. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +46 -6
  287. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  288. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
  289. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  290. package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
  291. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  292. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +24 -13
  293. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  294. package/dist/modules/tool-manuals/tool-manuals.service.js +138 -59
  295. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  296. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
  297. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  298. package/dist/modules/tool-manuals/tool-manuals.tools.js +56 -20
  299. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  300. package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
  301. package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
  302. package/dist/modules/tool-registry/manual.routes.js +27 -7
  303. package/dist/modules/tool-registry/manual.routes.js.map +1 -1
  304. package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
  305. package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
  306. package/dist/modules/tool-registry/tool.contract.js.map +1 -1
  307. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  308. package/dist/modules/workflow/agent-tools/workflow.tools.js +36 -27
  309. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  310. package/dist/modules/workflow/git/git.service.d.ts +2 -2
  311. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  312. package/dist/modules/workflow/git/git.service.js +20 -35
  313. package/dist/modules/workflow/git/git.service.js.map +1 -1
  314. package/dist/modules/workflow/workflow.service.d.ts +2 -1
  315. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  316. package/dist/modules/workflow/workflow.service.js +3 -0
  317. package/dist/modules/workflow/workflow.service.js.map +1 -1
  318. package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
  319. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
  320. package/dist/modules/workspace/agent-access.gate.js +1 -1
  321. package/dist/modules/workspace/agent-access.gate.js.map +1 -1
  322. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
  323. package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
  324. package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
  325. package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
  326. package/dist/modules/workspace/index.d.ts +8 -0
  327. package/dist/modules/workspace/index.d.ts.map +1 -1
  328. package/dist/modules/workspace/index.js +8 -0
  329. package/dist/modules/workspace/index.js.map +1 -1
  330. package/dist/modules/workspace/nul-scan.d.ts +19 -0
  331. package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
  332. package/dist/modules/workspace/nul-scan.js +55 -0
  333. package/dist/modules/workspace/nul-scan.js.map +1 -0
  334. package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
  335. package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
  336. package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
  337. package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
  338. package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
  339. package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
  340. package/dist/modules/workspace/ripgrep-search.js +657 -0
  341. package/dist/modules/workspace/ripgrep-search.js.map +1 -0
  342. package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
  343. package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
  344. package/dist/modules/workspace/ripgrep-version.js +31 -0
  345. package/dist/modules/workspace/ripgrep-version.js.map +1 -0
  346. package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
  347. package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
  348. package/dist/modules/workspace/startup/beside-checkout.js +14 -5
  349. package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
  350. package/dist/modules/workspace/worker-pool.d.ts +3 -4
  351. package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
  352. package/dist/modules/workspace/worker-pool.js.map +1 -1
  353. package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
  354. package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
  355. package/dist/modules/workspace/workspace-tmp.js +435 -0
  356. package/dist/modules/workspace/workspace-tmp.js.map +1 -0
  357. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  358. package/dist/modules/workspace/workspace.routes.js +5 -26
  359. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  360. package/dist/modules/workspace/workspace.service.d.ts +47 -6
  361. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  362. package/dist/modules/workspace/workspace.service.js +86 -6
  363. package/dist/modules/workspace/workspace.service.js.map +1 -1
  364. package/dist/modules/workspace/workspace.tools.d.ts +3 -2
  365. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  366. package/dist/modules/workspace/workspace.tools.js +531 -137
  367. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  368. package/dist/shared/oauth-token-error.d.ts +23 -0
  369. package/dist/shared/oauth-token-error.d.ts.map +1 -0
  370. package/dist/shared/oauth-token-error.js +47 -0
  371. package/dist/shared/oauth-token-error.js.map +1 -0
  372. package/dist/shared/path-order.d.ts +28 -8
  373. package/dist/shared/path-order.d.ts.map +1 -1
  374. package/dist/shared/path-order.js +37 -8
  375. package/dist/shared/path-order.js.map +1 -1
  376. package/dist/shared/read-capped.d.ts +19 -0
  377. package/dist/shared/read-capped.d.ts.map +1 -0
  378. package/dist/shared/read-capped.js +45 -0
  379. package/dist/shared/read-capped.js.map +1 -0
  380. package/migrations/0022_session_version.sql +6 -0
  381. package/migrations/0023_agent_path.sql +3 -0
  382. package/migrations/meta/0022_snapshot.json +2360 -0
  383. package/migrations/meta/0023_snapshot.json +2378 -0
  384. package/migrations/meta/_journal.json +14 -0
  385. package/package.json +4 -3
  386. package/src/__tests__/hexis-catalog.ts +144 -0
  387. package/src/__tests__/pg-scratch.test.ts +69 -0
  388. package/src/__tests__/pg-scratch.ts +31 -0
  389. package/src/__tests__/real-disk.ts +21 -0
  390. package/src/__tests__/slow-ripgrep.ts +65 -0
  391. package/src/__tests__/tokens.ts +19 -0
  392. package/src/__tests__/tool-manuals.ts +32 -0
  393. package/src/__tests__/tool-wiring.ts +25 -0
  394. package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
  395. package/src/core/__tests__/catalog-revision.test.ts +89 -0
  396. package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
  397. package/src/core/__tests__/gated-boot.test.ts +8 -1
  398. package/src/core/__tests__/lifecycle.test.ts +46 -13
  399. package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
  400. package/src/core/catalog-revision.ts +24 -2
  401. package/src/core/core-ports.ts +42 -0
  402. package/src/core/create-core-server.ts +14 -1
  403. package/src/core/create-core-services.ts +62 -15
  404. package/src/core/lifecycle.ts +26 -2
  405. package/src/index.ts +1 -1
  406. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
  407. package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
  408. package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
  409. package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
  410. package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
  411. package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
  412. package/src/modules/access/__tests__/access-groups.test.ts +55 -22
  413. package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
  414. package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
  415. package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
  416. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
  417. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
  418. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
  419. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  420. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
  421. package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -10
  422. package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
  423. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
  424. package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
  425. package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
  426. package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
  427. package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
  428. package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
  429. package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
  430. package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
  431. package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
  432. package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
  433. package/src/modules/access/access-control.interface.ts +43 -10
  434. package/src/modules/access/access-control.service.ts +250 -72
  435. package/src/modules/access/access-mutation.service.ts +48 -53
  436. package/src/modules/access/access-view.ts +12 -10
  437. package/src/modules/access/access.routes.ts +66 -37
  438. package/src/modules/access/groups-admin.routes.ts +4 -2
  439. package/src/modules/access/groups-admin.service.ts +114 -44
  440. package/src/modules/access/groups-edit.ts +1 -1
  441. package/src/modules/access/index.ts +1 -1
  442. package/src/modules/access/reference-scan.ts +319 -46
  443. package/src/modules/access/roles-admin.service.ts +43 -26
  444. package/src/modules/access/synced-groups-writer.ts +1 -1
  445. package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
  446. package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
  447. package/src/modules/access-model/access-grammar.ts +65 -20
  448. package/src/modules/access-model/access-splice.ts +31 -23
  449. package/src/modules/access-model/group-files.ts +5 -0
  450. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +110 -25
  451. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
  452. package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
  453. package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
  454. package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
  455. package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
  456. package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
  457. package/src/modules/agent-guide/agent-guide.tools.ts +51 -22
  458. package/src/modules/agent-guide/agent-guide.ts +120 -13
  459. package/src/modules/agent-guide/guide-budget.ts +73 -0
  460. package/src/modules/agent-guide/index.ts +12 -0
  461. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +5 -4
  462. package/src/modules/agent-instructions/compose.ts +7 -1
  463. package/src/modules/agent-instructions/shared-file-rules.ts +27 -9
  464. package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
  465. package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
  466. package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
  467. package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
  468. package/src/modules/agent-scope/connection-scope.ts +199 -0
  469. package/src/modules/agent-scope/index.ts +15 -0
  470. package/src/modules/audit/agent-audit.service.ts +2 -0
  471. package/src/modules/audit/audit.contract.ts +6 -0
  472. package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
  473. package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
  474. package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
  475. package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
  476. package/src/modules/auth/account-admission.ts +19 -0
  477. package/src/modules/auth/auth.middleware.ts +45 -4
  478. package/src/modules/auth/auth.routes.ts +46 -29
  479. package/src/modules/auth/auth.service.ts +135 -34
  480. package/src/modules/auth/index.ts +2 -0
  481. package/src/modules/auth/oidc-auth-provider.ts +2 -9
  482. package/src/modules/code-mode/code-mode.tool.ts +2 -2
  483. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
  484. package/src/modules/connection-probe/connection-probe.service.ts +1 -34
  485. package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
  486. package/src/modules/database/core-schema.ts +33 -0
  487. package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
  488. package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
  489. package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
  490. package/src/modules/embed/embed.tools.ts +5 -0
  491. package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
  492. package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
  493. package/src/modules/kb-fs/index.ts +2 -0
  494. package/src/modules/kb-fs/locking-filesystem.ts +7 -1
  495. package/src/modules/kb-fs/repo-path.ts +33 -7
  496. package/src/modules/kb-fs/tmp-ref.ts +81 -0
  497. package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
  498. package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
  499. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
  500. package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
  501. package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
  502. package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
  503. package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
  504. package/src/modules/mcp/mcp.routes.ts +39 -3
  505. package/src/modules/mcp/mcp.service.ts +112 -52
  506. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
  507. package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
  508. package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
  509. package/src/modules/plugins/index.ts +1 -1
  510. package/src/modules/plugins/plugin-links.ts +19 -2
  511. package/src/modules/plugins/plugins.routes.ts +35 -4
  512. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
  513. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
  514. package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
  515. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
  516. package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
  517. package/src/modules/secrets-vault/index.ts +1 -1
  518. package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
  519. package/src/modules/secrets-vault/secrets-vault.routes.ts +114 -42
  520. package/src/modules/settings/oidc-check.ts +5 -18
  521. package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
  522. package/src/modules/skills/index.ts +7 -2
  523. package/src/modules/skills/skills.contract.ts +17 -0
  524. package/src/modules/skills/skills.service.ts +5 -1
  525. package/src/modules/skills/skills.tools.ts +45 -14
  526. package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
  527. package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
  528. package/src/modules/tool-auth/external-api-key.service.ts +5 -2
  529. package/src/modules/tool-auth/index.ts +1 -1
  530. package/src/modules/tool-auth/internal-token.service.ts +10 -0
  531. package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
  532. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
  533. package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
  534. package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
  535. package/src/modules/tool-helpers/tool-context.ts +10 -0
  536. package/src/modules/tool-helpers/tool-def.ts +1 -1
  537. package/src/modules/tool-helpers/tool-handler.ts +34 -0
  538. package/src/modules/tool-helpers/tool.contract.ts +10 -0
  539. package/src/modules/tool-helpers/validate-token.ts +4 -2
  540. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
  541. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
  542. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
  543. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +99 -8
  544. package/src/modules/tool-manuals/index.ts +6 -2
  545. package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
  546. package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -6
  547. package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
  548. package/src/modules/tool-manuals/tool-manuals.service.ts +169 -61
  549. package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -25
  550. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
  551. package/src/modules/tool-registry/manual.routes.ts +26 -5
  552. package/src/modules/tool-registry/tool.contract.ts +7 -0
  553. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
  554. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
  555. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
  556. package/src/modules/workflow/agent-tools/workflow.tools.ts +36 -27
  557. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
  558. package/src/modules/workflow/git/git.service.ts +29 -48
  559. package/src/modules/workflow/workflow.service.ts +5 -0
  560. package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
  561. package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
  562. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
  563. package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
  564. package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
  565. package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
  566. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
  567. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
  568. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -12
  569. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
  570. package/src/modules/workspace/__tests__/workspace.tools.test.ts +549 -162
  571. package/src/modules/workspace/agent-access.gate.ts +1 -1
  572. package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
  573. package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
  574. package/src/modules/workspace/index.ts +20 -0
  575. package/src/modules/workspace/nul-scan.ts +50 -0
  576. package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
  577. package/src/modules/workspace/ripgrep-search.ts +715 -0
  578. package/src/modules/workspace/ripgrep-version.ts +35 -0
  579. package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
  580. package/src/modules/workspace/startup/beside-checkout.ts +13 -5
  581. package/src/modules/workspace/worker-pool.ts +3 -4
  582. package/src/modules/workspace/workspace-tmp.ts +457 -0
  583. package/src/modules/workspace/workspace.routes.ts +6 -27
  584. package/src/modules/workspace/workspace.service.ts +92 -15
  585. package/src/modules/workspace/workspace.tools.ts +574 -176
  586. package/src/shared/__tests__/read-capped.test.ts +68 -0
  587. package/src/shared/oauth-token-error.ts +46 -0
  588. package/src/shared/path-order.ts +37 -8
  589. package/src/shared/read-capped.ts +40 -0
  590. package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
  591. package/starter-packs/general/Plugins/general-starter/access.md +1 -1
  592. package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
  593. package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
  594. package/starter-packs/support/Plugins/support-starter/access.md +1 -1
  595. package/agent-guide/finding-things.md +0 -7
  596. package/agent-guide/introduction.md +0 -27
  597. package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
  598. package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
  599. package/dist/modules/workspace/bounded-matcher.js +0 -200
  600. package/dist/modules/workspace/bounded-matcher.js.map +0 -1
  601. package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
  602. package/src/modules/workspace/bounded-matcher.ts +0 -271
@@ -41,6 +41,16 @@ export const users = pgTable('users', {
41
41
  * until an admin (or the user, from their Account page) sets one.
42
42
  */
43
43
  passwordHash: text('password_hash'),
44
+ /**
45
+ * Which generation of this account's sessions is honoured. Every session
46
+ * token carries the value it was issued at (`sv`; a token from before the
47
+ * column existed carries none and counts as 0), and one whose value is not
48
+ * the row's is refused. Changing the password moves it on, so every session
49
+ * opened with the old password ends at its next request — the one that
50
+ * made the change is handed a fresh token. Connection keys are not
51
+ * sessions and do not look at it.
52
+ */
53
+ sessionVersion: integer('session_version').default(0).notNull(),
44
54
  /**
45
55
  * The one onboarding fact the server keeps: has this person concluded the
46
56
  * connect-your-agent setup (the welcome page's Done, or the reminder
@@ -277,6 +287,15 @@ export const externalApiKeys = pgTable('api_tokens', {
277
287
  * deleted in one step.
278
288
  */
279
289
  deletedAt: timestamp('deleted_at'),
290
+ /**
291
+ * The agent configuration the key connects as — a repo-relative knowledge-base
292
+ * path, chosen when the key is minted. Null is Everything: the key sees
293
+ * every tool, skill and plugin its owner may read, exactly as before the
294
+ * column existed. The path is the whole record: the file is re-read on
295
+ * every surface build, so editing it changes what the key sees without a
296
+ * new key (see `modules/agent-scope`).
297
+ */
298
+ agentPath: text('agent_path'),
280
299
  }, (t) => ({
281
300
  byUser: index('api_tokens_by_user').on(t.userId),
282
301
  }));
@@ -486,6 +505,12 @@ export const oauthAuthCodes = pgTable('oauth_auth_codes', {
486
505
  expiresAt: timestamp('expires_at').notNull(),
487
506
  consumedAt: timestamp('consumed_at'),
488
507
  createdAt: timestamp('created_at').defaultNow().notNull(),
508
+ /**
509
+ * The "Connect as" choice made on the consent step, carried from Finish to
510
+ * the token exchange that makes (or finds) the agent connection — the code
511
+ * is the only thing that travels between the two. Null is Everything.
512
+ */
513
+ agentPath: text('agent_path'),
489
514
  }, (t) => ({
490
515
  // Sweep expired/spent rows without a full scan.
491
516
  byExpiry: index('oauth_auth_codes_by_expiry').on(t.expiresAt),
@@ -525,6 +550,14 @@ export const agentConnections = pgTable('agent_connections', {
525
550
  revokedAt: timestamp('revoked_at'),
526
551
  /** `owner` | `admin` — who ended it; null while live. Same vocabulary as `api_tokens.revoked_by`. */
527
552
  revokedBy: text('revoked_by'),
553
+ /**
554
+ * The agent configuration the person chose to connect as on the consent step — a
555
+ * repo-relative knowledge-base path — or null for Everything. Set by every
556
+ * consent that lands on the row, so re-authorising with another choice
557
+ * changes what the agent sees from its next request. Same meaning as
558
+ * `api_tokens.agent_path`.
559
+ */
560
+ agentPath: text('agent_path'),
528
561
  }, (t) => ({
529
562
  byUser: index('agent_connections_by_user').on(t.userId),
530
563
  liveUnq: uniqueIndex('agent_connections_live_unq')
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
4
4
  import { createDeclaredVariableRoutes } from '../declared-variables.routes.js';
5
5
  import type { IToolManualService, ToolManualSummary } from '../../tool-manuals/tool-manuals.contract.js';
6
6
  import type { ISecretsVaultService } from '../../secrets-vault/secrets-vault.contract.js';
7
+ import { ConnectionScope, type IAgentScopeResolver } from '../../agent-scope/index.js';
7
8
 
8
9
  /**
9
10
  * The one core route that releases secret VALUES. What is worth pinning here is
@@ -46,6 +47,9 @@ async function mount(opts: {
46
47
  source?: 'session' | 'external' | 'internal';
47
48
  manuals?: ToolManualSummary[];
48
49
  secrets?: Record<string, string>;
50
+ /** The `.agent` file the caller's connection was made as; absent is Everything. */
51
+ agentPath?: string;
52
+ agentScope?: Pick<IAgentScopeResolver, 'resolve'>;
49
53
  }): Promise<Harness> {
50
54
  const userId = opts.userId ?? 'user-1';
51
55
  const source = opts.source ?? 'external';
@@ -64,10 +68,18 @@ async function mount(opts: {
64
68
  toolManualService,
65
69
  secretsVault,
66
70
  (req, _res, next) => {
67
- if (userId) req.toolAuth = { userId, source, scope: 'write' } as typeof req.toolAuth;
71
+ if (userId) {
72
+ req.toolAuth = {
73
+ userId,
74
+ source,
75
+ scope: 'write',
76
+ ...(opts.agentPath ? { agentPath: opts.agentPath } : {}),
77
+ } as typeof req.toolAuth;
78
+ }
68
79
  next();
69
80
  },
70
81
  async () => 'runner@x.eu',
82
+ opts.agentScope,
71
83
  ),
72
84
  );
73
85
  httpServer = await new Promise<HttpServer>((r) => {
@@ -133,6 +145,37 @@ describe('POST /agent/local-tools/:slug/variables', () => {
133
145
  expect(resolve).not.toHaveBeenCalled();
134
146
  });
135
147
 
148
+ it("releases only the variables of a connector the connection's .agent file loads", async () => {
149
+ const agent = { path: 'Agents/ops.agent', name: 'Ops' };
150
+ const loading = (connectors: string[]) => ({
151
+ resolve: async () => ConnectionScope.narrowed(agent, { skills: [], connectors, platformTools: null }, 'fp'),
152
+ });
153
+ // A folder entry over the tool's plugin loads it.
154
+ const inScope = await mount({
155
+ manuals: [GIT_TOOL],
156
+ secrets: { git_GITHUB_TOKEN: 'ghp_secret' },
157
+ agentPath: agent.path,
158
+ agentScope: loading(['Plugins/Engineering']),
159
+ });
160
+ expect((await fetch(`${inScope.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(200);
161
+ await new Promise<void>((r) => httpServer!.close(() => r()));
162
+
163
+ // A readable tool the file does not load is as absent as an unreadable one.
164
+ const outOfScope = await mount({
165
+ manuals: [GIT_TOOL],
166
+ secrets: { git_GITHUB_TOKEN: 'ghp_secret' },
167
+ agentPath: agent.path,
168
+ agentScope: loading(['Plugins/billing.tool']),
169
+ });
170
+ expect((await fetch(`${outOfScope.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(404);
171
+ expect(outOfScope.resolve).not.toHaveBeenCalled();
172
+ await new Promise<void>((r) => httpServer!.close(() => r()));
173
+
174
+ // A narrowed connection with no resolver wired loads nothing.
175
+ const unwired = await mount({ manuals: [GIT_TOOL], agentPath: agent.path });
176
+ expect((await fetch(`${unwired.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(404);
177
+ });
178
+
136
179
  it('403s an unauthenticated caller', async () => {
137
180
  const { base } = await mount({ userId: '', manuals: [GIT_TOOL] });
138
181
  expect((await fetch(`${base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(403);
@@ -5,6 +5,7 @@ const log = logger('declared-variables');
5
5
  import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
6
6
  import type { IToolManualService } from '../tool-manuals/index.js';
7
7
  import type { ISecretsVaultService } from '../secrets-vault/index.js';
8
+ import { resolveConnectionScope, type IAgentScopeResolver } from '../agent-scope/index.js';
8
9
 
9
10
  type ResolveUserEmail = (userId: string) => Promise<string | undefined>;
10
11
 
@@ -48,6 +49,12 @@ export function createDeclaredVariableRoutes(
48
49
  secretsVault: ISecretsVaultService,
49
50
  manualAuth: RequestHandler,
50
51
  resolveUserEmail: ResolveUserEmail,
52
+ /**
53
+ * Resolves the scope of a connection made as an agent configuration: such a
54
+ * connection is handed only the variables of a connector the file loads.
55
+ * Absent, a narrowed connection is handed none.
56
+ */
57
+ agentScope?: Pick<IAgentScopeResolver, 'resolve'>,
51
58
  ): express.Router {
52
59
  const router = express.Router();
53
60
 
@@ -125,7 +132,11 @@ export function createDeclaredVariableRoutes(
125
132
  // `listAccessible` already applies the per-file read verdict, so an
126
133
  // unreadable manual is simply absent — same fail-closed read model the
127
134
  // catalog uses.
128
- const manual = (await toolManualService.listAccessible(who.email)).find((m) => m.slug === slug);
135
+ const found = (await toolManualService.listAccessible(who.email)).find((m) => m.slug === slug);
136
+ // A connection made as an agent configuration is handed only the variables of
137
+ // a connector it loads; another is as absent as an unreadable one.
138
+ const scope = found ? await resolveConnectionScope(agentScope, who.email, req.toolAuth?.agentPath) : null;
139
+ const manual = found && scope?.allowsConnector(found.path) ? found : undefined;
129
140
  if (!manual) return void res.status(404).json({ error: 'Not found' });
130
141
  if (manual.remote !== false) {
131
142
  return void res.status(409).json({
@@ -281,6 +281,18 @@ describe('open_page: refusals', () => {
281
281
  expect(embedService.mintForUser).not.toHaveBeenCalled();
282
282
  });
283
283
 
284
+ it('refuses a tmp ref — no knowledge-base page — reading and minting nothing', async () => {
285
+ const read = vi.fn<ReadForTool>();
286
+ const { call, embedService } = await serve({ read });
287
+ const { status, body } = await call({ path: '__tmp__/chat-uploads/plan.xlsx' });
288
+ expect(status).toBe(400);
289
+ expect(body.kind).toBe('tmp-ref-read-only');
290
+ expect(body.error).toContain('open_page does not take one there');
291
+ expect(body.error).toContain('copy_file');
292
+ expect(read).not.toHaveBeenCalled();
293
+ expect(embedService.mintForUser).not.toHaveBeenCalled();
294
+ });
295
+
284
296
  it('refuses a path outside the knowledge base, and a missing one', async () => {
285
297
  const { call, embedService } = await serve();
286
298
  expect((await call({ path: '' })).status).toBe(400);
@@ -6,6 +6,7 @@ import type { KbContext } from '../../shared/kb-context.js';
6
6
  import type { ReadForTool } from '../workspace/index.js';
7
7
  import type { IEmbedService } from './embed.interface.js';
8
8
  import { requireExternalSource } from '../tool-auth/index.js';
9
+ import { assertTmpRefsTaken } from '../kb-fs/index.js';
9
10
  import { SESSION_ID_INPUT, type ToolDescriptionNotes, sessionIdInputOf } from '../workspace/index.js';
10
11
 
11
12
  /** The tool name, in one place: the app manifest keys its view by it. */
@@ -149,6 +150,10 @@ export function registerEmbedTools(
149
150
  toolHandler(async (args, ctx: ToolContext) => {
150
151
  const raw = typeof args.path === 'string' ? args.path.trim() : '';
151
152
  if (!raw) throw new ToolError('`path` is required.', 400);
153
+ // A tmp ref (`__tmp__/…`) is no knowledge-base page: refused by the one
154
+ // admission gate the normalised tools pass, before `toRepoRelative`
155
+ // would read it as the repository path `<kbDirName>/__tmp__/…`.
156
+ assertTmpRefsTaken(OPEN_PAGE_TOOL, { path: raw });
152
157
  const heading = typeof args.heading === 'string' && args.heading.trim() ? args.heading.trim() : undefined;
153
158
  const repoRelative = toRepoRelative(raw, kbDirName);
154
159
  if (repoRelative === null) throw new ToolError(OUTSIDE_REPO(kbDirName), 400);
@@ -1482,6 +1482,31 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
1482
1482
  expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
1483
1483
  });
1484
1484
 
1485
+ /**
1486
+ * The same race for an EXCLUSIVE write (`overwrite: false`), which is how
1487
+ * `copy_file` lands bytes read out of the workspace's tmp folder: the
1488
+ * refusal is the clash sentence, and the lock releases untouched rather
1489
+ * than discarding the winner's queued file.
1490
+ */
1491
+ it('an exclusive write onto a taken name is the same refusal, and releases UNTOUCHED', async () => {
1492
+ const workflow = makeWorkflow();
1493
+ const layer = new LockingFilesystem(
1494
+ { basePath: root, contained: true },
1495
+ { workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
1496
+ );
1497
+ await fs.writeFile(onDisk(TAKEN), 'the winner of the race');
1498
+
1499
+ await expect(layer.writeFile(TAKEN, 'the loser', { overwrite: false })).rejects.toMatchObject({
1500
+ name: 'DestinationTakenError',
1501
+ message: 'A file named taken.md already exists in KnowledgeBase.',
1502
+ });
1503
+
1504
+ expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', TAKEN, USER);
1505
+ expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
1506
+ expect(workflow.releaseLock).not.toHaveBeenCalled();
1507
+ expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
1508
+ });
1509
+
1485
1510
  it.skipIf(process.platform === 'win32')('answers the clash sentence when a DANGLING link holds the name', async () => {
1486
1511
  // A link to nothing still owns the directory entry, so the exclusive
1487
1512
  // create fails — and an existence probe that follows links would say the
@@ -231,8 +231,8 @@ describe('a line break in a path — one name to the gates, two object names to
231
231
  });
232
232
 
233
233
  it('refuses it through normalizePathArgs, which is where the MCP tools meet the rule', () => {
234
- expect(() => normalizePathArgs({ path: INJECTED }, KB)).toThrow(/control character/);
235
- expect(() => normalizePathArgs({ files: [{ path: INJECTED }] }, KB)).toThrow(/control character/);
234
+ expect(() => normalizePathArgs({ path: INJECTED }, KB, { tool: 'read_file' })).toThrow(/control character/);
235
+ expect(() => normalizePathArgs({ files: [{ path: INJECTED }] }, KB, { tool: 'read_file' })).toThrow(/control character/);
236
236
  });
237
237
  });
238
238
 
@@ -360,6 +360,8 @@ describe('assertRepoRootNameFree', () => {
360
360
  });
361
361
 
362
362
  describe('normalizePathArgs', () => {
363
+ const T = { tool: 'read_file' };
364
+
363
365
  it('normalizes every path-shaped tool argument, batch entries included', () => {
364
366
  expect(
365
367
  normalizePathArgs(
@@ -372,6 +374,7 @@ describe('normalizePathArgs', () => {
372
374
  files: [{ path: 'e.md', content: '/x' }],
373
375
  },
374
376
  KB,
377
+ T,
375
378
  ),
376
379
  ).toEqual({
377
380
  branch: '/main',
@@ -385,22 +388,64 @@ describe('normalizePathArgs', () => {
385
388
 
386
389
  it('leaves a spill ref alone — it belongs to no workspace', () => {
387
390
  expect(
388
- normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, (v) => v.startsWith('__tool_chain_spill__/')),
391
+ normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, { tool: 'read_file', skip: (v) => v.startsWith('__tool_chain_spill__/') }),
389
392
  ).toEqual({ path: '__tool_chain_spill__/abc.json' });
390
393
  });
391
394
 
395
+ it('still takes the earlier call form, a skip predicate or nothing, and refuses a tmp ref in every input there', () => {
396
+ const spill = (v: string) => v.startsWith('__tool_chain_spill__/');
397
+ expect(normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, spill)).toEqual({ path: '__tool_chain_spill__/abc.json' });
398
+ expect(normalizePathArgs({ path: 'a.md' }, KB)).toEqual({ path: 'knowledge-base/a.md' });
399
+ for (const legacy of [undefined, spill]) {
400
+ expect(() => normalizePathArgs({ path: '__tmp__/x.md' }, KB, legacy)).toThrow(/this tool does not take one there/);
401
+ }
402
+ });
403
+
392
404
  it('leaves an absent or empty path to the handler\'s own required-argument message', () => {
393
- expect(normalizePathArgs({ path: undefined }, KB)).toEqual({ path: undefined });
394
- expect(normalizePathArgs({ path: '' }, KB)).toEqual({ path: '' });
395
- expect(normalizePathArgs({ branch: 'main' }, KB)).toEqual({ branch: 'main' });
405
+ expect(normalizePathArgs({ path: undefined }, KB, T)).toEqual({ path: undefined });
406
+ expect(normalizePathArgs({ path: '' }, KB, T)).toEqual({ path: '' });
407
+ expect(normalizePathArgs({ branch: 'main' }, KB, T)).toEqual({ branch: 'main' });
396
408
  });
397
409
 
398
410
  it('collapses the spellings so two callers take one lock row', () => {
399
- expect(normalizePathArgs({ path: './knowledge-base//a.md' }, KB)).toEqual({ path: 'knowledge-base/a.md' });
411
+ expect(normalizePathArgs({ path: './knowledge-base//a.md' }, KB, T)).toEqual({ path: 'knowledge-base/a.md' });
412
+ });
413
+
414
+ it('admits a tmp ref, as written, in exactly the inputs the tool names', () => {
415
+ expect(normalizePathArgs({ src: '__tmp__/a.pdf', dest: 'a.pdf' }, KB, { tool: 'copy_file', tmpRefInputs: ['src'] })).toEqual({
416
+ src: '__tmp__/a.pdf',
417
+ dest: 'knowledge-base/a.pdf',
418
+ });
419
+ });
420
+
421
+ it('refuses a tmp ref in every other input, lists included, naming the tool and copy_file', () => {
422
+ const calls: Array<Record<string, unknown>> = [
423
+ { path: '__tmp__/a.md' },
424
+ { src: '__tmp__/a.md' },
425
+ { dest: '__tmp__/a.md' },
426
+ { destination: '__tmp__' },
427
+ { files: [{ path: 'ok.md' }, { path: '__tmp__/a.md' }] },
428
+ { paths: ['ok.md', '__tmp__/a.md'] },
429
+ { resolutions: [{ path: 'ok.md', choose: 'ours' }, { path: '__tmp__/a.md', content: 'x' }] },
430
+ ];
431
+ for (const args of calls) {
432
+ expect(() => normalizePathArgs(args, KB, { tool: 'save_file' })).toThrow(
433
+ /is a tmp ref, and save_file does not take one there: the workspace's tmp folder is read-only through the ref\..*copy_file/,
434
+ );
435
+ }
436
+ // A tool that takes a ref in `src` still refuses one in `dest`.
437
+ expect(() => normalizePathArgs({ dest: '__tmp__/a.md' }, KB, { tool: 'copy_file', tmpRefInputs: ['src'] })).toThrow(
438
+ /copy_file does not take one there/,
439
+ );
440
+ });
441
+
442
+ it('reads a path that only contains the prefix as an ordinary repository path', () => {
443
+ expect(normalizePathArgs({ path: 'knowledge-base/__tmp__/a.md' }, KB, T)).toEqual({ path: 'knowledge-base/__tmp__/a.md' });
444
+ expect(normalizePathArgs({ path: '/knowledge-base/__tmp__/a.md' }, KB, T)).toEqual({ path: 'knowledge-base/__tmp__/a.md' });
400
445
  });
401
446
 
402
447
  it('refuses a traversing tool argument', () => {
403
- expect(() => normalizePathArgs({ path: '../escape.md' }, KB)).toThrow(/outside the knowledge base repository/);
448
+ expect(() => normalizePathArgs({ path: '../escape.md' }, KB, T)).toThrow(/outside the knowledge base repository/);
404
449
  });
405
450
  });
406
451
 
@@ -8,6 +8,7 @@ export { WorkspaceMutex, WorkspaceIdMutex } from './mutex.js';
8
8
  export { readRefSha, readRevSha, type RefFileAnswer } from './ref-files.js';
9
9
  export { gitDirOf, readGitfile } from './git-dir.js';
10
10
  export { normalizeWorkspacePath } from './repo-path.js';
11
+ export { TMP_REF_PREFIX, assertTmpRefsTaken, isTmpRef, tmpRefReadOnlyRefusal } from './tmp-ref.js';
11
12
  // Reached by other modules; exported here so they stop importing the files.
12
13
  export { BevelIgnoreStack } from './bevel-ignore.js';
13
14
  export { assertValidBranchName, assertValidRelativePath, isBranchAuthoredBy, isOwnSuggestionsBranch } from './branch-name.js';
@@ -18,3 +19,4 @@ export { LockingFilesystem, MoveLockedError, MoveRacedError } from './locking-fi
18
19
  export { ReadOnlyFilesystem } from './read-only-filesystem.js';
19
20
  export { normalizeRepositoryAddress, sameRepository } from './remote-url.js';
20
21
  export { assertInsideRepo, assertKbDirNameFree, assertRepoRootNameFree, assertRepoRootNameFreeArgs, hasControlCharacter, isInsideRepo, normalizePathArgs } from './repo-path.js';
22
+ export type { NormalizePathArgsOptions } from './repo-path.js';
@@ -226,9 +226,15 @@ export class LockingFilesystem extends GitGuardedFilesystem {
226
226
  const plan = await this.planCreate(inputPath, 'file');
227
227
  if (plan?.kind === 'seed-access-md') await this.seedAccessMd(plan);
228
228
  const toWrite = content;
229
+ // An EXCLUSIVE write (`overwrite: false`, the `wx` flag) that finds the
230
+ // name taken is refused as a copy is (`asTakenRefusal`): the refusal is a
231
+ // `DestinationTakenError`, so the lock is released untouched. Left as the
232
+ // raw failure it would be a failed op, and the discard that follows resets
233
+ // the PATH — throwing away the winner's file, whose commit is still queued.
234
+ const land = () => super.writeFile(inputPath, toWrite, options);
229
235
  return this.withLock(
230
236
  inputPath,
231
- () => super.writeFile(inputPath, toWrite, options),
237
+ () => (options?.overwrite === false ? this.asTakenRefusal(inputPath, land) : land()),
232
238
  (check || validate) &&
233
239
  (async () => {
234
240
  // The caller's verdict first: it decides whether this write happens
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { WorkflowValidationError } from '../../shared/domain-errors.js';
3
3
  import { sanitizedPath } from '../../shared/printable.js';
4
+ import { assertTmpRefsTaken, isTmpRef } from './tmp-ref.js';
4
5
 
5
6
  /**
6
7
  * Workspace-relative paths and the repository folder.
@@ -312,6 +313,16 @@ export function assertKbDirNameFree(
312
313
  );
313
314
  }
314
315
 
316
+ /** How {@link normalizePathArgs} treats one tool's call. */
317
+ export interface NormalizePathArgsOptions {
318
+ /** The tool the call is to, named in a tmp-ref refusal. */
319
+ tool: string;
320
+ /** The inputs of `tool` that take a tmp ref (`__tmp__/…`); every other refuses one. */
321
+ tmpRefInputs?: readonly string[];
322
+ /** A value that is no workspace path at all, passed to the handler as written. */
323
+ skip?: (value: string) => boolean;
324
+ }
325
+
315
326
  /** The tool inputs that carry a workspace path. */
316
327
  const PATH_ARG_KEYS = ['path', 'src', 'dest', 'destination'] as const;
317
328
 
@@ -321,19 +332,34 @@ const PATH_ARG_KEYS = ['path', 'src', 'dest', 'destination'] as const;
321
332
  * copy. This is where the MCP surface meets the normaliser — one call, before
322
333
  * any handler runs, so no tool keeps a resolver of its own.
323
334
  *
324
- * `skip` is for the one input that is not a workspace path at all: `read_file`
325
- * also takes a `__tool_chain_spill__/…` ref, which belongs to no workspace and
326
- * must reach the handler as written. An absent or non-string value is left
327
- * alone too — the handler's own required-argument message is better than a
328
- * path refusal about `undefined`.
335
+ * It is also where a tmp ref (`__tmp__/…`) is admitted or refused, first:
336
+ * {@link assertTmpRefsTaken} refuses one in every input of `tool` that
337
+ * `tmpRefInputs` does not name, and a ref in an input that does name it
338
+ * reaches the handler as written — so no tool whose paths pass through here
339
+ * can read the ref as the repository path `<kbDirName>/__tmp__/…`.
340
+ *
341
+ * `skip` is for the other input that is not a workspace path at all:
342
+ * `read_file` also takes a `__tool_chain_spill__/…` ref, which belongs to no
343
+ * workspace and must reach the handler as written. An absent or non-string
344
+ * value is left alone too — the handler's own required-argument message is
345
+ * better than a path refusal about `undefined`.
346
+ *
347
+ * The earlier call form — a `skip` predicate (or nothing) as the third
348
+ * argument — is still taken, for a distribution's own tool written against
349
+ * it: such a call names no tool and takes a tmp ref in no input, so the ref is
350
+ * refused there as read-only, under the name "this tool".
329
351
  */
330
352
  export function normalizePathArgs(
331
353
  args: Record<string, unknown>,
332
354
  kbDirName: string,
333
- skip?: (value: string) => boolean,
355
+ options?: NormalizePathArgsOptions | ((value: string) => boolean),
334
356
  ): Record<string, unknown> {
357
+ const { tool, tmpRefInputs, skip } =
358
+ typeof options === 'object' ? options : { tool: 'this tool', tmpRefInputs: undefined, skip: options };
359
+ assertTmpRefsTaken(tool, args, tmpRefInputs);
360
+ // Every tmp ref still here is in an input that takes one.
335
361
  const one = (value: unknown): unknown =>
336
- typeof value !== 'string' || value.length === 0 || skip?.(value)
362
+ typeof value !== 'string' || value.length === 0 || isTmpRef(value) || skip?.(value)
337
363
  ? value
338
364
  : normalizeWorkspacePath(value, kbDirName);
339
365
  const out: Record<string, unknown> = { ...args };
@@ -0,0 +1,81 @@
1
+ import { WorkflowValidationError } from '../../shared/domain-errors.js';
2
+ import { sanitizedPath } from '../../shared/printable.js';
3
+
4
+ /**
5
+ * The tmp ref's admission: which tool inputs may carry one, and the refusal
6
+ * every other input answers. What the ref REACHES is `workspace/workspace-tmp.ts`'s;
7
+ * whether a tool may be HANDED one is decided here, once, by
8
+ * {@link assertTmpRefsTaken} — which {@link normalizePathArgs} runs before it
9
+ * places any path under the checkout, so no tool surface that takes a path
10
+ * can turn `__tmp__/x` into the repository path `<kbDirName>/__tmp__/x` and
11
+ * write, read or show it as if the ref had been honoured. A surface that
12
+ * resolves its paths without the normaliser (`open_page`) calls the gate
13
+ * itself.
14
+ */
15
+
16
+ /** The ref's prefix. A ref is `__tmp__/<path inside the workspace's tmp folder>`. */
17
+ export const TMP_REF_PREFIX = '__tmp__/';
18
+
19
+ /**
20
+ * Whether a tool argument is a tmp ref rather than a repository path: the
21
+ * prefix as written, or the prefix's own folder name alone (which names the
22
+ * folder, and is refused as such by the resolver). A path that only CONTAINS
23
+ * the name — `/__tmp__/x`, `knowledge-base/__tmp__/x` — is an ordinary
24
+ * repository path.
25
+ */
26
+ export function isTmpRef(value: unknown): boolean {
27
+ return typeof value === 'string' && (value === TMP_REF_PREFIX.slice(0, -1) || value.startsWith(TMP_REF_PREFIX));
28
+ }
29
+
30
+ /**
31
+ * Why a tool refuses a tmp ref in an input that is not one of the four that
32
+ * take it: the folder is read-only through the ref, and a file is kept by
33
+ * copying it into the repository. A 400 with a `kind` to branch on, before
34
+ * any gate, lock or write.
35
+ */
36
+ export function tmpRefReadOnlyRefusal(ref: string, tool: string): WorkflowValidationError {
37
+ const shown = sanitizedPath(ref);
38
+ return new WorkflowValidationError(
39
+ `"${shown}" is a tmp ref, and ${tool} does not take one there: the workspace's tmp folder is read-only through ` +
40
+ 'the ref. read_file, file_stat and grep read the one file it names; to keep the file, copy_file it to a ' +
41
+ 'repository path (the ref as `src`, the repository path as `dest`).',
42
+ { kind: 'tmp-ref-read-only', path: shown, tool },
43
+ );
44
+ }
45
+
46
+ /** The tool inputs that carry a workspace path as a string. */
47
+ const PATH_INPUTS = ['path', 'src', 'dest', 'destination'] as const;
48
+
49
+ /**
50
+ * Every path a tool call carries, with the input it came in: the string
51
+ * inputs, and for EVERY list input, each entry that is a path itself
52
+ * (`paths` of `request_file_download`) or an object carrying one in `path`
53
+ * (`files[].path` of a batch write, `resolutions[].path` of
54
+ * `update_branch`). Read by shape rather than by a list of tool inputs, so a
55
+ * list a tool adds later is gated without anyone remembering to name it here.
56
+ */
57
+ function pathEntries(args: Record<string, unknown>): Array<[string, unknown]> {
58
+ const entries: Array<[string, unknown]> = PATH_INPUTS.map((key) => [key, args[key]]);
59
+ for (const [key, value] of Object.entries(args)) {
60
+ if (!Array.isArray(value)) continue;
61
+ for (const item of value as unknown[]) {
62
+ if (typeof item === 'string') {
63
+ if (key === 'paths') entries.push([key, item]);
64
+ } else if (item && typeof item === 'object') {
65
+ entries.push([key, (item as Record<string, unknown>).path]);
66
+ }
67
+ }
68
+ }
69
+ return entries;
70
+ }
71
+
72
+ /**
73
+ * Refuse a tmp ref in any input of `tool` that is not in `takes` — with
74
+ * {@link tmpRefReadOnlyRefusal}, before anything else runs. `takes` is empty
75
+ * for every tool but the four reads of the one file a ref names.
76
+ */
77
+ export function assertTmpRefsTaken(tool: string, args: Record<string, unknown>, takes: readonly string[] = []): void {
78
+ for (const [key, value] of pathEntries(args)) {
79
+ if (isTmpRef(value) && !takes.includes(key)) throw tmpRefReadOnlyRefusal(value as string, tool);
80
+ }
81
+ }