@intutic/mcp-governance-proxy 2.2.0 → 2.3.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.
- package/README.md +7 -6
- package/dist/agentTools.d.ts +68 -0
- package/dist/agentTools.d.ts.map +1 -0
- package/dist/agentTools.js +150 -0
- package/dist/agentTools.js.map +1 -0
- package/dist/anomaly/detectors.js +1 -1
- package/dist/anomaly/detectors.js.map +1 -1
- package/dist/approvalHold.d.ts +8 -7
- package/dist/approvalHold.d.ts.map +1 -1
- package/dist/approvalHold.js +7 -6
- package/dist/approvalHold.js.map +1 -1
- package/dist/budget.d.ts +133 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +191 -0
- package/dist/budget.js.map +1 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +0 -1
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/index.d.ts +0 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +0 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/policyCache.d.ts +15 -3
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +36 -15
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/daemon/socketServer.d.ts +0 -1
- package/dist/daemon/socketServer.d.ts.map +1 -1
- package/dist/daemon/socketServer.js +0 -1
- package/dist/daemon/socketServer.js.map +1 -1
- package/dist/daemon/statusReporter.d.ts.map +1 -1
- package/dist/daemon/statusReporter.js +0 -1
- package/dist/daemon/statusReporter.js.map +1 -1
- package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
- package/dist/daemon/telemetryBatcher.js +0 -1
- package/dist/daemon/telemetryBatcher.js.map +1 -1
- package/dist/daemonClient.d.ts.map +1 -1
- package/dist/daemonClient.js +0 -1
- package/dist/daemonClient.js.map +1 -1
- package/dist/dlp.d.ts +32 -3
- package/dist/dlp.d.ts.map +1 -1
- package/dist/dlp.js +116 -18
- package/dist/dlp.js.map +1 -1
- package/dist/dlpPii.d.ts +66 -0
- package/dist/dlpPii.d.ts.map +1 -0
- package/dist/dlpPii.js +235 -0
- package/dist/dlpPii.js.map +1 -0
- package/dist/emitter.d.ts +27 -3
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +7 -1
- package/dist/emitter.js.map +1 -1
- package/dist/guardedValkey.d.ts +40 -0
- package/dist/guardedValkey.d.ts.map +1 -0
- package/dist/guardedValkey.js +84 -0
- package/dist/guardedValkey.js.map +1 -0
- package/dist/injection.js +1 -1
- package/dist/interceptor.d.ts +53 -10
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +154 -65
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +21 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +29 -2
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +25 -6
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +113 -51
- package/dist/proxy.js.map +1 -1
- package/dist/refusals.d.ts +26 -0
- package/dist/refusals.d.ts.map +1 -0
- package/dist/refusals.js +43 -0
- package/dist/refusals.js.map +1 -0
- package/dist/registryObserver.d.ts +9 -5
- package/dist/registryObserver.d.ts.map +1 -1
- package/dist/registryObserver.js +19 -8
- package/dist/registryObserver.js.map +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/sessionScope.d.ts +1 -1
- package/dist/sessionScope.js +1 -1
- package/dist/sessionStore.d.ts +10 -23
- package/dist/sessionStore.d.ts.map +1 -1
- package/dist/sessionStore.js +17 -76
- package/dist/sessionStore.js.map +1 -1
- package/dist/wasm/context.d.ts +10 -0
- package/dist/wasm/context.d.ts.map +1 -1
- package/dist/wasm/context.js +27 -0
- package/dist/wasm/context.js.map +1 -1
- package/dist/wasm/hostImports.d.ts +3 -3
- package/dist/wasm/hostImports.d.ts.map +1 -1
- package/dist/wasm/hostImports.js +8 -7
- package/dist/wasm/hostImports.js.map +1 -1
- package/dist/wasm/loader.d.ts +8 -1
- package/dist/wasm/loader.d.ts.map +1 -1
- package/dist/wasm/loader.js +2 -0
- package/dist/wasm/loader.js.map +1 -1
- package/dist/wasm/referencedFiles.d.ts +29 -4
- package/dist/wasm/referencedFiles.d.ts.map +1 -1
- package/dist/wasm/referencedFiles.js +48 -21
- package/dist/wasm/referencedFiles.js.map +1 -1
- package/dist/wasm/runner.d.ts +106 -22
- package/dist/wasm/runner.d.ts.map +1 -1
- package/dist/wasm/runner.js +194 -74
- package/dist/wasm/runner.js.map +1 -1
- package/dist/wasm/watch.js +1 -1
- package/dist/wasm/worker.d.ts +10 -6
- package/dist/wasm/worker.d.ts.map +1 -1
- package/dist/wasm/worker.js +93 -9
- package/dist/wasm/worker.js.map +1 -1
- package/package.json +5 -4
- package/dist/toolPoison.d.ts +0 -32
- package/dist/toolPoison.d.ts.map +0 -1
- package/dist/toolPoison.js +0 -81
- package/dist/toolPoison.js.map +0 -1
- package/dist/wasm/fuel.d.ts +0 -35
- package/dist/wasm/fuel.d.ts.map +0 -1
- package/dist/wasm/fuel.js +0 -299
- package/dist/wasm/fuel.js.map +0 -1
- package/dist/wasm/memoryCap.d.ts +0 -21
- package/dist/wasm/memoryCap.d.ts.map +0 -1
- package/dist/wasm/memoryCap.js +0 -54
- package/dist/wasm/memoryCap.js.map +0 -1
- package/dist/wasm/wasmBinary.d.ts +0 -25
- package/dist/wasm/wasmBinary.d.ts.map +0 -1
- package/dist/wasm/wasmBinary.js +0 -100
- package/dist/wasm/wasmBinary.js.map +0 -1
package/dist/dlpPii.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dlpPii.ts — validated PII detectors (card and IBAN checksums, SSN issued ranges).
|
|
3
|
+
*
|
|
4
|
+
* The TypeScript twin of the Rust proxy's `packages/proxy/src/dlp/pii.rs`.
|
|
5
|
+
* Both read the same definition (`PII_DEFINITION` from
|
|
6
|
+
* `@intutic/shared-types`, a byte-identical copy of the proxy's
|
|
7
|
+
* `pii_detectors.json`) and implement its validators and boundary rule the
|
|
8
|
+
* same way; `packages/shared-types/fixtures/pii-detector-vectors.json` runs
|
|
9
|
+
* through both (`__tests__/dlpPii.test.ts`, `tests/pii_conformance_test.rs`).
|
|
10
|
+
* A change to a rule here needs the same change there, or the vectors fail on
|
|
11
|
+
* one side.
|
|
12
|
+
*
|
|
13
|
+
* The regex finds a candidate; the validator decides. Luhn plus the IIN table
|
|
14
|
+
* for cards, the country length table plus mod-97 for IBANs, the never-issued
|
|
15
|
+
* ranges for SSNs, structure for email, digit count for phone numbers.
|
|
16
|
+
*
|
|
17
|
+
* The boundary rule replaces `\b`, which means different things in the two
|
|
18
|
+
* engines. Neighbouring characters must not be ASCII letters, digits or `_`,
|
|
19
|
+
* except that a match right after a JSON escape (`\n`, `\r`, `\t`) counts as
|
|
20
|
+
* bounded — both scanners read JSON as sent. A match starting right after a
|
|
21
|
+
* backslash starts inside an escape and is rejected. `numeric` detectors
|
|
22
|
+
* also reject a match glued to more digits by `.` or `-` (a decimal fraction,
|
|
23
|
+
* a five-group serial). A rejected candidate resumes the search one character
|
|
24
|
+
* after its start, so a card number right after `12 ` is still found.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { PII_DEFINITION, PII_DETECTOR_IDS } from '@intutic/shared-types';
|
|
29
|
+
const CARD_BRANDS = PII_DEFINITION.card_brands;
|
|
30
|
+
const IBAN_LENGTHS = PII_DEFINITION.iban_lengths;
|
|
31
|
+
const isDigit = (c) => c >= 48 && c <= 57;
|
|
32
|
+
const isWord = (c) => isDigit(c) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95;
|
|
33
|
+
const BACKSLASH = 92;
|
|
34
|
+
const DOT = 46;
|
|
35
|
+
const DASH = 45;
|
|
36
|
+
function luhn(digits) {
|
|
37
|
+
let sum = 0;
|
|
38
|
+
for (let i = 0; i < digits.length; i++) {
|
|
39
|
+
let d = digits.charCodeAt(digits.length - 1 - i) - 48;
|
|
40
|
+
if (i % 2 === 1) {
|
|
41
|
+
d *= 2;
|
|
42
|
+
if (d > 9)
|
|
43
|
+
d -= 9;
|
|
44
|
+
}
|
|
45
|
+
sum += d;
|
|
46
|
+
}
|
|
47
|
+
return sum % 10 === 0;
|
|
48
|
+
}
|
|
49
|
+
function card(span) {
|
|
50
|
+
const digits = span.replace(/[^0-9]/g, '');
|
|
51
|
+
const n = digits.length;
|
|
52
|
+
const known = CARD_BRANDS.some((b) => b.lengths.includes(n) &&
|
|
53
|
+
b.prefixes.some(([lo, hi]) => {
|
|
54
|
+
const head = digits.slice(0, lo.length);
|
|
55
|
+
return head >= lo && head <= hi;
|
|
56
|
+
}));
|
|
57
|
+
return known && luhn(digits);
|
|
58
|
+
}
|
|
59
|
+
/** Truncates to the country's length: the regex is greedy and runs on into `EUR`. */
|
|
60
|
+
function iban(span) {
|
|
61
|
+
const want = IBAN_LENGTHS[span.slice(0, 2)];
|
|
62
|
+
if (want === undefined)
|
|
63
|
+
return null;
|
|
64
|
+
let chars = '';
|
|
65
|
+
let end = span.length;
|
|
66
|
+
for (let i = 0; i < span.length; i++) {
|
|
67
|
+
const c = span[i];
|
|
68
|
+
if (c === ' ')
|
|
69
|
+
continue;
|
|
70
|
+
if (chars.length === want) {
|
|
71
|
+
// A further character glued on (no space) makes it longer than any
|
|
72
|
+
// IBAN of this country.
|
|
73
|
+
if (span[i - 1] !== ' ')
|
|
74
|
+
return null;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
chars += c;
|
|
78
|
+
end = i + 1;
|
|
79
|
+
}
|
|
80
|
+
if (chars.length !== want)
|
|
81
|
+
return null;
|
|
82
|
+
const rotated = chars.slice(4) + chars.slice(0, 4);
|
|
83
|
+
let rem = 0;
|
|
84
|
+
for (const c of rotated) {
|
|
85
|
+
const code = c.charCodeAt(0);
|
|
86
|
+
const v = isDigit(code) ? code - 48 : code - 65 + 10;
|
|
87
|
+
rem = v >= 10 ? (rem * 100 + v) % 97 : (rem * 10 + v) % 97;
|
|
88
|
+
}
|
|
89
|
+
return rem === 1 ? end : null;
|
|
90
|
+
}
|
|
91
|
+
/** Never issued: area 000, 666 or 9xx; group 00; serial 0000. */
|
|
92
|
+
function ssn(span) {
|
|
93
|
+
const area = span.slice(0, 3);
|
|
94
|
+
const group = span.slice(4, 6);
|
|
95
|
+
const serial = span.slice(7, 11);
|
|
96
|
+
return area !== '000' && area !== '666' && area[0] !== '9' && group !== '00' && serial !== '0000';
|
|
97
|
+
}
|
|
98
|
+
function email(span) {
|
|
99
|
+
const at = span.indexOf('@');
|
|
100
|
+
if (at < 0)
|
|
101
|
+
return false;
|
|
102
|
+
const local = span.slice(0, at);
|
|
103
|
+
const domain = span.slice(at + 1);
|
|
104
|
+
if (local.startsWith('.') || local.endsWith('.') || local.includes('..'))
|
|
105
|
+
return false;
|
|
106
|
+
const labels = domain.split('.');
|
|
107
|
+
if (labels.some((l) => l === '' || l.startsWith('-') || l.endsWith('-')))
|
|
108
|
+
return false;
|
|
109
|
+
const tld = labels[labels.length - 1];
|
|
110
|
+
if (tld.length < 2 || !/^[A-Za-z]+$/.test(tld))
|
|
111
|
+
return false;
|
|
112
|
+
// `icon@2x.png`: an asset scale suffix, not an address.
|
|
113
|
+
const first = labels[0];
|
|
114
|
+
return !/^[0-9]+x$/.test(first);
|
|
115
|
+
}
|
|
116
|
+
function phone(span) {
|
|
117
|
+
const n = span.replace(/[^0-9]/g, '').length;
|
|
118
|
+
return n >= 10 && n <= 15;
|
|
119
|
+
}
|
|
120
|
+
const whole = (check) => (span) => check(span) ? span.length : null;
|
|
121
|
+
const VALIDATORS = {
|
|
122
|
+
card_luhn: whole(card),
|
|
123
|
+
iban_mod97: iban,
|
|
124
|
+
ssn_ranges: whole(ssn),
|
|
125
|
+
email_shape: whole(email),
|
|
126
|
+
phone_digits: whole(phone),
|
|
127
|
+
};
|
|
128
|
+
/** Every PII detector, in definition order, whatever its configured action. */
|
|
129
|
+
export const PII_DETECTORS = PII_DEFINITION.detectors.map((d) => {
|
|
130
|
+
const validate = VALIDATORS[d.validator];
|
|
131
|
+
if (!validate)
|
|
132
|
+
throw new Error(`pii detector ${d.id} names unknown validator ${d.validator}`);
|
|
133
|
+
return {
|
|
134
|
+
id: d.id,
|
|
135
|
+
category: d.category,
|
|
136
|
+
description: d.description,
|
|
137
|
+
regex: new RegExp(d.regex, 'g'),
|
|
138
|
+
numeric: d.boundary === 'numeric',
|
|
139
|
+
validate,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
function bounded(text, start, end, numeric) {
|
|
143
|
+
if (start > 0) {
|
|
144
|
+
const p = text.charCodeAt(start - 1);
|
|
145
|
+
if (p === BACKSLASH)
|
|
146
|
+
return false;
|
|
147
|
+
const escaped = (p === 110 || p === 114 || p === 116) && start >= 2 && text.charCodeAt(start - 2) === BACKSLASH;
|
|
148
|
+
if (isWord(p) && !escaped)
|
|
149
|
+
return false;
|
|
150
|
+
if (numeric && (p === DOT || p === DASH) && start >= 2 && isDigit(text.charCodeAt(start - 2)))
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (end < text.length) {
|
|
154
|
+
const q = text.charCodeAt(end);
|
|
155
|
+
if (isWord(q))
|
|
156
|
+
return false;
|
|
157
|
+
if (numeric && (q === DOT || q === DASH) && end + 1 < text.length && isDigit(text.charCodeAt(end + 1))) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
/** Validated matches of one detector, as `[start, end)` UTF-16 ranges, in text order. */
|
|
164
|
+
export function findPii(det, text) {
|
|
165
|
+
const out = [];
|
|
166
|
+
const re = det.regex;
|
|
167
|
+
let pos = 0;
|
|
168
|
+
while (pos < text.length) {
|
|
169
|
+
re.lastIndex = pos;
|
|
170
|
+
const m = re.exec(text);
|
|
171
|
+
if (!m)
|
|
172
|
+
break;
|
|
173
|
+
const start = m.index;
|
|
174
|
+
const len = det.validate(m[0]);
|
|
175
|
+
const end = len === null ? null : start + len;
|
|
176
|
+
if (end !== null && bounded(text, start, end, det.numeric)) {
|
|
177
|
+
out.push([start, end]);
|
|
178
|
+
pos = end;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
pos = start + 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Every detector over `text`, actions ignored, in definition order then text
|
|
188
|
+
* order — what the conformance vectors measure.
|
|
189
|
+
*/
|
|
190
|
+
export function detectPii(text) {
|
|
191
|
+
const out = [];
|
|
192
|
+
for (const det of PII_DETECTORS) {
|
|
193
|
+
for (const [start, end] of findPii(det, text))
|
|
194
|
+
out.push({ id: det.id, start, end });
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The detectors an `INTUTIC_MCP_DLP_DETECTORS` value names — a JSON object of
|
|
200
|
+
* detector id → `off` | `redact` | `block` — with their actions. Detectors it
|
|
201
|
+
* leaves out are not listed: they keep their defaults, or the workspace's
|
|
202
|
+
* action (`effectivePiiActions` in `@intutic/shared-types`). Never throws,
|
|
203
|
+
* like the other `INTUTIC_MCP_*` JSON settings: an unparseable value, an
|
|
204
|
+
* unknown id or an unknown action is reported in `problems` and that entry is
|
|
205
|
+
* left out, so it keeps its default, which for card, IBAN and SSN is on.
|
|
206
|
+
*/
|
|
207
|
+
export function parseLocalPiiActions(raw) {
|
|
208
|
+
const configured = {};
|
|
209
|
+
if (!raw)
|
|
210
|
+
return { configured, problems: [] };
|
|
211
|
+
let parsed;
|
|
212
|
+
try {
|
|
213
|
+
parsed = JSON.parse(raw);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return { configured, problems: ['INTUTIC_MCP_DLP_DETECTORS is not valid JSON; using the defaults'] };
|
|
217
|
+
}
|
|
218
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
219
|
+
return { configured, problems: ['INTUTIC_MCP_DLP_DETECTORS is not a JSON object; using the defaults'] };
|
|
220
|
+
}
|
|
221
|
+
const problems = [];
|
|
222
|
+
for (const [id, action] of Object.entries(parsed)) {
|
|
223
|
+
if (!PII_DETECTOR_IDS.includes(id)) {
|
|
224
|
+
problems.push(`INTUTIC_MCP_DLP_DETECTORS names unknown detector ${id}; known: ${PII_DETECTOR_IDS.join(', ')}`);
|
|
225
|
+
}
|
|
226
|
+
else if (action !== 'off' && action !== 'redact' && action !== 'block') {
|
|
227
|
+
problems.push(`INTUTIC_MCP_DLP_DETECTORS sets ${id} to ${String(action)}; only off, redact and block exist`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
configured[id] = action;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return { configured, problems };
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=dlpPii.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dlpPii.js","sourceRoot":"","sources":["../src/dlpPii.ts"],"names":[],"mappings":"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"}
|
package/dist/emitter.d.ts
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { CallerIdentity } from './identity.js';
|
|
12
12
|
import type { AnomalyFinding } from './anomaly/index.js';
|
|
13
|
+
import type { BudgetEventDetail } from './budget.js';
|
|
13
14
|
export type EventKind = 'tool_allowed' | 'tool_blocked' | 'tool_redacted'
|
|
14
15
|
/**
|
|
15
|
-
* A `warn` SOP rule matched and the call was allowed
|
|
16
|
+
* A `warn` SOP rule matched and the call was allowed: the same
|
|
16
17
|
* event the harness gates log for a warn-severity rule, carrying the rule
|
|
17
18
|
* id as `[<id>]` in `reason`, so a SHADOW guardrail seen through this
|
|
18
19
|
* proxy counts toward its evidence like one seen through a gate.
|
|
@@ -49,7 +50,18 @@ export type EventKind = 'tool_allowed' | 'tool_blocked' | 'tool_redacted'
|
|
|
49
50
|
*/
|
|
50
51
|
| 'tool_held'
|
|
51
52
|
/** An approved, unexpired, exact-match bypass let a held call through. */
|
|
52
|
-
| 'hold_approved_bypass_used'
|
|
53
|
+
| 'hold_approved_bypass_used'
|
|
54
|
+
/**
|
|
55
|
+
* An MCP call budget (budget.ts) reached its warning threshold. Once per
|
|
56
|
+
* budget per period, across every proxy sharing the Valkey; carries
|
|
57
|
+
* `budget` and a finding. The call itself was allowed.
|
|
58
|
+
*/
|
|
59
|
+
| 'mcp_budget_threshold'
|
|
60
|
+
/**
|
|
61
|
+
* An MCP call budget is used up. Once per budget per period, on the first
|
|
62
|
+
* refusal; every refusal also sends its own `tool_blocked`.
|
|
63
|
+
*/
|
|
64
|
+
| 'mcp_budget_exceeded';
|
|
53
65
|
/**
|
|
54
66
|
* What a detection-style event found, so the control plane can file it as a
|
|
55
67
|
* detector finding (adjudicable on the Findings page, routed as
|
|
@@ -72,6 +84,13 @@ export interface DetectionFinding {
|
|
|
72
84
|
export declare function detectionFinding(finding: AnomalyFinding, disposition: DetectionFinding['disposition'], severity: DetectionFinding['severity']): DetectionFinding;
|
|
73
85
|
export interface GovernanceEvent {
|
|
74
86
|
incidentId: string;
|
|
87
|
+
/**
|
|
88
|
+
* This event's id, made once in {@link GovernanceEmitter.emit} and carried
|
|
89
|
+
* on every path it is delivered by — the daemon socket, the direct post and
|
|
90
|
+
* the event file — so the control plane processes it once however many of
|
|
91
|
+
* them, and their retries, arrive.
|
|
92
|
+
*/
|
|
93
|
+
eventId: string;
|
|
75
94
|
kind: EventKind;
|
|
76
95
|
toolName: string;
|
|
77
96
|
toolInput: unknown;
|
|
@@ -88,6 +107,11 @@ export interface GovernanceEvent {
|
|
|
88
107
|
* Absent only for an emitter constructed without one.
|
|
89
108
|
*/
|
|
90
109
|
principal?: CallerIdentity;
|
|
110
|
+
/**
|
|
111
|
+
* Set on the budget events, and on the `tool_blocked` of a call a used-up
|
|
112
|
+
* budget refused: which budget, its limit, the calls made, when it resets.
|
|
113
|
+
*/
|
|
114
|
+
budget?: BudgetEventDetail;
|
|
91
115
|
timestamp: string;
|
|
92
116
|
}
|
|
93
117
|
export declare class GovernanceEmitter {
|
|
@@ -98,7 +122,7 @@ export declare class GovernanceEmitter {
|
|
|
98
122
|
private readonly mcpProxyMode;
|
|
99
123
|
private readonly identity;
|
|
100
124
|
constructor(controlPlaneUrl: string, apiKey: string, eventsFilePath: string, workspaceId: string, mcpProxyMode?: string, identity?: CallerIdentity | undefined);
|
|
101
|
-
emit(kind: EventKind, toolName: string, toolInput: unknown, reason?: string, finding?: DetectionFinding): void;
|
|
125
|
+
emit(kind: EventKind, toolName: string, toolInput: unknown, reason?: string, finding?: DetectionFinding, budget?: BudgetEventDetail): void;
|
|
102
126
|
private runDualPath;
|
|
103
127
|
private postToControlPlane;
|
|
104
128
|
private appendToFile;
|
package/dist/emitter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAIpD,MAAM,MAAM,SAAS,GACjB,cAAc,GACd,cAAc,GACd,eAAe;AACjB;;;;;GAKG;GACD,cAAc;AAChB;;;;;;GAMG;GACD,+BAA+B;AACjC;;;;;;;GAOG;GACD,oBAAoB;AACtB;;;;;GAKG;GACD,kBAAkB;AACpB;;;;GAIG;GACD,WAAW;AACb,0EAA0E;GACxE,2BAA2B;AAC7B;;;;GAIG;GACD,sBAAsB;AACxB;;;GAGG;GACD,qBAAqB,CAAA;AAEzB;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAA;IACZ,kDAAkD;IAClD,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;IACvC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;IACnC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,EAC5C,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,GACrC,gBAAgB,CAElB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBALR,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAsB,EACpC,QAAQ,GAAE,cAAc,GAAG,SAAqB;IAGnE,IAAI,CACF,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,OAAO,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,gBAAgB,EAC1B,MAAM,CAAC,EAAE,iBAAiB,GACzB,IAAI;IAgDP,OAAO,CAAC,WAAW;YAYL,kBAAkB;YAgClB,YAAY;CAM3B"}
|
package/dist/emitter.js
CHANGED
|
@@ -34,9 +34,10 @@ export class GovernanceEmitter {
|
|
|
34
34
|
this.mcpProxyMode = mcpProxyMode;
|
|
35
35
|
this.identity = identity;
|
|
36
36
|
}
|
|
37
|
-
emit(kind, toolName, toolInput, reason, finding) {
|
|
37
|
+
emit(kind, toolName, toolInput, reason, finding, budget) {
|
|
38
38
|
const event = {
|
|
39
39
|
incidentId: node_crypto.randomUUID(),
|
|
40
|
+
eventId: node_crypto.randomUUID(),
|
|
40
41
|
kind,
|
|
41
42
|
toolName,
|
|
42
43
|
toolInput,
|
|
@@ -46,6 +47,7 @@ export class GovernanceEmitter {
|
|
|
46
47
|
severity: finding?.severity,
|
|
47
48
|
finding,
|
|
48
49
|
principal: this.identity,
|
|
50
|
+
budget,
|
|
49
51
|
timestamp: new Date().toISOString(),
|
|
50
52
|
};
|
|
51
53
|
if (this.mcpProxyMode === 'daemon') {
|
|
@@ -58,12 +60,14 @@ export class GovernanceEmitter {
|
|
|
58
60
|
workspaceId: this.workspaceId,
|
|
59
61
|
harnessType: 'mcp-governance-proxy',
|
|
60
62
|
incidentId: event.incidentId,
|
|
63
|
+
eventId: event.eventId,
|
|
61
64
|
timestamp: event.timestamp,
|
|
62
65
|
reason,
|
|
63
66
|
severity: event.severity,
|
|
64
67
|
finding,
|
|
65
68
|
toolInput,
|
|
66
69
|
principal: event.principal,
|
|
70
|
+
budget,
|
|
67
71
|
};
|
|
68
72
|
callDaemonSocket('telemetry.enqueue', eventPayload).then(() => {
|
|
69
73
|
log.debug({ action: 'telemetry_enqueued' }, 'Telemetry successfully enqueued to daemon');
|
|
@@ -96,10 +100,12 @@ export class GovernanceEmitter {
|
|
|
96
100
|
workspaceId: event.workspaceId,
|
|
97
101
|
harnessType: event.harnessType,
|
|
98
102
|
incidentId: event.incidentId,
|
|
103
|
+
eventId: event.eventId,
|
|
99
104
|
reason: event.reason,
|
|
100
105
|
severity: event.severity,
|
|
101
106
|
finding: event.finding,
|
|
102
107
|
principal: event.principal,
|
|
108
|
+
budget: event.budget,
|
|
103
109
|
timestamp: event.timestamp,
|
|
104
110
|
},
|
|
105
111
|
],
|
package/dist/emitter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,WAAW,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* guardedValkey.ts — the one Valkey connection a proxy process holds, and the
|
|
3
|
+
* guard every command on it goes through.
|
|
4
|
+
*
|
|
5
|
+
* Two things in a proxy read and write Valkey: the shared session window
|
|
6
|
+
* (sessionStore.ts) and the MCP call budgets (budget.ts). They share this
|
|
7
|
+
* connection, and they share its rule: a command runs only on a connected
|
|
8
|
+
* client, never waits longer than a short timeout, and any failure comes back
|
|
9
|
+
* as `undefined`. What `undefined` means is each caller's business — the
|
|
10
|
+
* session window falls back to its per-process copy; a budget follows the
|
|
11
|
+
* proxy's fail setting.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Redis } from 'ioredis';
|
|
16
|
+
/** A command that takes longer than this is treated as Valkey being unavailable. */
|
|
17
|
+
export declare const VALKEY_COMMAND_TIMEOUT_MS = 200;
|
|
18
|
+
export interface GuardedValkeyOptions {
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface GuardedRunOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Wait (within the timeout) for a connection that is still being set up,
|
|
24
|
+
* instead of answering `undefined` straight away. For a check whose
|
|
25
|
+
* `undefined` can refuse a call: the first call after the proxy starts
|
|
26
|
+
* must not be refused because the connection was a few milliseconds old.
|
|
27
|
+
*/
|
|
28
|
+
awaitConnect?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class GuardedValkey {
|
|
31
|
+
readonly client: Redis;
|
|
32
|
+
private readonly timeoutMs;
|
|
33
|
+
private warned;
|
|
34
|
+
constructor(url: string, opts?: GuardedValkeyOptions);
|
|
35
|
+
/** Runs `op`, or answers `undefined` when Valkey is not connected, too slow, or fails. */
|
|
36
|
+
run<T>(op: (client: Redis) => Promise<T>, opts?: GuardedRunOptions): Promise<T | undefined>;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
private warnOnce;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=guardedValkey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardedValkey.d.ts","sourceRoot":"","sources":["../src/guardedValkey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAM/B,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAA;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,MAAM,CAAQ;gBAEV,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB;IAexD,0FAA0F;IACpF,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAsB/F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,OAAO,CAAC,QAAQ;CAYjB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* guardedValkey.ts — the one Valkey connection a proxy process holds, and the
|
|
3
|
+
* guard every command on it goes through.
|
|
4
|
+
*
|
|
5
|
+
* Two things in a proxy read and write Valkey: the shared session window
|
|
6
|
+
* (sessionStore.ts) and the MCP call budgets (budget.ts). They share this
|
|
7
|
+
* connection, and they share its rule: a command runs only on a connected
|
|
8
|
+
* client, never waits longer than a short timeout, and any failure comes back
|
|
9
|
+
* as `undefined`. What `undefined` means is each caller's business — the
|
|
10
|
+
* session window falls back to its per-process copy; a budget follows the
|
|
11
|
+
* proxy's fail setting.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Redis } from 'ioredis';
|
|
16
|
+
import { createStderrLogger } from './stderrLog.js';
|
|
17
|
+
import { describeConnectionError } from './valkeyErrors.js';
|
|
18
|
+
const log = createStderrLogger('mcp-proxy.valkey');
|
|
19
|
+
/** A command that takes longer than this is treated as Valkey being unavailable. */
|
|
20
|
+
export const VALKEY_COMMAND_TIMEOUT_MS = 200;
|
|
21
|
+
export class GuardedValkey {
|
|
22
|
+
client;
|
|
23
|
+
timeoutMs;
|
|
24
|
+
warned = false;
|
|
25
|
+
constructor(url, opts = {}) {
|
|
26
|
+
this.timeoutMs = opts.timeoutMs ?? VALKEY_COMMAND_TIMEOUT_MS;
|
|
27
|
+
this.client = new Redis(url, {
|
|
28
|
+
lazyConnect: true,
|
|
29
|
+
// No offline queue: a command issued before the connection is up must
|
|
30
|
+
// fail now, not wait for a Valkey that may never answer.
|
|
31
|
+
enableOfflineQueue: false,
|
|
32
|
+
maxRetriesPerRequest: 1,
|
|
33
|
+
connectTimeout: 1000,
|
|
34
|
+
retryStrategy: (times) => Math.min(1000 * 2 ** Math.min(times, 5), 30_000),
|
|
35
|
+
});
|
|
36
|
+
this.client.on('error', (err) => this.warnOnce('Valkey unreachable', err));
|
|
37
|
+
void this.client.connect().catch((err) => this.warnOnce('Valkey connect failed', err));
|
|
38
|
+
}
|
|
39
|
+
/** Runs `op`, or answers `undefined` when Valkey is not connected, too slow, or fails. */
|
|
40
|
+
async run(op, opts = {}) {
|
|
41
|
+
let timer;
|
|
42
|
+
const timeout = new Promise((resolve) => {
|
|
43
|
+
timer = setTimeout(() => resolve(undefined), this.timeoutMs);
|
|
44
|
+
timer.unref();
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
if (this.client.status !== 'ready') {
|
|
48
|
+
const settingUp = this.client.status === 'connecting' || this.client.status === 'connect';
|
|
49
|
+
if (!opts.awaitConnect || !settingUp)
|
|
50
|
+
return undefined;
|
|
51
|
+
const ready = new Promise((resolve) => this.client.once('ready', () => resolve(true)));
|
|
52
|
+
if ((await Promise.race([ready, timeout])) !== true)
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return await Promise.race([op(this.client), timeout]);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
this.warnOnce('Valkey command failed', err);
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
if (timer)
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async close() {
|
|
67
|
+
try {
|
|
68
|
+
await this.client.quit();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
this.client.disconnect();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
warnOnce(msg, err) {
|
|
75
|
+
const detail = { err: describeConnectionError(err) };
|
|
76
|
+
if (this.warned) {
|
|
77
|
+
log.debug(detail, msg);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.warned = true;
|
|
81
|
+
log.warn(detail, `${msg} — the anomaly session window falls back to this process, and MCP call budgets follow the fail setting`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=guardedValkey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardedValkey.js","sourceRoot":"","sources":["../src/guardedValkey.ts"],"names":[],"mappings":"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"}
|
package/dist/injection.js
CHANGED
|
@@ -65,7 +65,7 @@ const PATTERNS = [
|
|
|
65
65
|
*/
|
|
66
66
|
const KILL_THRESHOLD = 2;
|
|
67
67
|
/**
|
|
68
|
-
* Workspace-supplied patterns, compiled
|
|
68
|
+
* Workspace-supplied patterns, compiled. Same contract as
|
|
69
69
|
* `dlp.ts`'s `setDynamicPatterns`: sources come from the control plane's
|
|
70
70
|
* `mcpInjectionPatterns`, an invalid one is dropped and counted, and the
|
|
71
71
|
* hardcoded floor above applies regardless — an unreachable control plane
|
package/dist/interceptor.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* 0. The MCP server registry, server/tool allowlists and SSO group clearance
|
|
6
6
|
* 1. DLP scan (credential / destructive pattern detection)
|
|
7
7
|
* 2. SOP policy rules (fetched from control plane via PolicyClient)
|
|
8
|
+
* 3–5. Prompt injection, anomaly detectors, WASM rules
|
|
9
|
+
* 6. MCP call budgets — last, so only a call that would otherwise run is counted
|
|
8
10
|
*
|
|
9
11
|
* Returns an allow / block / redact decision.
|
|
10
12
|
*
|
|
@@ -16,25 +18,38 @@ import type { WasmRunner } from './wasm/runner.js';
|
|
|
16
18
|
import type { PolicyClient } from './policy.js';
|
|
17
19
|
import { type GovernanceEmitter } from './emitter.js';
|
|
18
20
|
import type { ApprovalHolds } from './approvalHold.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
import { type McpBudgetEnforcer } from './budget.js';
|
|
22
|
+
import type { McpRefusalCode } from './refusals.js';
|
|
23
|
+
/**
|
|
24
|
+
* A refusal: what the agent reads (`reason`), and what a client reads — the
|
|
25
|
+
* stable `code`, the `ruleId` that decided, and any `detail` the code carries
|
|
26
|
+
* (refusals.ts). All three reach the JSON-RPC frame's `error.data`.
|
|
27
|
+
*/
|
|
28
|
+
export interface Block {
|
|
22
29
|
action: 'block';
|
|
23
30
|
reason: string;
|
|
24
|
-
|
|
31
|
+
code: Exclude<McpRefusalCode, 'HELD'>;
|
|
32
|
+
ruleId: string;
|
|
33
|
+
detail?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export type Decision = {
|
|
36
|
+
action: 'allow';
|
|
37
|
+
} | Block | {
|
|
25
38
|
action: 'redact';
|
|
26
39
|
reason: string;
|
|
27
40
|
redactedInput: unknown;
|
|
28
41
|
}
|
|
29
42
|
/**
|
|
30
43
|
* Refused for now, pending a person's approval (`require_approval`). Not a
|
|
31
|
-
* block: the agent is told the hold id and
|
|
32
|
-
*
|
|
44
|
+
* block: the agent is told the hold id and to retry once it is approved,
|
|
45
|
+
* which passes only while the workspace's review-hold bypass is on — see
|
|
46
|
+
* approvalHold.ts. `holdId` is empty when the hold could not be recorded.
|
|
33
47
|
*/
|
|
34
48
|
| {
|
|
35
49
|
action: 'hold';
|
|
36
50
|
reason: string;
|
|
37
51
|
holdId: string;
|
|
52
|
+
ruleId: string;
|
|
38
53
|
};
|
|
39
54
|
export declare class ToolCallInterceptor {
|
|
40
55
|
private readonly policy;
|
|
@@ -111,6 +126,13 @@ export declare class ToolCallInterceptor {
|
|
|
111
126
|
* refusal — never an allow.
|
|
112
127
|
*/
|
|
113
128
|
private readonly holds;
|
|
129
|
+
/**
|
|
130
|
+
* Counts each call against the workspace's MCP call budgets (budget.ts).
|
|
131
|
+
* `undefined` (construction sites that predate budgets) skips them; the
|
|
132
|
+
* proxy always passes one, with no store when it has no Valkey, so a
|
|
133
|
+
* covered call then resolves through the fail setting.
|
|
134
|
+
*/
|
|
135
|
+
private readonly budgets;
|
|
114
136
|
constructor(policy: PolicyClient, emitter: GovernanceEmitter,
|
|
115
137
|
/**
|
|
116
138
|
* The local fail setting (`INTUTIC_MCP_FAIL_OPEN`, config.ts). The
|
|
@@ -183,7 +205,16 @@ export declare class ToolCallInterceptor {
|
|
|
183
205
|
* holds) leaves such a call held with nothing recorded, which is still a
|
|
184
206
|
* refusal — never an allow.
|
|
185
207
|
*/
|
|
186
|
-
holds?: ApprovalHolds | undefined
|
|
208
|
+
holds?: ApprovalHolds | undefined,
|
|
209
|
+
/**
|
|
210
|
+
* Counts each call against the workspace's MCP call budgets (budget.ts).
|
|
211
|
+
* `undefined` (construction sites that predate budgets) skips them; the
|
|
212
|
+
* proxy always passes one, with no store when it has no Valkey, so a
|
|
213
|
+
* covered call then resolves through the fail setting.
|
|
214
|
+
*/
|
|
215
|
+
budgets?: McpBudgetEnforcer | undefined);
|
|
216
|
+
/** Set once this process has said that budgets went unchecked under fail-open, so the log says it once. */
|
|
217
|
+
private budgetsUncheckedWarned;
|
|
187
218
|
/**
|
|
188
219
|
* Whether a governance check that cannot complete lets the call through:
|
|
189
220
|
* the workspace's `mcpProxyFailBehavior` when the control plane has sent
|
|
@@ -203,9 +234,9 @@ export declare class ToolCallInterceptor {
|
|
|
203
234
|
/**
|
|
204
235
|
* A `require_approval` rule matched. Returns the hold decision, or `null`
|
|
205
236
|
* when an approved bypass for this exact call lets it continue. Every hold
|
|
206
|
-
* reason names the hold id and
|
|
207
|
-
*
|
|
208
|
-
* knows what to
|
|
237
|
+
* reason names the hold id, who may approve it and when a retry passes, in
|
|
238
|
+
* the words the hook gates print (`holdApprovalHint`), so a person reading
|
|
239
|
+
* the agent's transcript knows what to do.
|
|
209
240
|
*/
|
|
210
241
|
private hold;
|
|
211
242
|
/**
|
|
@@ -248,5 +279,17 @@ export declare class ToolCallInterceptor {
|
|
|
248
279
|
* @returns Decision: allow, block, or redact
|
|
249
280
|
*/
|
|
250
281
|
decide(toolName: string, toolInput: unknown): Promise<Decision>;
|
|
282
|
+
/**
|
|
283
|
+
* Counts the call against every budget that covers it, or refuses it when
|
|
284
|
+
* one is used up. The threshold and exceeded events go out once per budget
|
|
285
|
+
* per period (the store claims them), each as a `budget_breach` finding; a
|
|
286
|
+
* refusal also sends `tool_blocked`, as every refusal does, carrying the
|
|
287
|
+
* budget — the control plane files one incident per budget per period from
|
|
288
|
+
* those and counts the rest on it.
|
|
289
|
+
*
|
|
290
|
+
* When the count cannot be checked, the fail setting decides, as for every
|
|
291
|
+
* other governance check that cannot complete.
|
|
292
|
+
*/
|
|
293
|
+
private checkBudgets;
|
|
251
294
|
}
|
|
252
295
|
//# sourceMappingURL=interceptor.d.ts.map
|