@bevel-software/platform-core-backend 0.30.0 → 0.32.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 (210) hide show
  1. package/agent-guide/access-control.md +49 -4
  2. package/agent-guide/html-views.md +76 -2
  3. package/dist/core/catalog-revision.js +3 -3
  4. package/dist/core/create-core-server.d.ts.map +1 -1
  5. package/dist/core/create-core-server.js +2 -1
  6. package/dist/core/create-core-server.js.map +1 -1
  7. package/dist/core/create-core-services.d.ts +7 -0
  8. package/dist/core/create-core-services.d.ts.map +1 -1
  9. package/dist/core/create-core-services.js +16 -3
  10. package/dist/core/create-core-services.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  16. package/dist/modules/access/access-control.service.js +1 -5
  17. package/dist/modules/access/access-control.service.js.map +1 -1
  18. package/dist/modules/access-model/index.d.ts +14 -0
  19. package/dist/modules/access-model/index.d.ts.map +1 -0
  20. package/dist/modules/access-model/index.js +13 -0
  21. package/dist/modules/access-model/index.js.map +1 -0
  22. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  23. package/dist/modules/audit/agent-audit.service.js +66 -6
  24. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  25. package/dist/modules/audit/audit.contract.d.ts +52 -8
  26. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  27. package/dist/modules/audit/audit.contract.js +37 -1
  28. package/dist/modules/audit/audit.contract.js.map +1 -1
  29. package/dist/modules/audit/audit.routes.d.ts +1 -1
  30. package/dist/modules/audit/audit.routes.d.ts.map +1 -1
  31. package/dist/modules/audit/audit.routes.js +15 -13
  32. package/dist/modules/audit/audit.routes.js.map +1 -1
  33. package/dist/modules/audit/request-audit.d.ts +21 -10
  34. package/dist/modules/audit/request-audit.d.ts.map +1 -1
  35. package/dist/modules/audit/request-audit.js +26 -10
  36. package/dist/modules/audit/request-audit.js.map +1 -1
  37. package/dist/modules/auth/account-hooks.d.ts +4 -6
  38. package/dist/modules/auth/account-hooks.d.ts.map +1 -1
  39. package/dist/modules/auth/account-hooks.js +7 -17
  40. package/dist/modules/auth/account-hooks.js.map +1 -1
  41. package/dist/modules/auth/auth.service.d.ts +9 -8
  42. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  43. package/dist/modules/auth/auth.service.js +19 -18
  44. package/dist/modules/auth/auth.service.js.map +1 -1
  45. package/dist/modules/database/core-schema.d.ts +43 -4
  46. package/dist/modules/database/core-schema.d.ts.map +1 -1
  47. package/dist/modules/database/core-schema.js +31 -7
  48. package/dist/modules/database/core-schema.js.map +1 -1
  49. package/dist/modules/kb-fs/index.d.ts +1 -0
  50. package/dist/modules/kb-fs/index.d.ts.map +1 -1
  51. package/dist/modules/kb-fs/index.js +1 -0
  52. package/dist/modules/kb-fs/index.js.map +1 -1
  53. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  54. package/dist/modules/kb-fs/locking-filesystem.js +3 -12
  55. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  56. package/dist/modules/kb-fs/mutex.d.ts +11 -0
  57. package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
  58. package/dist/modules/kb-fs/mutex.js +14 -0
  59. package/dist/modules/kb-fs/mutex.js.map +1 -1
  60. package/dist/modules/mcp/mcp-auth.middleware.d.ts +14 -0
  61. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  62. package/dist/modules/mcp/mcp-auth.middleware.js +4 -0
  63. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  64. package/dist/modules/mcp/mcp.routes.d.ts +5 -0
  65. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  66. package/dist/modules/mcp/mcp.routes.js +67 -0
  67. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  68. package/dist/modules/mcp/mcp.service.d.ts +107 -27
  69. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  70. package/dist/modules/mcp/mcp.service.js +257 -50
  71. package/dist/modules/mcp/mcp.service.js.map +1 -1
  72. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +28 -0
  73. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  74. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +166 -39
  75. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  76. package/dist/modules/mcp/request-context.d.ts +25 -0
  77. package/dist/modules/mcp/request-context.d.ts.map +1 -0
  78. package/dist/modules/mcp/request-context.js +11 -0
  79. package/dist/modules/mcp/request-context.js.map +1 -0
  80. package/dist/modules/mcp/shared-definitions.d.ts +56 -0
  81. package/dist/modules/mcp/shared-definitions.d.ts.map +1 -0
  82. package/dist/modules/mcp/shared-definitions.js +117 -0
  83. package/dist/modules/mcp/shared-definitions.js.map +1 -0
  84. package/dist/modules/mcp/surface-cache.d.ts +70 -0
  85. package/dist/modules/mcp/surface-cache.d.ts.map +1 -0
  86. package/dist/modules/mcp/surface-cache.js +104 -0
  87. package/dist/modules/mcp/surface-cache.js.map +1 -0
  88. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  89. package/dist/modules/tool-helpers/tool-context.js +33 -12
  90. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  91. package/dist/modules/tool-helpers/tool.contract.d.ts +8 -0
  92. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  93. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  94. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
  95. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +3 -14
  96. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
  97. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +10 -1
  98. package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -1
  99. package/dist/modules/workflow/agent-tools/change-request-summary.js +2 -0
  100. package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -1
  101. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +18 -1
  102. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  103. package/dist/modules/workflow/agent-tools/workflow.tools.js +458 -2
  104. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  105. package/dist/modules/workflow/change-request-hooks.d.ts +78 -0
  106. package/dist/modules/workflow/change-request-hooks.d.ts.map +1 -0
  107. package/dist/modules/workflow/change-request-hooks.js +15 -0
  108. package/dist/modules/workflow/change-request-hooks.js.map +1 -0
  109. package/dist/modules/workflow/git/git.service.d.ts +113 -4
  110. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  111. package/dist/modules/workflow/git/git.service.js +470 -106
  112. package/dist/modules/workflow/git/git.service.js.map +1 -1
  113. package/dist/modules/workflow/git/pull-request.service.d.ts +10 -0
  114. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  115. package/dist/modules/workflow/git/pull-request.service.js +9 -0
  116. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  117. package/dist/modules/workflow/read-on-branch.d.ts +35 -0
  118. package/dist/modules/workflow/read-on-branch.d.ts.map +1 -0
  119. package/dist/modules/workflow/read-on-branch.js +58 -0
  120. package/dist/modules/workflow/read-on-branch.js.map +1 -0
  121. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  122. package/dist/modules/workflow/workflow.routes.js +22 -41
  123. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  124. package/dist/modules/workflow/workflow.service.d.ts +124 -24
  125. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  126. package/dist/modules/workflow/workflow.service.js +326 -48
  127. package/dist/modules/workflow/workflow.service.js.map +1 -1
  128. package/dist/modules/workspace/index.d.ts +2 -0
  129. package/dist/modules/workspace/index.d.ts.map +1 -1
  130. package/dist/modules/workspace/index.js +4 -0
  131. package/dist/modules/workspace/index.js.map +1 -1
  132. package/dist/shared/git.contract.d.ts +7 -0
  133. package/dist/shared/git.contract.d.ts.map +1 -1
  134. package/dist/shared/git.contract.js +9 -0
  135. package/dist/shared/git.contract.js.map +1 -1
  136. package/dist/shared/listener-seam.d.ts +34 -0
  137. package/dist/shared/listener-seam.d.ts.map +1 -0
  138. package/dist/shared/listener-seam.js +42 -0
  139. package/dist/shared/listener-seam.js.map +1 -0
  140. package/dist/shared/no-link-below.d.ts +10 -0
  141. package/dist/shared/no-link-below.d.ts.map +1 -0
  142. package/dist/shared/no-link-below.js +30 -0
  143. package/dist/shared/no-link-below.js.map +1 -0
  144. package/migrations/0018_agent_events_page.sql +11 -0
  145. package/migrations/0019_oauth_token_rotated_at.sql +7 -0
  146. package/migrations/meta/0018_snapshot.json +2429 -0
  147. package/migrations/meta/0019_snapshot.json +2435 -0
  148. package/migrations/meta/_journal.json +15 -1
  149. package/package.json +3 -3
  150. package/src/core/catalog-revision.ts +3 -3
  151. package/src/core/create-core-server.ts +2 -1
  152. package/src/core/create-core-services.ts +21 -2
  153. package/src/index.ts +7 -0
  154. package/src/modules/access/access-control.service.ts +1 -6
  155. package/src/modules/access-model/index.ts +13 -0
  156. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +22 -1
  157. package/src/modules/audit/__tests__/agent-audit.service.test.ts +78 -1
  158. package/src/modules/audit/__tests__/audit.routes.test.ts +43 -5
  159. package/src/modules/audit/agent-audit.service.ts +66 -6
  160. package/src/modules/audit/audit.contract.ts +62 -10
  161. package/src/modules/audit/audit.routes.ts +14 -12
  162. package/src/modules/audit/request-audit.ts +28 -13
  163. package/src/modules/auth/__tests__/account-deactivation.test.ts +38 -2
  164. package/src/modules/auth/account-hooks.ts +13 -21
  165. package/src/modules/auth/auth.service.ts +19 -18
  166. package/src/modules/database/core-schema.ts +31 -7
  167. package/src/modules/kb-fs/__tests__/mutex.canonical.test.ts +57 -0
  168. package/src/modules/kb-fs/index.ts +1 -0
  169. package/src/modules/kb-fs/locking-filesystem.ts +3 -12
  170. package/src/modules/kb-fs/mutex.ts +16 -0
  171. package/src/modules/mcp/__tests__/bevel-oauth-provider.pg.test.ts +279 -0
  172. package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +190 -15
  173. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +7 -1
  174. package/src/modules/mcp/__tests__/mcp.routes.page.test.ts +115 -0
  175. package/src/modules/mcp/__tests__/mcp.surface-cache.test.ts +521 -0
  176. package/src/modules/mcp/__tests__/shared-definitions.test.ts +28 -0
  177. package/src/modules/mcp/__tests__/surface-cache.test.ts +127 -0
  178. package/src/modules/mcp/mcp-auth.middleware.ts +21 -0
  179. package/src/modules/mcp/mcp.routes.ts +68 -0
  180. package/src/modules/mcp/mcp.service.ts +315 -51
  181. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +214 -48
  182. package/src/modules/mcp/request-context.ts +34 -0
  183. package/src/modules/mcp/shared-definitions.ts +121 -0
  184. package/src/modules/mcp/surface-cache.ts +154 -0
  185. package/src/modules/tool-helpers/tool-context.ts +37 -18
  186. package/src/modules/tool-helpers/tool.contract.ts +8 -0
  187. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +1 -1
  188. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +1 -1
  189. package/src/modules/workflow/__tests__/change-request-hooks.test.ts +62 -0
  190. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +23 -12
  191. package/src/modules/workflow/__tests__/workflow.routes.open-change-request.test.ts +79 -0
  192. package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +9 -1
  193. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +508 -5
  194. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +474 -4
  195. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +4 -22
  196. package/src/modules/workflow/agent-tools/change-request-summary.ts +10 -1
  197. package/src/modules/workflow/agent-tools/workflow.tools.ts +468 -1
  198. package/src/modules/workflow/change-request-hooks.ts +96 -0
  199. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +76 -11
  200. package/src/modules/workflow/git/__tests__/git.service.mergeIntoBranch.test.ts +721 -0
  201. package/src/modules/workflow/git/git.service.ts +537 -106
  202. package/src/modules/workflow/git/pull-request.service.ts +10 -0
  203. package/src/modules/workflow/read-on-branch.ts +79 -0
  204. package/src/modules/workflow/workflow.routes.ts +21 -39
  205. package/src/modules/workflow/workflow.service.ts +390 -55
  206. package/src/modules/workspace/index.ts +11 -0
  207. package/src/shared/__tests__/no-link-below.test.ts +45 -0
  208. package/src/shared/git.contract.ts +10 -0
  209. package/src/shared/listener-seam.ts +52 -0
  210. package/src/shared/no-link-below.ts +29 -0
@@ -0,0 +1,25 @@
1
+ import type { RequestAudit } from '../audit/index.js';
2
+ /**
3
+ * What ONE MCP request brings to the tool client it runs on: the bearer its
4
+ * loopback calls carry, and the Audit log's view of it.
5
+ *
6
+ * The client is cached across requests (see {@link SurfaceCache}), so neither
7
+ * may live on the client. A connection-key request's loopback bearer is its
8
+ * own key — per-key metering rides on it, and a revoked key must stop working
9
+ * on its very next request even while the person's client is warm. An OAuth
10
+ * or session request's bearer is an internal token minted for that request,
11
+ * with a lifetime: baked into a cached client it would outlive nothing and
12
+ * expire under a page kept open for a day. So the client reads both from
13
+ * here, at call time, and a request supplies its own.
14
+ */
15
+ export interface McpRequestContext {
16
+ /** The bearer this request's loopback calls carry (`${CONNECTION_KEY}` of every platform-hosted manual). */
17
+ loopbackBearer: string;
18
+ /** The request's Audit log view, or null when the request is attributed to nothing. */
19
+ audit: RequestAudit | null;
20
+ }
21
+ /** Run `fn` as part of the request `ctx`: every tool call it makes, however deep, reads `ctx`. */
22
+ export declare function runInRequest<T>(ctx: McpRequestContext, fn: () => T): T;
23
+ /** The request the current call belongs to, or undefined outside one. */
24
+ export declare function currentRequest(): McpRequestContext | undefined;
25
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../../src/modules/mcp/request-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,iBAAiB;IAChC,4GAA4G;IAC5G,cAAc,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5B;AAID,kGAAkG;AAClG,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtE;AAED,yEAAyE;AACzE,wBAAgB,cAAc,IAAI,iBAAiB,GAAG,SAAS,CAE9D"}
@@ -0,0 +1,11 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ const storage = new AsyncLocalStorage();
3
+ /** Run `fn` as part of the request `ctx`: every tool call it makes, however deep, reads `ctx`. */
4
+ export function runInRequest(ctx, fn) {
5
+ return storage.run(ctx, fn);
6
+ }
7
+ /** The request the current call belongs to, or undefined outside one. */
8
+ export function currentRequest() {
9
+ return storage.getStore();
10
+ }
11
+ //# sourceMappingURL=request-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../../src/modules/mcp/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAuBrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAqB,CAAC;AAE3D,kGAAkG;AAClG,MAAM,UAAU,YAAY,CAAI,GAAsB,EAAE,EAAW;IACjE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { CallTemplate } from '@utcp/sdk';
2
+ import type { CodeModeUtcpClient } from '@utcp/code-mode';
3
+ import type { ProxiedTool } from '@bevel-software/platform-mcp-core';
4
+ /**
5
+ * Tool definitions held once for every cached client that discovered the same
6
+ * ones.
7
+ *
8
+ * Two hundred people whose catalogs are identical would otherwise hold two
9
+ * hundred copies of the same descriptions and schemas: a client's repository
10
+ * keeps what discovery parsed, and every build parses its own. After a build,
11
+ * each manual's discovered tools are swapped for the first copy of identical
12
+ * tools this process has seen, so the descriptions, schemas and call templates
13
+ * are one object graph no matter how many clients point at it. A client keeps
14
+ * only what is its own: the repository's per-tool shells, its variables and
15
+ * its secrets loader.
16
+ *
17
+ * Identity is the CONTENT of the definitions, not the manual's name or its
18
+ * template: the knowledge base's manual is filtered per caller, so two people
19
+ * can get different tools from the same template, and only identical content
20
+ * may be shared. Definitions are tool schemas and call TEMPLATES — `${VAR}`
21
+ * placeholders, never a resolved value — so sharing them shares no secret.
22
+ *
23
+ * Nothing here is read back for correctness: a definition dropped from these
24
+ * maps (they are bounded) only stops the NEXT build from sharing it.
25
+ */
26
+ export declare class SharedDefinitions {
27
+ private readonly maxEntries;
28
+ private readonly byContent;
29
+ private readonly interfaceCaches;
30
+ /** `maxEntries` bounds each map; 0 shares nothing. */
31
+ constructor(maxEntries?: number);
32
+ /**
33
+ * The first-seen copy of `value` (by its JSON), or `value` itself when it is
34
+ * the first. Kinds share no key space: a tool list and a listing could
35
+ * otherwise serialise alike.
36
+ */
37
+ intern<T>(kind: string, value: T): T;
38
+ /**
39
+ * Point `client`'s repository at shared definitions: every manual's tools,
40
+ * and the client's cache of generated TypeScript interfaces.
41
+ *
42
+ * `rawTemplates` are the manuals as fetched, by the name they are registered
43
+ * under. Registration stores the template AFTER variable substitution — for
44
+ * a platform-hosted manual, with the bearer of the request that built the
45
+ * client in its headers. Nothing calls through a stored template (each
46
+ * tool's own template is substituted at call time), but a cached client
47
+ * must hold no request's credential, so the stored template is put back to
48
+ * its placeholders.
49
+ */
50
+ share(client: CodeModeUtcpClient, rawTemplates: ReadonlyMap<string, CallTemplate>): Promise<void>;
51
+ /** The flattened catalog a surface lists, shared the same way. */
52
+ listing(tools: ProxiedTool[]): ProxiedTool[];
53
+ /** The generated-interface cache of one exact tool set. */
54
+ private interfaceCache;
55
+ }
56
+ //# sourceMappingURL=shared-definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-definitions.d.ts","sourceRoot":"","sources":["../../../src/modules/mcp/shared-definitions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,iBAAiB;IAKhB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0C;IAE1E,sDAAsD;gBACzB,UAAU,SAAO;IAE9C;;;;OAIG;IACH,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAepC;;;;;;;;;;;OAWG;IACG,KAAK,CAAC,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBvG,kEAAkE;IAClE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE;IAI5C,2DAA2D;IAC3D,OAAO,CAAC,cAAc;CAYvB"}
@@ -0,0 +1,117 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Tool definitions held once for every cached client that discovered the same
4
+ * ones.
5
+ *
6
+ * Two hundred people whose catalogs are identical would otherwise hold two
7
+ * hundred copies of the same descriptions and schemas: a client's repository
8
+ * keeps what discovery parsed, and every build parses its own. After a build,
9
+ * each manual's discovered tools are swapped for the first copy of identical
10
+ * tools this process has seen, so the descriptions, schemas and call templates
11
+ * are one object graph no matter how many clients point at it. A client keeps
12
+ * only what is its own: the repository's per-tool shells, its variables and
13
+ * its secrets loader.
14
+ *
15
+ * Identity is the CONTENT of the definitions, not the manual's name or its
16
+ * template: the knowledge base's manual is filtered per caller, so two people
17
+ * can get different tools from the same template, and only identical content
18
+ * may be shared. Definitions are tool schemas and call TEMPLATES — `${VAR}`
19
+ * placeholders, never a resolved value — so sharing them shares no secret.
20
+ *
21
+ * Nothing here is read back for correctness: a definition dropped from these
22
+ * maps (they are bounded) only stops the NEXT build from sharing it.
23
+ */
24
+ export class SharedDefinitions {
25
+ maxEntries;
26
+ byContent = new Map();
27
+ interfaceCaches = new Map();
28
+ /** `maxEntries` bounds each map; 0 shares nothing. */
29
+ constructor(maxEntries = 1000) {
30
+ this.maxEntries = maxEntries;
31
+ }
32
+ /**
33
+ * The first-seen copy of `value` (by its JSON), or `value` itself when it is
34
+ * the first. Kinds share no key space: a tool list and a listing could
35
+ * otherwise serialise alike.
36
+ */
37
+ intern(kind, value) {
38
+ const key = `${kind}\u0000${fingerprint(value)}`;
39
+ const held = this.byContent.get(key);
40
+ if (held !== undefined) {
41
+ // Recently used goes last, so the first entry is the one to drop.
42
+ this.byContent.delete(key);
43
+ this.byContent.set(key, held);
44
+ return held;
45
+ }
46
+ if (this.maxEntries <= 0)
47
+ return value;
48
+ bound(this.byContent, this.maxEntries);
49
+ this.byContent.set(key, value);
50
+ return value;
51
+ }
52
+ /**
53
+ * Point `client`'s repository at shared definitions: every manual's tools,
54
+ * and the client's cache of generated TypeScript interfaces.
55
+ *
56
+ * `rawTemplates` are the manuals as fetched, by the name they are registered
57
+ * under. Registration stores the template AFTER variable substitution — for
58
+ * a platform-hosted manual, with the bearer of the request that built the
59
+ * client in its headers. Nothing calls through a stored template (each
60
+ * tool's own template is substituted at call time), but a cached client
61
+ * must hold no request's credential, so the stored template is put back to
62
+ * its placeholders.
63
+ */
64
+ async share(client, rawTemplates) {
65
+ const repo = client.config.tool_repository;
66
+ const templates = await repo.getManualCallTemplates();
67
+ const everyTool = [];
68
+ for (const stored of templates) {
69
+ const name = String(stored.name ?? '');
70
+ const manual = await repo.getManual(name);
71
+ if (!manual)
72
+ continue;
73
+ const tools = this.intern('tools', manual.tools);
74
+ everyTool.push(...tools);
75
+ const raw = rawTemplates.get(name);
76
+ const template = raw ? { ...raw, name } : stored;
77
+ await repo.saveManual(template, { ...manual, tools });
78
+ }
79
+ // `toolFunctionCache` is filled lazily by `tools_info` and every chain,
80
+ // keyed by tool name and derived from the definitions alone. Shared by
81
+ // every client holding the very same tool set, it is generated once for
82
+ // all of them instead of once per person.
83
+ client.toolFunctionCache = this.interfaceCache(fingerprint(everyTool));
84
+ }
85
+ /** The flattened catalog a surface lists, shared the same way. */
86
+ listing(tools) {
87
+ return this.intern('listing', tools);
88
+ }
89
+ /** The generated-interface cache of one exact tool set. */
90
+ interfaceCache(toolSet) {
91
+ let cache = this.interfaceCaches.get(toolSet);
92
+ if (cache) {
93
+ this.interfaceCaches.delete(toolSet);
94
+ }
95
+ else {
96
+ cache = new Map();
97
+ if (this.maxEntries <= 0)
98
+ return cache;
99
+ bound(this.interfaceCaches, this.maxEntries);
100
+ }
101
+ this.interfaceCaches.set(toolSet, cache);
102
+ return cache;
103
+ }
104
+ }
105
+ /** Make room for one more entry, dropping the least recently used. */
106
+ function bound(map, maxEntries) {
107
+ while (map.size >= maxEntries) {
108
+ const oldest = map.keys().next().value;
109
+ if (oldest === undefined)
110
+ break;
111
+ map.delete(oldest);
112
+ }
113
+ }
114
+ function fingerprint(value) {
115
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex');
116
+ }
117
+ //# sourceMappingURL=shared-definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-definitions.js","sourceRoot":"","sources":["../../../src/modules/mcp/shared-definitions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The hosted MCP server's memory of built tool surfaces: one per caller and
3
+ * catalog revision, built once and reused until something it was built from
4
+ * changes.
5
+ *
6
+ * Keyed by whatever the service says identifies a surface — the person, the
7
+ * kind of caller, the key's scope and the catalog revision. A catalog change
8
+ * needs no listener: it is a new revision, so a new key, and the old entry
9
+ * ages out unused. What the revision cannot see (a secret) is evicted
10
+ * explicitly through {@link evictWhere} / {@link clear}.
11
+ *
12
+ * Bounded twice: an entry unused for `idleTtlMs` is dropped, and at most
13
+ * `maxEntries` are held, the least recently used going first. `maxEntries: 0`
14
+ * keeps nothing, which is the per-request build this replaced: the escape
15
+ * hatch if the cache ever has to be switched off.
16
+ *
17
+ * Single-flight: concurrent misses on one key share one build. A build that
18
+ * fails is not remembered, so the next request tries again. A build that was
19
+ * in flight when its key was evicted answers the requests waiting on it but is
20
+ * not kept: it was built from what the eviction said is stale. A build still
21
+ * running after `buildTimeoutMs` is let go the same way, so a discovery that
22
+ * hangs holds up the requests already waiting on it, not every later one.
23
+ *
24
+ * Holds references only. A surface owns no connection (its `mcp` manuals route
25
+ * to the downstream pool, which owns and closes those), so dropping an entry
26
+ * is the whole teardown.
27
+ */
28
+ export interface SurfaceCacheOptions {
29
+ /** Drop an entry unused for this long. Default one hour. */
30
+ idleTtlMs?: number;
31
+ /** Hold at most this many entries. Default 500; 0 keeps nothing. */
32
+ maxEntries?: number;
33
+ /** Let go of a build still running after this long; the next request starts another. Default two minutes. */
34
+ buildTimeoutMs?: number;
35
+ /** Clock, for tests. */
36
+ now?: () => number;
37
+ }
38
+ export declare const SURFACE_IDLE_TTL_MS: number;
39
+ export declare const SURFACE_BUILD_TIMEOUT_MS: number;
40
+ export declare const SURFACE_MAX_ENTRIES = 500;
41
+ export declare class SurfaceCache<V> {
42
+ private readonly entries;
43
+ private readonly idleTtlMs;
44
+ private readonly maxEntries;
45
+ private readonly buildTimeoutMs;
46
+ private readonly now;
47
+ constructor(opts?: SurfaceCacheOptions);
48
+ /**
49
+ * The value for `key`, built by `build` when none is held. `hit` says
50
+ * whether this call found one (or joined a build already running).
51
+ *
52
+ * `maxAgeMs`, read off a built value, ends that entry early whatever its
53
+ * use: a surface built while one of its manuals was failing is kept only
54
+ * as long as that failure would be remembered anyway.
55
+ */
56
+ get(key: string, build: () => Promise<V>, maxAgeMs?: (value: V) => number | undefined): Promise<{
57
+ value: V;
58
+ hit: boolean;
59
+ }>;
60
+ /** Drop every entry whose key matches. A build in flight still answers its waiters, and is not kept. */
61
+ evictWhere(predicate: (key: string) => boolean): void;
62
+ /** Drop everything. */
63
+ clear(): void;
64
+ private drop;
65
+ /** Remove `entry` only if it is still the one under `key` (a newer build may have taken its place). */
66
+ private forget;
67
+ /** Drop what has been idle too long, or outlived its own end, and let go of a build that has run too long. */
68
+ private sweep;
69
+ }
70
+ //# sourceMappingURL=surface-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface-cache.d.ts","sourceRoot":"","sources":["../../../src/modules/mcp/surface-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6GAA6G;IAC7G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AACtD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAavC,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,GAAE,mBAAwB;IAO1C;;;;;;;OAOG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAC1C,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC;IAuCtC,wGAAwG;IACxG,UAAU,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAMrD,uBAAuB;IACvB,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,IAAI;IAOZ,uGAAuG;IACvG,OAAO,CAAC,MAAM;IAId,8GAA8G;IAC9G,OAAO,CAAC,KAAK;CAUd"}
@@ -0,0 +1,104 @@
1
+ export const SURFACE_IDLE_TTL_MS = 60 * 60 * 1000;
2
+ export const SURFACE_BUILD_TIMEOUT_MS = 2 * 60 * 1000;
3
+ export const SURFACE_MAX_ENTRIES = 500;
4
+ export class SurfaceCache {
5
+ entries = new Map();
6
+ idleTtlMs;
7
+ maxEntries;
8
+ buildTimeoutMs;
9
+ now;
10
+ constructor(opts = {}) {
11
+ this.idleTtlMs = opts.idleTtlMs ?? SURFACE_IDLE_TTL_MS;
12
+ this.maxEntries = opts.maxEntries ?? SURFACE_MAX_ENTRIES;
13
+ this.buildTimeoutMs = opts.buildTimeoutMs ?? SURFACE_BUILD_TIMEOUT_MS;
14
+ this.now = opts.now ?? Date.now;
15
+ }
16
+ /**
17
+ * The value for `key`, built by `build` when none is held. `hit` says
18
+ * whether this call found one (or joined a build already running).
19
+ *
20
+ * `maxAgeMs`, read off a built value, ends that entry early whatever its
21
+ * use: a surface built while one of its manuals was failing is kept only
22
+ * as long as that failure would be remembered anyway.
23
+ */
24
+ async get(key, build, maxAgeMs) {
25
+ this.sweep();
26
+ const now = this.now();
27
+ const held = this.entries.get(key);
28
+ if (held) {
29
+ held.lastUsed = now;
30
+ // Most recently used goes last, so the first entry is always the one to drop.
31
+ this.entries.delete(key);
32
+ this.entries.set(key, held);
33
+ return { value: await held.value, hit: true };
34
+ }
35
+ if (this.maxEntries <= 0)
36
+ return { value: await build(), hit: false };
37
+ while (this.entries.size >= this.maxEntries) {
38
+ const oldest = this.entries.keys().next().value;
39
+ if (oldest === undefined)
40
+ break;
41
+ this.drop(oldest);
42
+ }
43
+ const entry = {
44
+ value: build(),
45
+ startedAt: now,
46
+ lastUsed: now,
47
+ expiresAt: Number.POSITIVE_INFINITY,
48
+ live: true,
49
+ settled: false,
50
+ };
51
+ this.entries.set(key, entry);
52
+ try {
53
+ const value = await entry.value;
54
+ entry.settled = true;
55
+ const maxAge = maxAgeMs?.(value);
56
+ if (maxAge !== undefined)
57
+ entry.expiresAt = this.now() + maxAge;
58
+ if (!entry.live)
59
+ this.forget(key, entry);
60
+ return { value, hit: false };
61
+ }
62
+ catch (err) {
63
+ this.forget(key, entry);
64
+ throw err;
65
+ }
66
+ }
67
+ /** Drop every entry whose key matches. A build in flight still answers its waiters, and is not kept. */
68
+ evictWhere(predicate) {
69
+ for (const key of [...this.entries.keys()]) {
70
+ if (predicate(key))
71
+ this.drop(key);
72
+ }
73
+ }
74
+ /** Drop everything. */
75
+ clear() {
76
+ this.evictWhere(() => true);
77
+ }
78
+ drop(key) {
79
+ const entry = this.entries.get(key);
80
+ if (!entry)
81
+ return;
82
+ entry.live = false;
83
+ this.entries.delete(key);
84
+ }
85
+ /** Remove `entry` only if it is still the one under `key` (a newer build may have taken its place). */
86
+ forget(key, entry) {
87
+ if (this.entries.get(key) === entry)
88
+ this.entries.delete(key);
89
+ }
90
+ /** Drop what has been idle too long, or outlived its own end, and let go of a build that has run too long. */
91
+ sweep() {
92
+ const now = this.now();
93
+ for (const [key, entry] of this.entries) {
94
+ if (!entry.settled) {
95
+ if (now - entry.startedAt >= this.buildTimeoutMs)
96
+ this.drop(key);
97
+ continue;
98
+ }
99
+ if (now - entry.lastUsed >= this.idleTtlMs || now >= entry.expiresAt)
100
+ this.drop(key);
101
+ }
102
+ }
103
+ }
104
+ //# sourceMappingURL=surface-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface-cache.js","sourceRoot":"","sources":["../../../src/modules/mcp/surface-cache.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-context.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool-context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAOxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAI1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,gBAAgB,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,EAAE,cAAc,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CACjB,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KAC/C,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,EAAE,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACpF;AAYD,MAAM,MAAM,kBAAkB,GAAG,CAC/B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC,WAAW,CAAC,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB,CAuFnF"}
1
+ {"version":3,"file":"tool-context.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool-context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAQxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAI1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,gBAAgB,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,EAAE,cAAc,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CACjB,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KAC/C,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,EAAE,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACpF;AAYD,MAAM,MAAM,kBAAkB,GAAG,CAC/B,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC,WAAW,CAAC,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB,CAyGnF"}
@@ -35,6 +35,33 @@ export function createToolContextResolver(deps) {
35
35
  if (!user)
36
36
  throw new ToolError('Your account is no longer available.', 401);
37
37
  const { loadActiveGroups } = deps;
38
+ const validatorCache = new Map();
39
+ /**
40
+ * The agent's pre-disk `roles.yaml` gate for `branch`'s workspace
41
+ * (cached): refuses a write that would leave the file unparseable (an
42
+ * app-wide admin lockout), that creates a role, or that adds a
43
+ * `- group:` entry naming no group — agents manage membership; roles are
44
+ * pre-set. ONE gate for every agent write to the workspace: the file
45
+ * tools take it before a byte lands, and `update_branch` takes it for a
46
+ * resolution's content before the merge.
47
+ */
48
+ const getWriteValidator = async (branch) => {
49
+ assertBranchProvided(branch);
50
+ const cached = validatorCache.get(branch);
51
+ if (cached)
52
+ return cached;
53
+ const ws = await deps.workspaceService.getOrCreateForUser(user, branch);
54
+ const basePath = await deps.workspaceService.getWorkspacePath(ws.id);
55
+ const validator = makeAgentRolesYamlWriteValidator(deps.kbDirName, () => readTextIfExists(path.join(basePath, deps.kbDirName, ROLES_YAML_BASENAME)), loadActiveGroups &&
56
+ (async () => {
57
+ // A source that exists but cannot be read leaves group entries
58
+ // unchecked rather than refusing every one.
59
+ const active = await loadActiveGroups((file) => readTextIfExists(path.join(basePath, deps.kbDirName, file)));
60
+ return active.health.ok ? active : null;
61
+ }));
62
+ validatorCache.set(branch, validator);
63
+ return validator;
64
+ };
38
65
  const fsCache = new Map();
39
66
  const getFilesystem = async (branch) => {
40
67
  // THE choke point: every knowledge-base tool resolves its workspace
@@ -54,6 +81,7 @@ export function createToolContextResolver(deps) {
54
81
  const cached = fsCache.get(branch);
55
82
  if (cached)
56
83
  return cached;
84
+ // (The write validator below is built through `getWriteValidator`.)
57
85
  const ws = await deps.workspaceService.getOrCreateForUser(user, branch);
58
86
  const workspaceId = ws.id;
59
87
  const basePath = await deps.workspaceService.getWorkspacePath(workspaceId);
@@ -64,18 +92,10 @@ export function createToolContextResolver(deps) {
64
92
  branch: branchForWorkspaceId(workspaceId),
65
93
  user,
66
94
  kbDirName: deps.kbDirName,
67
- // Reject an agent write that would leave roles.yaml unparseable
68
- // (app-wide admin lockout) or that creates a role before it
69
- // reaches disk, or that adds a `- group:` entry naming no group
70
- // — the agent gets a tool error and the file is untouched.
71
- // Agents manage membership; roles are pre-set.
72
- validateWrite: makeAgentRolesYamlWriteValidator(deps.kbDirName, () => readTextIfExists(path.join(basePath, deps.kbDirName, ROLES_YAML_BASENAME)), loadActiveGroups &&
73
- (async () => {
74
- // A source that exists but cannot be read leaves group
75
- // entries unchecked rather than refusing every one.
76
- const active = await loadActiveGroups((file) => readTextIfExists(path.join(basePath, deps.kbDirName, file)));
77
- return active.health.ok ? active : null;
78
- })),
95
+ // The agent's pre-disk roles.yaml gate (`getWriteValidator`):
96
+ // the file tools and `update_branch`'s resolutions take the
97
+ // same one.
98
+ validateWrite: await getWriteValidator(branch),
79
99
  creatorAccess: deps.creatorAccess,
80
100
  })
81
101
  : new ReadOnlyFilesystem({ basePath, contained: true });
@@ -104,6 +124,7 @@ export function createToolContextResolver(deps) {
104
124
  workflowService: deps.workflowService,
105
125
  events: deps.events,
106
126
  getFilesystem,
127
+ getWriteValidator,
107
128
  };
108
129
  };
109
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool-context.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { AuthUser, IWorkflowService } from '@bevel-software/platform-shared
2
2
  import type { LocalFilesystem } from '@mastra/core/workspace';
3
3
  import type { WorkspaceService } from '../workspace/workspace.service.js';
4
4
  import type { WorkflowEventBus } from '../workflow/event-bus.js';
5
+ import type { WriteValidator } from '../kb-fs/index.js';
5
6
  /**
6
7
  * The hosting/execution contract a module works against when it lets `toolHelpers`
7
8
  * host its endpoint (`toolHandler`). It is NOT a registry concern — the registry
@@ -59,6 +60,13 @@ export interface ToolContext {
59
60
  events: WorkflowEventBus;
60
61
  /** Build the lock-aware filesystem for `branch`'s workspace (cached). The tool supplies `branch` from its own input. */
61
62
  getFilesystem(branch: string): Promise<LocalFilesystem>;
63
+ /**
64
+ * The agent's pre-disk write gate for `branch`'s workspace (cached) — the
65
+ * one the filesystem above runs before a byte lands (`roles.yaml`: must
66
+ * parse, no new role, no unknown group). A tool that writes content by
67
+ * another road than that filesystem (a merge resolution) takes it too.
68
+ */
69
+ getWriteValidator(branch: string): Promise<WriteValidator>;
62
70
  }
63
71
  /**
64
72
  * A tool's implementation: a pure function of `(args, ctx)`. Return a value for
@@ -1 +1 @@
1
- {"version":3,"file":"tool.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAIxB,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IAC5C,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,gBAAgB,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,wHAAwH;IACxH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,WAAW,KACb,OAAO,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;AAE/C;;;;;;;;GAQG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAGhC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAF1C,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAK7C;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAUtF"}
1
+ {"version":3,"file":"tool.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAIxB,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IAC5C,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,gBAAgB,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,wHAAwH;IACxH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACxD;;;;;OAKG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5D;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,WAAW,KACb,OAAO,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;AAE/C;;;;;;;;GAQG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAGhC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAF1C,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAK7C;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAUtF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool.contract.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool.contract.ts"],"names":[],"mappings":"AA8EA;;;;;;;;GAQG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGvB;IACA;IAHX,YACE,OAAe,EACN,MAAc,EACd,OAAiC;QAE1C,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAQ;QACd,YAAO,GAAP,OAAO,CAA0B;QAG1C,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAC;IACpD,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACxB,MAAM,IAAI,GAAG;QACb,MAAM,IAAI,GAAG;QACb,OAAQ,GAA6B,CAAC,OAAO,KAAK,QAAQ,CAC3D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tool.contract.js","sourceRoot":"","sources":["../../../src/modules/tool-helpers/tool.contract.ts"],"names":[],"mappings":"AAsFA;;;;;;;;GAQG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGvB;IACA;IAHX,YACE,OAAe,EACN,MAAc,EACd,OAAiC;QAE1C,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAQ;QACd,YAAO,GAAP,OAAO,CAA0B;QAG1C,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAC;IACpD,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACxB,MAAM,IAAI,GAAG;QACb,MAAM,IAAI,GAAG;QACb,OAAQ,GAA6B,CAAC,OAAO,KAAK,QAAQ,CAC3D,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"change-request-read.tools.d.ts","sourceRoot":"","sources":["../../../../src/modules/workflow/agent-tools/change-request-read.tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAEV,mBAAmB,EAGpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,sCAAsC,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAoB,MAAM,qCAAqC,CAAC;AAEpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAsB/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,kBAAkB,EAC/B,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACxD,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,GACxC,IAAI,CA0fN;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;OAGG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,2EAA2E;IAC3E,aAAa,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACxD,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC;iCAOC,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC;yBAUhE,WAAW,eACH,MAAM,QACb,MAAM,SACL,MAAM,EAAE,KACd,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;wBAahB,WAAW,UACR,MAAM,QACR;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KACzB,OAAO,CAAC,YAAY,CAAC;uBA8CE,MAAM,KAAG,SAAS;kBAIjB,WAAW,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;EAW1E"}
1
+ {"version":3,"file":"change-request-read.tools.d.ts","sourceRoot":"","sources":["../../../../src/modules/workflow/agent-tools/change-request-read.tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAEV,mBAAmB,EAGpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,sCAAsC,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAoB,MAAM,qCAAqC,CAAC;AAEpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAuB/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,kBAAkB,EAC/B,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACxD,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,GACxC,IAAI,CA0fN;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;OAGG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,2EAA2E;IAC3E,aAAa,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,EACxD,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC;iCAOC,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC;yBAI3C,WAAW,eAAe,MAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;wBAI3G,WAAW,UACR,MAAM,QACR;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KACzB,OAAO,CAAC,YAAY,CAAC;uBA8CE,MAAM,KAAG,SAAS;kBAIjB,WAAW,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;EAW1E"}
@@ -1,5 +1,6 @@
1
1
  import { ToolError } from '../../tool-helpers/tool.contract.js';
2
2
  import { toolDef } from '../../tool-helpers/tool-def.js';
3
+ import { readablePathsAt } from '../read-on-branch.js';
3
4
  import { PER_PAGE_DEFAULT, PER_PAGE_MAX, fileIsReadable, isAuthor, matchesAuthor, pathsOf, maySeeChangeRequest, pageOf, pagingOf, statesFor, toCrComment, toCrDetail, toCrFile, toCrReviews, toCrSummary, toCrViewer, visibleBlockers, visibleComments, } from './change-request-read-shape.js';
4
5
  /**
5
6
  * The five read tools over change requests, named and shaped after GitHub's
@@ -533,20 +534,8 @@ export function changeRequestScope(accessControl, kb) {
533
534
  * identically — reusing one avoids cloning a branch just to read a ref.
534
535
  */
535
536
  const repoGlobalWorkspaceId = async (ctx) => (await ctx.workspaceService.findAnyWorkspaceId()) ?? kb.defaultWorkspaceId();
536
- /**
537
- * Read verdicts for `paths` at `origin/<base>`, as a membership set of what
538
- * the caller MAY read. A null answer (the ref or its `roles.yaml` does not
539
- * resolve) withholds everything, which is the same reading the app's
540
- * fork-point route gives it.
541
- */
542
- const readablePaths = async (ctx, workspaceId, base, paths) => {
543
- if (paths.length === 0)
544
- return new Set();
545
- const verdicts = await accessControl.canReadBatchAtRef(workspaceId, `origin/${base}`, ctx.user.email, [...new Set(paths)]);
546
- if (!verdicts)
547
- return new Set();
548
- return new Set([...verdicts].filter(([, allowed]) => allowed).map(([p]) => p));
549
- };
537
+ /** What the caller MAY read of `paths` at `origin/<base>` — the one rule, `readablePathsAt`. */
538
+ const readablePaths = (ctx, workspaceId, base, paths) => readablePathsAt(accessControl, workspaceId, ctx.user.email, base, paths);
550
539
  const scopedDetail = async (ctx, number, opts) => {
551
540
  const workspaceId = await repoGlobalWorkspaceId(ctx);
552
541
  const detail = await ctx.workflowService.getChangeRequestDetail(number, {