@intutic/mcp-governance-proxy 2.2.0 → 2.4.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 (137) hide show
  1. package/README.md +8 -7
  2. package/dist/agentTools.d.ts +83 -0
  3. package/dist/agentTools.d.ts.map +1 -0
  4. package/dist/agentTools.js +170 -0
  5. package/dist/agentTools.js.map +1 -0
  6. package/dist/anomaly/detectors.js +1 -1
  7. package/dist/anomaly/detectors.js.map +1 -1
  8. package/dist/approvalHold.d.ts +8 -7
  9. package/dist/approvalHold.d.ts.map +1 -1
  10. package/dist/approvalHold.js +7 -6
  11. package/dist/approvalHold.js.map +1 -1
  12. package/dist/budget.d.ts +133 -0
  13. package/dist/budget.d.ts.map +1 -0
  14. package/dist/budget.js +191 -0
  15. package/dist/budget.js.map +1 -0
  16. package/dist/config.d.ts +1 -1
  17. package/dist/config.js +1 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/daemon/healthMonitor.d.ts.map +1 -1
  20. package/dist/daemon/healthMonitor.js +0 -1
  21. package/dist/daemon/healthMonitor.js.map +1 -1
  22. package/dist/daemon/index.d.ts +0 -2
  23. package/dist/daemon/index.d.ts.map +1 -1
  24. package/dist/daemon/index.js +0 -2
  25. package/dist/daemon/index.js.map +1 -1
  26. package/dist/daemon/policyCache.d.ts +22 -3
  27. package/dist/daemon/policyCache.d.ts.map +1 -1
  28. package/dist/daemon/policyCache.js +38 -15
  29. package/dist/daemon/policyCache.js.map +1 -1
  30. package/dist/daemon/socketServer.d.ts +0 -1
  31. package/dist/daemon/socketServer.d.ts.map +1 -1
  32. package/dist/daemon/socketServer.js +0 -1
  33. package/dist/daemon/socketServer.js.map +1 -1
  34. package/dist/daemon/statusReporter.d.ts.map +1 -1
  35. package/dist/daemon/statusReporter.js +0 -1
  36. package/dist/daemon/statusReporter.js.map +1 -1
  37. package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
  38. package/dist/daemon/telemetryBatcher.js +0 -1
  39. package/dist/daemon/telemetryBatcher.js.map +1 -1
  40. package/dist/daemonClient.d.ts.map +1 -1
  41. package/dist/daemonClient.js +0 -1
  42. package/dist/daemonClient.js.map +1 -1
  43. package/dist/dlp.d.ts +32 -3
  44. package/dist/dlp.d.ts.map +1 -1
  45. package/dist/dlp.js +116 -18
  46. package/dist/dlp.js.map +1 -1
  47. package/dist/dlpPii.d.ts +66 -0
  48. package/dist/dlpPii.d.ts.map +1 -0
  49. package/dist/dlpPii.js +235 -0
  50. package/dist/dlpPii.js.map +1 -0
  51. package/dist/emitter.d.ts +65 -3
  52. package/dist/emitter.d.ts.map +1 -1
  53. package/dist/emitter.js +13 -1
  54. package/dist/emitter.js.map +1 -1
  55. package/dist/guardedValkey.d.ts +40 -0
  56. package/dist/guardedValkey.d.ts.map +1 -0
  57. package/dist/guardedValkey.js +84 -0
  58. package/dist/guardedValkey.js.map +1 -0
  59. package/dist/httpJson.d.ts +7 -0
  60. package/dist/httpJson.d.ts.map +1 -1
  61. package/dist/httpJson.js +39 -0
  62. package/dist/httpJson.js.map +1 -1
  63. package/dist/injection.js +1 -1
  64. package/dist/interceptor.d.ts +65 -10
  65. package/dist/interceptor.d.ts.map +1 -1
  66. package/dist/interceptor.js +190 -69
  67. package/dist/interceptor.js.map +1 -1
  68. package/dist/policy.d.ts +37 -1
  69. package/dist/policy.d.ts.map +1 -1
  70. package/dist/policy.js +62 -3
  71. package/dist/policy.js.map +1 -1
  72. package/dist/proxy.d.ts +30 -9
  73. package/dist/proxy.d.ts.map +1 -1
  74. package/dist/proxy.js +127 -57
  75. package/dist/proxy.js.map +1 -1
  76. package/dist/refusals.d.ts +26 -0
  77. package/dist/refusals.d.ts.map +1 -0
  78. package/dist/refusals.js +43 -0
  79. package/dist/refusals.js.map +1 -0
  80. package/dist/registryObserver.d.ts +9 -5
  81. package/dist/registryObserver.d.ts.map +1 -1
  82. package/dist/registryObserver.js +19 -8
  83. package/dist/registryObserver.js.map +1 -1
  84. package/dist/session.d.ts +1 -1
  85. package/dist/session.js +1 -1
  86. package/dist/sessionScope.d.ts +1 -1
  87. package/dist/sessionScope.js +1 -1
  88. package/dist/sessionStore.d.ts +10 -23
  89. package/dist/sessionStore.d.ts.map +1 -1
  90. package/dist/sessionStore.js +17 -76
  91. package/dist/sessionStore.js.map +1 -1
  92. package/dist/wasm/cloudRules.d.ts +138 -0
  93. package/dist/wasm/cloudRules.d.ts.map +1 -0
  94. package/dist/wasm/cloudRules.js +242 -0
  95. package/dist/wasm/cloudRules.js.map +1 -0
  96. package/dist/wasm/context.d.ts +10 -0
  97. package/dist/wasm/context.d.ts.map +1 -1
  98. package/dist/wasm/context.js +27 -0
  99. package/dist/wasm/context.js.map +1 -1
  100. package/dist/wasm/hostImports.d.ts +3 -3
  101. package/dist/wasm/hostImports.d.ts.map +1 -1
  102. package/dist/wasm/hostImports.js +8 -7
  103. package/dist/wasm/hostImports.js.map +1 -1
  104. package/dist/wasm/loader.d.ts +10 -1
  105. package/dist/wasm/loader.d.ts.map +1 -1
  106. package/dist/wasm/loader.js +2 -0
  107. package/dist/wasm/loader.js.map +1 -1
  108. package/dist/wasm/referencedFiles.d.ts +29 -4
  109. package/dist/wasm/referencedFiles.d.ts.map +1 -1
  110. package/dist/wasm/referencedFiles.js +48 -21
  111. package/dist/wasm/referencedFiles.js.map +1 -1
  112. package/dist/wasm/runner.d.ts +169 -31
  113. package/dist/wasm/runner.d.ts.map +1 -1
  114. package/dist/wasm/runner.js +298 -83
  115. package/dist/wasm/runner.js.map +1 -1
  116. package/dist/wasm/watch.js +1 -1
  117. package/dist/wasm/worker.d.ts +10 -6
  118. package/dist/wasm/worker.d.ts.map +1 -1
  119. package/dist/wasm/worker.js +93 -9
  120. package/dist/wasm/worker.js.map +1 -1
  121. package/package.json +5 -4
  122. package/dist/toolPoison.d.ts +0 -32
  123. package/dist/toolPoison.d.ts.map +0 -1
  124. package/dist/toolPoison.js +0 -81
  125. package/dist/toolPoison.js.map +0 -1
  126. package/dist/wasm/fuel.d.ts +0 -35
  127. package/dist/wasm/fuel.d.ts.map +0 -1
  128. package/dist/wasm/fuel.js +0 -299
  129. package/dist/wasm/fuel.js.map +0 -1
  130. package/dist/wasm/memoryCap.d.ts +0 -21
  131. package/dist/wasm/memoryCap.d.ts.map +0 -1
  132. package/dist/wasm/memoryCap.js +0 -54
  133. package/dist/wasm/memoryCap.js.map +0 -1
  134. package/dist/wasm/wasmBinary.d.ts +0 -25
  135. package/dist/wasm/wasmBinary.d.ts.map +0 -1
  136. package/dist/wasm/wasmBinary.js +0 -100
  137. package/dist/wasm/wasmBinary.js.map +0 -1
@@ -7,9 +7,12 @@
7
7
  * the proxy ever sees a server's tools/list. So the proxy reports its own
8
8
  * server: once at start (so a server refused under a `deny` default still
9
9
  * reaches the approval queue, even if its tools/list never gets through) and
10
- * again whenever the tool names it sees change (so the registry can offer
11
- * per-tool toggles). The control plane creates a candidate on first sight and
12
- * notifies the workspace.
10
+ * again whenever the tools it sees change — a tool added or removed, or a
11
+ * description or input schema changed. The report carries the tool names (for
12
+ * the registry's per-tool toggles) and the definitions as the server declared
13
+ * them, before curation, which the control plane compares with the last ones
14
+ * it stored to score the change's risk. The control plane creates a candidate
15
+ * on first sight and notifies the workspace.
13
16
  *
14
17
  * Best-effort and quiet: a failed report costs one registry refresh, never a
15
18
  * tool call.
@@ -32,22 +35,30 @@ export class RegistryObserver {
32
35
  this.transport = transport;
33
36
  }
34
37
  /**
35
- * Reports the server, with `tools` when known. A report identical to the
38
+ * Reports the server, with its tools when known. A report identical to the
36
39
  * last successful one is skipped. Without an API key, or without a
37
40
  * `--server-name` to report, there is nothing the registry could record.
38
41
  */
39
42
  async observe(tools) {
40
43
  if (!this.apiKey || this.serverName === 'unknown')
41
44
  return;
42
- const toolNames = tools ? [...new Set(tools)].sort() : undefined;
43
- const key = JSON.stringify(toolNames ?? null);
44
- if (key === this.lastReported || (toolNames === undefined && this.lastReported !== null))
45
+ let definitions;
46
+ if (tools) {
47
+ // One entry per name, the first declared — the rule the risk score matches by.
48
+ const byName = new Map();
49
+ for (const t of tools)
50
+ if (!byName.has(t.name))
51
+ byName.set(t.name, t);
52
+ definitions = [...byName.values()].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
53
+ }
54
+ const key = JSON.stringify(definitions ?? null);
55
+ if (key === this.lastReported || (definitions === undefined && this.lastReported !== null))
45
56
  return;
46
57
  try {
47
58
  await postJson(`${this.controlPlaneUrl}/api/v1/mcp/servers/observe`, this.apiKey, {
48
59
  serverName: this.serverName,
49
60
  transport: this.transport,
50
- ...(toolNames ? { tools: toolNames } : {}),
61
+ ...(definitions ? { tools: definitions.map((t) => t.name), toolDefinitions: definitions } : {}),
51
62
  });
52
63
  this.lastReported = key;
53
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"registryObserver.js","sourceRoot":"","sources":["../src/registryObserver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,MAAM,GAAG,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAA;AAE9C,MAAM,OAAO,gBAAgB;IAIR;IACA;IACA;IACA;IANX,YAAY,GAAkB,IAAI,CAAA;IAE1C,YACmB,eAAuB,EACvB,MAAc,EACd,UAAkB,EAClB,SAAmC;QAHnC,oBAAe,GAAf,eAAe,CAAQ;QACvB,WAAM,GAAN,MAAM,CAAQ;QACd,eAAU,GAAV,UAAU,CAAQ;QAClB,cAAS,GAAT,SAAS,CAA0B;IACnD,CAAC;IAEJ;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,KAAyB;QACrC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,OAAM;QACzD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC,CAAA;QAC7C,IAAI,GAAG,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;YAAE,OAAM;QAChG,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,eAAe,6BAA6B,EAAE,IAAI,CAAC,MAAM,EAAE;gBAChF,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C,CAAC,CAAA;YACF,IAAI,CAAC,YAAY,GAAG,GAAG,CAAA;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,yBAAyB,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,EAAE,kDAAkD,CAAC,CAAA;QACnI,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"registryObserver.js","sourceRoot":"","sources":["../src/registryObserver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,MAAM,GAAG,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAA;AAE9C,MAAM,OAAO,gBAAgB;IAIR;IACA;IACA;IACA;IANX,YAAY,GAAkB,IAAI,CAAA;IAE1C,YACmB,eAAuB,EACvB,MAAc,EACd,UAAkB,EAClB,SAAmC;QAHnC,oBAAe,GAAf,eAAe,CAAQ;QACvB,WAAM,GAAN,MAAM,CAAQ;QACd,eAAU,GAAV,UAAU,CAAQ;QAClB,cAAS,GAAT,SAAS,CAA0B;IACnD,CAAC;IAEJ;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,KAAoC;QAChD,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,OAAM;QACzD,IAAI,WAA4C,CAAA;QAChD,IAAI,KAAK,EAAE,CAAC;YACV,+EAA+E;YAC/E,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAA;YACnD,KAAK,MAAM,CAAC,IAAI,KAAK;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;YACrE,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACrG,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,IAAI,IAAI,CAAC,CAAA;QAC/C,IAAI,GAAG,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;YAAE,OAAM;QAClG,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,eAAe,6BAA6B,EAAE,IAAI,CAAC,MAAM,EAAE;gBAChF,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChG,CAAC,CAAA;YACF,IAAI,CAAC,YAAY,GAAG,GAAG,CAAA;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,yBAAyB,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,EAAE,kDAAkD,CAAC,CAAA;QACnI,CAAC;IACH,CAAC;CACF"}
package/dist/session.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * process (`mcpAutoWrite.ts`'s `wrapWithProxy`, in both `per-session` and
9
9
  * `daemon` mode), so one harness session runs one proxy per wrapped server.
10
10
  * This class holds the tool-call sequence and the per-detector reask counters
11
- * the anomaly detectors read. Until Wave 5.3 (TD-437) that state was
11
+ * the anomaly detectors read. Until Wave 5.3 that state was
12
12
  * per-process only — a cross-server ping-pong was invisible, a reask budget
13
13
  * reset per server and per restart — and the module doc claimed there was
14
14
  * "no second instance to share state with", which was never true of the
package/dist/session.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * process (`mcpAutoWrite.ts`'s `wrapWithProxy`, in both `per-session` and
9
9
  * `daemon` mode), so one harness session runs one proxy per wrapped server.
10
10
  * This class holds the tool-call sequence and the per-detector reask counters
11
- * the anomaly detectors read. Until Wave 5.3 (TD-437) that state was
11
+ * the anomaly detectors read. Until Wave 5.3 that state was
12
12
  * per-process only — a cross-server ping-pong was invisible, a reask budget
13
13
  * reset per server and per restart — and the module doc claimed there was
14
14
  * "no second instance to share state with", which was never true of the
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * sessionScope.ts — the shared-window scope a proxy process derives for
3
- * itself (TD-437, Wave 5.3).
3
+ * itself (Wave 5.3).
4
4
  *
5
5
  * The sync daemon wraps EACH MCP server entry with its own proxy process, so
6
6
  * one harness session runs several `McpGovernanceProxy` processes at once —
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * sessionScope.ts — the shared-window scope a proxy process derives for
3
- * itself (TD-437, Wave 5.3).
3
+ * itself (Wave 5.3).
4
4
  *
5
5
  * The sync daemon wraps EACH MCP server entry with its own proxy process, so
6
6
  * one harness session runs several `McpGovernanceProxy` processes at once —
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * sessionStore.ts — the Valkey-backed session window the sibling proxy
3
- * processes of one harness session share (TD-437, Wave 5.3).
3
+ * processes of one harness session share (Wave 5.3).
4
4
  *
5
5
  * The Rust LLM proxy keeps a session's tool-call history in Valkey
6
6
  * (`store/valkey.rs`: a capped LIST for the sequence, a ZSET for the 60 s
@@ -12,15 +12,15 @@
12
12
  *
13
13
  * Never a hard dependency. Every operation fails open to `undefined`/`false`
14
14
  * and the caller (`SessionState`) falls back to its in-process window: when
15
- * no URL is configured, when the client is not connected (`status !==
16
- * 'ready'`, so an absent Valkey adds no latency), when a call takes longer
17
- * than {@link SHARED_WINDOW_TIMEOUT_MS}, or on any error. The proxy behaves
18
- * exactly as before Wave 5.3 in every one of those cases.
15
+ * no URL is configured, when the client is not connected (so an absent
16
+ * Valkey adds no latency), when a call takes longer than
17
+ * `VALKEY_COMMAND_TIMEOUT_MS`, or on any error — the guard every command on
18
+ * the proxy's one Valkey connection goes through (guardedValkey.ts). The
19
+ * proxy behaves exactly as before Wave 5.3 in every one of those cases.
19
20
  *
20
21
  * @module
21
22
  */
22
- /** A shared read or write that takes longer than this loses to the local window. */
23
- export declare const SHARED_WINDOW_TIMEOUT_MS = 200;
23
+ import type { GuardedValkey } from './guardedValkey.js';
24
24
  /** Sliding lifetime of the sequence and call-window keys — the Rust `TOOL_SEQUENCE_TTL_SECS`. */
25
25
  export declare const SESSION_WINDOW_TTL_SECS = 86400;
26
26
  /** Lifetime of a reask allowance, set when the counter is created and never refreshed —
@@ -44,26 +44,13 @@ export interface SharedSessionStore {
44
44
  recordCall(scope: string, toolName: string, nowMs: number): Promise<boolean>;
45
45
  /** The count including this trip, or `undefined` when unavailable. */
46
46
  incrReaskAttempt(scope: string, key: string): Promise<number | undefined>;
47
- close(): Promise<void>;
48
- }
49
- export interface ValkeySessionStoreOptions {
50
- timeoutMs?: number;
51
47
  }
52
48
  export declare class ValkeySessionStore implements SharedSessionStore {
53
- private readonly client;
54
- private readonly timeoutMs;
55
- private warned;
56
- constructor(url: string, opts?: ValkeySessionStoreOptions);
49
+ private readonly valkey;
50
+ /** The proxy's one Valkey connection, shared with its call budgets; the proxy closes it. */
51
+ constructor(valkey: GuardedValkey);
57
52
  readWindow(scope: string, nowMs: number, windowMs: number): Promise<SharedWindowSnapshot | undefined>;
58
53
  recordCall(scope: string, toolName: string, nowMs: number): Promise<boolean>;
59
54
  incrReaskAttempt(scope: string, key: string): Promise<number | undefined>;
60
- close(): Promise<void>;
61
- /**
62
- * The fail-open wrapper every operation goes through: nothing is attempted
63
- * unless the client is connected, nothing waits longer than the timeout,
64
- * and any failure is `undefined`.
65
- */
66
- private guarded;
67
- private warnOnce;
68
55
  }
69
56
  //# sourceMappingURL=sessionStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionStore.d.ts","sourceRoot":"","sources":["../src/sessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAC3C,iGAAiG;AACjG,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAC7C;0EAC0E;AAC1E,eAAO,MAAM,iBAAiB,OAAQ,CAAA;AAEtC,eAAO,MAAM,WAAW;4BACP,MAAM;4BACN,MAAM;4BACN,MAAM,OAAO,MAAM;CAC1B,CAAA;AAEV,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;IACrG,sEAAsE;IACtE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5E,sEAAsE;IACtE,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,kBAAmB,YAAW,kBAAkB;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAO;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,MAAM,CAAQ;gBAEV,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,yBAA8B;IAevD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAcrG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB5E,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IASzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B;;;;OAIG;YACW,OAAO;IAiBrB,OAAO,CAAC,QAAQ;CASjB"}
1
+ {"version":3,"file":"sessionStore.d.ts","sourceRoot":"","sources":["../src/sessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGvD,iGAAiG;AACjG,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAC7C;0EAC0E;AAC1E,eAAO,MAAM,iBAAiB,OAAQ,CAAA;AAEtC,eAAO,MAAM,WAAW;4BACP,MAAM;4BACN,MAAM;4BACN,MAAM,OAAO,MAAM;CAC1B,CAAA;AAEV,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;IACrG,sEAAsE;IACtE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5E,sEAAsE;IACtE,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CAC1E;AAED,qBAAa,kBAAmB,YAAW,kBAAkB;IAE/C,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,4FAA4F;gBAC/D,MAAM,EAAE,aAAa;IAE5C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAcrG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB5E,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAQhF"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * sessionStore.ts — the Valkey-backed session window the sibling proxy
3
- * processes of one harness session share (TD-437, Wave 5.3).
3
+ * processes of one harness session share (Wave 5.3).
4
4
  *
5
5
  * The Rust LLM proxy keeps a session's tool-call history in Valkey
6
6
  * (`store/valkey.rs`: a capped LIST for the sequence, a ZSET for the 60 s
@@ -12,21 +12,16 @@
12
12
  *
13
13
  * Never a hard dependency. Every operation fails open to `undefined`/`false`
14
14
  * and the caller (`SessionState`) falls back to its in-process window: when
15
- * no URL is configured, when the client is not connected (`status !==
16
- * 'ready'`, so an absent Valkey adds no latency), when a call takes longer
17
- * than {@link SHARED_WINDOW_TIMEOUT_MS}, or on any error. The proxy behaves
18
- * exactly as before Wave 5.3 in every one of those cases.
15
+ * no URL is configured, when the client is not connected (so an absent
16
+ * Valkey adds no latency), when a call takes longer than
17
+ * `VALKEY_COMMAND_TIMEOUT_MS`, or on any error — the guard every command on
18
+ * the proxy's one Valkey connection goes through (guardedValkey.ts). The
19
+ * proxy behaves exactly as before Wave 5.3 in every one of those cases.
19
20
  *
20
21
  * @module
21
22
  */
22
23
  import * as node_crypto from 'node:crypto';
23
- import { Redis } from 'ioredis';
24
- import { createStderrLogger } from './stderrLog.js';
25
- import { describeConnectionError } from './valkeyErrors.js';
26
24
  import { TOOL_SEQUENCE_CAP } from './session.js';
27
- const log = createStderrLogger('mcp-proxy.sessionStore');
28
- /** A shared read or write that takes longer than this loses to the local window. */
29
- export const SHARED_WINDOW_TIMEOUT_MS = 200;
30
25
  /** Sliding lifetime of the sequence and call-window keys — the Rust `TOOL_SEQUENCE_TTL_SECS`. */
31
26
  export const SESSION_WINDOW_TTL_SECS = 86_400;
32
27
  /** Lifetime of a reask allowance, set when the counter is created and never refreshed —
@@ -38,26 +33,14 @@ export const sessionKeys = {
38
33
  reask: (scope, key) => `v2:mcpsession:${scope}:reask:${key}`,
39
34
  };
40
35
  export class ValkeySessionStore {
41
- client;
42
- timeoutMs;
43
- warned = false;
44
- constructor(url, opts = {}) {
45
- this.timeoutMs = opts.timeoutMs ?? SHARED_WINDOW_TIMEOUT_MS;
46
- this.client = new Redis(url, {
47
- lazyConnect: true,
48
- // No offline queue: a command issued before the connection is up must
49
- // fail now, not wait for a Valkey that may never answer.
50
- enableOfflineQueue: false,
51
- maxRetriesPerRequest: 1,
52
- connectTimeout: 1000,
53
- retryStrategy: (times) => Math.min(1000 * 2 ** Math.min(times, 5), 30_000),
54
- });
55
- this.client.on('error', (err) => this.warnOnce('Valkey session store unreachable', err));
56
- void this.client.connect().catch((err) => this.warnOnce('Valkey session store connect failed', err));
36
+ valkey;
37
+ /** The proxy's one Valkey connection, shared with its call budgets; the proxy closes it. */
38
+ constructor(valkey) {
39
+ this.valkey = valkey;
57
40
  }
58
41
  async readWindow(scope, nowMs, windowMs) {
59
- return this.guarded(async () => {
60
- const results = await this.client
42
+ return this.valkey.run(async (client) => {
43
+ const results = await client
61
44
  .pipeline()
62
45
  .lrange(sessionKeys.tools(scope), 0, -1)
63
46
  .zcount(sessionKeys.calls(scope), nowMs - windowMs, '+inf')
@@ -70,10 +53,10 @@ export class ValkeySessionStore {
70
53
  });
71
54
  }
72
55
  async recordCall(scope, toolName, nowMs) {
73
- const done = await this.guarded(async () => {
56
+ const done = await this.valkey.run(async (client) => {
74
57
  const tools = sessionKeys.tools(scope);
75
58
  const calls = sessionKeys.calls(scope);
76
- await this.client
59
+ await client
77
60
  .pipeline()
78
61
  .rpush(tools, toolName)
79
62
  .ltrim(tools, -TOOL_SEQUENCE_CAP, -1)
@@ -89,55 +72,13 @@ export class ValkeySessionStore {
89
72
  return done === true;
90
73
  }
91
74
  async incrReaskAttempt(scope, key) {
92
- return this.guarded(async () => {
75
+ return this.valkey.run(async (client) => {
93
76
  const k = sessionKeys.reask(scope, key);
94
- const n = await this.client.incr(k);
77
+ const n = await client.incr(k);
95
78
  if (n === 1)
96
- await this.client.expire(k, REASK_WINDOW_SECS);
79
+ await client.expire(k, REASK_WINDOW_SECS);
97
80
  return n;
98
81
  });
99
82
  }
100
- async close() {
101
- try {
102
- await this.client.quit();
103
- }
104
- catch {
105
- this.client.disconnect();
106
- }
107
- }
108
- /**
109
- * The fail-open wrapper every operation goes through: nothing is attempted
110
- * unless the client is connected, nothing waits longer than the timeout,
111
- * and any failure is `undefined`.
112
- */
113
- async guarded(op) {
114
- if (this.client.status !== 'ready')
115
- return undefined;
116
- let timer;
117
- const timeout = new Promise((resolve) => {
118
- timer = setTimeout(() => resolve(undefined), this.timeoutMs);
119
- timer.unref();
120
- });
121
- try {
122
- return await Promise.race([op(), timeout]);
123
- }
124
- catch (err) {
125
- this.warnOnce('Valkey session store operation failed', err);
126
- return undefined;
127
- }
128
- finally {
129
- if (timer)
130
- clearTimeout(timer);
131
- }
132
- }
133
- warnOnce(msg, err) {
134
- const detail = { err: describeConnectionError(err) };
135
- if (this.warned) {
136
- log.debug(detail, msg);
137
- return;
138
- }
139
- this.warned = true;
140
- log.warn(detail, `${msg} — falling back to the per-process session window`);
141
- }
142
83
  }
143
84
  //# sourceMappingURL=sessionStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionStore.js","sourceRoot":"","sources":["../src/sessionStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessionStore.js","sourceRoot":"","sources":["../src/sessionStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * wasm/cloudRules.ts — the workspace's custom rules from the control plane:
3
+ * the rules uploaded from the dashboard, the CLI or Terraform, which the LLM
4
+ * proxy enforces too.
5
+ *
6
+ * Port of the control-plane half of `packages/proxy/src/wasm/registry.rs`
7
+ * (`ensure_up_to_date`, `load_descriptor`, `report_refusal`). The descriptors
8
+ * arrive with the workspace policy this proxy already fetches (`wasmRules` on
9
+ * `GET /api/v1/sop/rules`, or on the MCP daemon's cached
10
+ * `GET /api/v1/policy/resolve`), so they refresh when the policy does. Each
11
+ * binary is fetched by the SHA-256 its descriptor names, only when no loaded
12
+ * rule already has that hash, and must hash to it before it is compiled:
13
+ * whoever can answer for that hash could otherwise swap a governance rule for
14
+ * one that allows everything.
15
+ *
16
+ * A version that cannot load — binary missing, hash mismatch, or a module the
17
+ * worker refuses — is reported once per hash and reason, and the version of
18
+ * that rule already loaded, if any, keeps enforcing. A control plane that
19
+ * cannot be reached for a binary leaves the whole loaded set as it was, as
20
+ * `registry.rs` does when its fetch fails.
21
+ *
22
+ * @module
23
+ */
24
+ import type { WasmRuleDescriptor, WasmRuleMode } from '@intutic/shared-types';
25
+ import type { WasmRuleRefusalDetail } from '../emitter.js';
26
+ import { type CompileBridge, type CompileFailureReason } from './loader.js';
27
+ /**
28
+ * Minimum time between attempts after a sync that could not reach the control
29
+ * plane or refused a version — the Rust proxy's 5-second rescan, so a binary
30
+ * published a moment after its descriptor is picked up as quickly there.
31
+ */
32
+ export declare const CLOUD_RETRY_MS = 5000;
33
+ /** A control-plane rule loaded into the worker. */
34
+ export interface CloudRuleMeta {
35
+ /** The control plane's rule id (`wasm_…`), reported as is, like the Rust proxy does. */
36
+ ruleId: string;
37
+ name: string;
38
+ sha256: string;
39
+ priority: number;
40
+ mode: WasmRuleMode;
41
+ readsReferencedFiles: boolean;
42
+ rego: boolean;
43
+ /** Kept so a worker respawn can recompile without fetching again. */
44
+ bytes: Uint8Array;
45
+ }
46
+ /**
47
+ * Why a control-plane rule version was not loaded (`registry.rs`'s `Refusal`).
48
+ * `kind` is the reason the control plane files the incident under
49
+ * (`RULE_LOAD_FAILURE_REASONS`): a module the worker refuses carries the
50
+ * worker's own reason for it.
51
+ */
52
+ export type CloudRuleRefusal = {
53
+ kind: 'missing';
54
+ } | {
55
+ kind: 'hash_mismatch';
56
+ actual: string;
57
+ } | {
58
+ kind: CompileFailureReason;
59
+ error: string;
60
+ };
61
+ /** One refused version, reported once per hash and reason. */
62
+ export interface CloudRuleRefusalReport {
63
+ descriptor: WasmRuleDescriptor;
64
+ refusal: CloudRuleRefusal;
65
+ /** Whether an earlier version of the rule stays in force. */
66
+ hasPrevious: boolean;
67
+ /** The sentence the incident and the log line carry, in the Rust proxy's words. */
68
+ description: string;
69
+ }
70
+ /**
71
+ * The binary a hash names: its bytes, or `null` when the control plane has
72
+ * none. Throws when the control plane cannot be asked.
73
+ */
74
+ export type FetchRuleBinary = (sha256: string) => Promise<Uint8Array | null>;
75
+ /** What the sync needs from the worker, implemented by `WasmRunner`. */
76
+ export interface CloudRuleBridge extends CompileBridge {
77
+ /** A rule's loaded version changed: whatever the previous version did no longer counts against it. */
78
+ replaced(ruleId: string): void;
79
+ }
80
+ /**
81
+ * Fetches binaries from the control plane's rule store by hash. A 404 is a
82
+ * binary the control plane does not have; any other failure means it could
83
+ * not be asked.
84
+ */
85
+ export declare function fetchRuleBinaryFrom(controlPlaneUrl: string, apiKey: string): FetchRuleBinary;
86
+ /** A refusal as the `wasm_rule_refused` event carries it. */
87
+ export declare function refusalDetail(report: CloudRuleRefusalReport): WasmRuleRefusalDetail;
88
+ /** Whether `bytes` are the binary `sha256` names; case is not significant in hex. */
89
+ export declare function binaryMatches(bytes: Uint8Array, sha256: string): {
90
+ matches: boolean;
91
+ actual: string;
92
+ };
93
+ export declare function describeRefusal(descriptor: WasmRuleDescriptor, refusal: CloudRuleRefusal, hasPrevious: boolean): string;
94
+ export declare class CloudRuleSet {
95
+ private readonly fetchBinary;
96
+ private readonly report;
97
+ private rules;
98
+ /** The descriptor list last applied, serialized; `undefined` until one has been. */
99
+ private appliedKey;
100
+ /** The list of the last attempt, applied or not, and when it ran. */
101
+ private attemptedKey;
102
+ private attemptedAt;
103
+ /** Whether the applied list left a version unloaded, so it is retried. */
104
+ private incomplete;
105
+ private syncing;
106
+ private readonly refusalsReported;
107
+ constructor(fetchBinary: FetchRuleBinary, report: (report: CloudRuleRefusalReport) => void);
108
+ /** Whether a descriptor list has been applied, so the loaded set is the workspace's. */
109
+ isLoaded(): boolean;
110
+ /** The loaded rules, in descriptor order. */
111
+ getRules(): readonly CloudRuleMeta[];
112
+ /**
113
+ * Brings the loaded set in line with `descriptors`.
114
+ *
115
+ * A changed list is applied before this resolves, so the call that follows
116
+ * a policy refresh is judged by the new set. Retrying versions the applied
117
+ * list refused, at most every {@link CLOUD_RETRY_MS}, runs in the
118
+ * background: the loaded set is already the workspace's. A list that could
119
+ * not be applied is retried no sooner either, so an unreachable control
120
+ * plane costs a call one fetch timeout at most every few seconds rather
121
+ * than on every call.
122
+ */
123
+ sync(descriptors: readonly WasmRuleDescriptor[], bridge: CloudRuleBridge): Promise<void>;
124
+ /**
125
+ * Recompiles every loaded rule from its kept bytes — the worker that held
126
+ * them was replaced. Waits for a sync in flight, which may have compiled
127
+ * into the old worker.
128
+ */
129
+ reload(bridge: CompileBridge): Promise<void>;
130
+ private run;
131
+ private apply;
132
+ /** A binary some loaded rule already has: compiled again only when it is new to this rule id. */
133
+ private reuse;
134
+ /** `load_descriptor`: the hash, then the same load checks as a local rule. */
135
+ private load;
136
+ private reportOnce;
137
+ }
138
+ //# sourceMappingURL=cloudRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudRules.d.ts","sourceRoot":"","sources":["../../src/wasm/cloudRules.ts"],"names":[],"mappings":"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"}