@dudousxd/nestjs-agent-core 0.15.4 → 0.16.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.
@@ -0,0 +1,2519 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/guardrails/index.ts
22
+ var guardrails_exports = {};
23
+ __export(guardrails_exports, {
24
+ ACTION_SEVERITY: () => ACTION_SEVERITY,
25
+ DEFAULT_APPROVAL_MESSAGE: () => DEFAULT_APPROVAL_MESSAGE,
26
+ DEFAULT_MESSAGES: () => DEFAULT_MESSAGES,
27
+ GUARDRAIL_STAGES: () => GUARDRAIL_STAGES,
28
+ GuardrailBlockedError: () => GuardrailBlockedError,
29
+ Guardrails: () => Guardrails,
30
+ INJECTION_REPLACEMENT: () => INJECTION_REPLACEMENT,
31
+ INJECTION_SIGNALS: () => INJECTION_SIGNALS,
32
+ InMemoryVaultStore: () => InMemoryVaultStore,
33
+ PII_TYPES: () => PII_TYPES,
34
+ SECRET_TYPES: () => SECRET_TYPES,
35
+ StreamGuard: () => StreamGuard,
36
+ Vault: () => Vault,
37
+ applyRedactions: () => applyRedactions,
38
+ cardBrand: () => cardBrand,
39
+ cnpjValid: () => cnpjValid,
40
+ compileGuardPattern: () => compileGuardPattern,
41
+ cpfValid: () => cpfValid,
42
+ createGuardrails: () => createGuardrails,
43
+ decodeTagChars: () => decodeTagChars,
44
+ detectKeywords: () => detectKeywords,
45
+ detectPii: () => detectPii,
46
+ detectRegex: () => detectRegex,
47
+ detectSecrets: () => detectSecrets,
48
+ entropy: () => entropy,
49
+ fold: () => fold,
50
+ globToRegExp: () => globToRegExp,
51
+ ibanValid: () => ibanValid,
52
+ isIPv4: () => isIPv4,
53
+ jsonSlots: () => jsonSlots,
54
+ labelFor: () => labelFor,
55
+ luhnValid: () => luhnValid,
56
+ matchesAny: () => matchesAny,
57
+ orderRules: () => orderRules,
58
+ phoneValid: () => phoneValid,
59
+ refuseResponse: () => refuseResponse,
60
+ requestSlots: () => requestSlots,
61
+ resolveOverlaps: () => resolveOverlaps,
62
+ responseSlots: () => responseSlots,
63
+ ruleApplies: () => ruleApplies,
64
+ runDetector: () => runDetector,
65
+ scan: () => scan,
66
+ scoreInjection: () => scoreInjection,
67
+ scoreToolText: () => scoreToolText,
68
+ screenToolDefinition: () => screenToolDefinition,
69
+ shorthandRules: () => shorthandRules,
70
+ spanLocator: () => spanLocator,
71
+ ssnValid: () => ssnValid,
72
+ toolResultSlots: () => toolResultSlots,
73
+ toolText: () => toolText
74
+ });
75
+ module.exports = __toCommonJS(guardrails_exports);
76
+
77
+ // src/guardrails/types.ts
78
+ var GUARDRAIL_STAGES = [
79
+ "llm_request",
80
+ "llm_response",
81
+ "tool_args",
82
+ "tool_result",
83
+ "tool_description"
84
+ ];
85
+ var ACTION_SEVERITY = {
86
+ allow: 0,
87
+ log: 1,
88
+ redact: 2,
89
+ approve: 3,
90
+ block: 4
91
+ };
92
+ var PII_TYPES = [
93
+ "email",
94
+ "phone",
95
+ "cpf",
96
+ "cnpj",
97
+ "ssn",
98
+ "credit_card",
99
+ "iban",
100
+ "ip_address"
101
+ ];
102
+ var SECRET_TYPES = [
103
+ "openai_key",
104
+ "anthropic_key",
105
+ "aws_access_key",
106
+ "aws_secret_key",
107
+ "github_token",
108
+ "gitlab_token",
109
+ "slack_token",
110
+ "google_api_key",
111
+ "stripe_key",
112
+ "jwt",
113
+ "bearer_token",
114
+ "private_key",
115
+ "generic_secret"
116
+ ];
117
+
118
+ // src/guardrails/detectors/pii.ts
119
+ function isIPv4(raw) {
120
+ const parts = raw.split(".");
121
+ return parts.length === 4 && parts.every((p) => /^(?:0|[1-9]\d{0,2})$/.test(p) && Number(p) <= 255);
122
+ }
123
+ __name(isIPv4, "isIPv4");
124
+ function luhnValid(digits) {
125
+ if (!/^\d+$/.test(digits)) return false;
126
+ let sum = 0;
127
+ let double = false;
128
+ for (let i = digits.length - 1; i >= 0; i--) {
129
+ let d = digits.charCodeAt(i) - 48;
130
+ if (double) {
131
+ d *= 2;
132
+ if (d > 9) d -= 9;
133
+ }
134
+ sum += d;
135
+ double = !double;
136
+ }
137
+ return sum % 10 === 0;
138
+ }
139
+ __name(luhnValid, "luhnValid");
140
+ function cardBrand(digits) {
141
+ const p2 = Number(digits.slice(0, 2));
142
+ const p3 = Number(digits.slice(0, 3));
143
+ const p4 = Number(digits.slice(0, 4));
144
+ const p6 = Number(digits.slice(0, 6));
145
+ const len = digits.length;
146
+ if ([
147
+ 401178,
148
+ 401179,
149
+ 431274,
150
+ 438935,
151
+ 451416,
152
+ 457393,
153
+ 457631,
154
+ 457632,
155
+ 504175,
156
+ 627780,
157
+ 636297,
158
+ 636368
159
+ ].includes(p6)) return "elo";
160
+ if (p6 >= 506699 && p6 <= 506778 || p6 >= 509e3 && p6 <= 509999 || p6 >= 650031 && p6 <= 650051) return "elo";
161
+ if (p6 === 606282 || p4 === 3841) return "hipercard";
162
+ if (digits.startsWith("4") && (len === 13 || len === 16 || len === 19)) return "visa";
163
+ if ((p2 >= 51 && p2 <= 55 || p4 >= 2221 && p4 <= 2720) && len === 16) return "mastercard";
164
+ if ((p2 === 34 || p2 === 37) && len === 15) return "amex";
165
+ if ((p4 === 6011 || p2 === 65 || p3 >= 644 && p3 <= 649) && len >= 16) return "discover";
166
+ if ((p2 === 36 || p2 === 38 || p3 >= 300 && p3 <= 305) && len >= 14) return "diners";
167
+ if (p4 >= 3528 && p4 <= 3589 && len >= 16) return "jcb";
168
+ if ((p2 === 62 || p2 === 81) && len >= 16) return "unionpay";
169
+ if ((p4 === 5018 || p4 === 5020 || p4 === 5038 || p4 === 6304 || p4 === 6759 || p4 === 6761) && len >= 12) return "maestro";
170
+ return void 0;
171
+ }
172
+ __name(cardBrand, "cardBrand");
173
+ function cpfValid(raw) {
174
+ const d = raw.replace(/\D/g, "");
175
+ if (d.length !== 11 || /^(\d)\1{10}$/.test(d)) return false;
176
+ const check = /* @__PURE__ */ __name((len) => {
177
+ let sum = 0;
178
+ for (let i = 0; i < len; i++) sum += (d.charCodeAt(i) - 48) * (len + 1 - i);
179
+ const r = sum * 10 % 11;
180
+ return r === 10 ? 0 : r;
181
+ }, "check");
182
+ return check(9) === d.charCodeAt(9) - 48 && check(10) === d.charCodeAt(10) - 48;
183
+ }
184
+ __name(cpfValid, "cpfValid");
185
+ function cnpjValid(raw) {
186
+ const c = raw.replace(/[.\-/\s]/g, "").toUpperCase();
187
+ if (!/^[0-9A-Z]{12}\d{2}$/.test(c) || /^(\d)\1{13}$/.test(c)) return false;
188
+ const value = /* @__PURE__ */ __name((i) => c.charCodeAt(i) - 48, "value");
189
+ const check = /* @__PURE__ */ __name((len) => {
190
+ const weights = len === 12 ? [
191
+ 5,
192
+ 4,
193
+ 3,
194
+ 2,
195
+ 9,
196
+ 8,
197
+ 7,
198
+ 6,
199
+ 5,
200
+ 4,
201
+ 3,
202
+ 2
203
+ ] : [
204
+ 6,
205
+ 5,
206
+ 4,
207
+ 3,
208
+ 2,
209
+ 9,
210
+ 8,
211
+ 7,
212
+ 6,
213
+ 5,
214
+ 4,
215
+ 3,
216
+ 2
217
+ ];
218
+ let sum = 0;
219
+ for (let i = 0; i < len; i++) sum += value(i) * (weights[i] ?? 0);
220
+ const r = sum % 11;
221
+ return r < 2 ? 0 : 11 - r;
222
+ }, "check");
223
+ return check(12) === value(12) && check(13) === value(13);
224
+ }
225
+ __name(cnpjValid, "cnpjValid");
226
+ function ssnValid(raw) {
227
+ const m = /^(\d{3})[- ](\d{2})[- ](\d{4})$/.exec(raw);
228
+ if (!m) return false;
229
+ const area = m[1] ?? "";
230
+ const group = m[2] ?? "";
231
+ const serial = m[3] ?? "";
232
+ if (area === "000" || area === "666" || area.startsWith("9")) return false;
233
+ if (group === "00" || serial === "0000") return false;
234
+ if ([
235
+ "078-05-1120",
236
+ "219-09-9999",
237
+ "123-45-6789"
238
+ ].includes(`${area}-${group}-${serial}`)) return false;
239
+ return true;
240
+ }
241
+ __name(ssnValid, "ssnValid");
242
+ var IBAN_LENGTHS = {
243
+ AD: 24,
244
+ AE: 23,
245
+ AT: 20,
246
+ BE: 16,
247
+ BG: 22,
248
+ BH: 22,
249
+ BR: 29,
250
+ CH: 21,
251
+ CR: 22,
252
+ CY: 28,
253
+ CZ: 24,
254
+ DE: 22,
255
+ DK: 18,
256
+ DO: 28,
257
+ EE: 20,
258
+ ES: 24,
259
+ FI: 18,
260
+ FO: 18,
261
+ FR: 27,
262
+ GB: 22,
263
+ GI: 23,
264
+ GL: 18,
265
+ GR: 27,
266
+ GT: 28,
267
+ HR: 21,
268
+ HU: 28,
269
+ IE: 22,
270
+ IL: 23,
271
+ IS: 26,
272
+ IT: 27,
273
+ JO: 30,
274
+ KW: 30,
275
+ KZ: 20,
276
+ LB: 28,
277
+ LI: 21,
278
+ LT: 20,
279
+ LU: 20,
280
+ LV: 21,
281
+ MC: 27,
282
+ MD: 24,
283
+ ME: 22,
284
+ MK: 19,
285
+ MR: 27,
286
+ MT: 31,
287
+ MU: 30,
288
+ NL: 18,
289
+ NO: 15,
290
+ PK: 24,
291
+ PL: 28,
292
+ PS: 29,
293
+ PT: 25,
294
+ QA: 29,
295
+ RO: 24,
296
+ RS: 22,
297
+ SA: 24,
298
+ SE: 24,
299
+ SI: 19,
300
+ SK: 24,
301
+ SM: 27,
302
+ TN: 24,
303
+ TR: 26,
304
+ UA: 29,
305
+ VG: 24,
306
+ XK: 20
307
+ };
308
+ function ibanValid(raw) {
309
+ const s = raw.replace(/\s/g, "").toUpperCase();
310
+ if (!/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(s)) return false;
311
+ const expected = IBAN_LENGTHS[s.slice(0, 2)];
312
+ if (expected !== void 0 && s.length !== expected) return false;
313
+ if (expected === void 0) return false;
314
+ const rearranged = s.slice(4) + s.slice(0, 4);
315
+ let rem = 0;
316
+ for (const ch of rearranged) {
317
+ const v = ch >= "A" ? String(ch.charCodeAt(0) - 55) : ch;
318
+ for (const digit of v) rem = (rem * 10 + (digit.charCodeAt(0) - 48)) % 97;
319
+ }
320
+ return rem === 1;
321
+ }
322
+ __name(ibanValid, "ibanValid");
323
+ function phoneValid(raw) {
324
+ const digits = raw.replace(/\D/g, "");
325
+ if (raw.trim().startsWith("+")) return digits.length >= 8 && digits.length <= 15;
326
+ if (digits.length < 10 || digits.length > 11) return false;
327
+ if (/^\(?\d{2}\)?[\s.-]?9?\d{4}[\s.-]\d{4}$/.test(raw.trim())) return true;
328
+ if (/^\(?\d{3}\)?[\s.-]?\d{3}[\s.-]\d{4}$/.test(raw.trim())) return true;
329
+ return false;
330
+ }
331
+ __name(phoneValid, "phoneValid");
332
+ var PATTERNS = [
333
+ {
334
+ type: "email",
335
+ re: /(?<![\w.+-])[A-Za-z0-9](?:[A-Za-z0-9._%+-]{0,63})@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,62})\.)+[A-Za-z]{2,24}(?![\w-])/g,
336
+ score: 0.99
337
+ },
338
+ {
339
+ type: "iban",
340
+ re: /(?<![A-Za-z0-9])[A-Z]{2}\d{2}(?: ?[A-Z0-9]){11,30}(?![A-Za-z0-9])/g,
341
+ valid: ibanValid,
342
+ score: 0.99
343
+ },
344
+ {
345
+ type: "cnpj",
346
+ re: /(?<![\w./-])[0-9A-Z]{2}\.?[0-9A-Z]{3}\.?[0-9A-Z]{3}\/?[0-9A-Z]{4}-?\d{2}(?![\w/-])/g,
347
+ valid: /* @__PURE__ */ __name((m) => cnpjValid(m) && (/^\d/.test(m.replace(/\D/g, "")) || /[./-]/.test(m)) && !/^[A-Z]+$/.test(m), "valid"),
348
+ score: 0.98
349
+ },
350
+ {
351
+ type: "cpf",
352
+ re: /(?<![\w./-])\d{3}\.?\d{3}\.?\d{3}-?\d{2}(?![\w/-])/g,
353
+ valid: cpfValid,
354
+ score: 0.95
355
+ },
356
+ {
357
+ type: "credit_card",
358
+ re: /(?<![\d-])\d(?:[ -]?\d){11,18}(?![\d-])/g,
359
+ valid: /* @__PURE__ */ __name((m) => {
360
+ const digits = m.replace(/\D/g, "");
361
+ if (/ /.test(m) && /-/.test(m)) return false;
362
+ return digits.length >= 13 && digits.length <= 19 && luhnValid(digits) && !!cardBrand(digits);
363
+ }, "valid"),
364
+ score: 0.97
365
+ },
366
+ {
367
+ type: "ssn",
368
+ re: /(?<![\d-])\d{3}[- ]\d{2}[- ]\d{4}(?![\d-])/g,
369
+ valid: ssnValid,
370
+ score: 0.85
371
+ },
372
+ {
373
+ type: "phone",
374
+ re: /(?<![\w+])(?:\+\d{1,3}[\s.-]?)?\(?\d{2,4}\)?[\s.-]?\d{3,5}[\s.-]\d{4}(?![\w])/g,
375
+ valid: phoneValid,
376
+ score: 0.75
377
+ },
378
+ {
379
+ type: "ip_address",
380
+ re: /(?<![\d.])(?:\d{1,3}\.){3}\d{1,3}(?![\d.])/g,
381
+ valid: /* @__PURE__ */ __name((m) => isIPv4(m) && !m.startsWith("0.") && m !== "127.0.0.1", "valid"),
382
+ score: 0.6
383
+ }
384
+ ];
385
+ function detectPii(text, types = PII_TYPES) {
386
+ const want = new Set(types);
387
+ const out = [];
388
+ for (const p of PATTERNS) {
389
+ if (!want.has(p.type)) continue;
390
+ p.re.lastIndex = 0;
391
+ for (let m = p.re.exec(text); m; m = p.re.exec(text)) {
392
+ const value = m[0];
393
+ if (p.valid && !p.valid(value)) continue;
394
+ out.push({
395
+ detector: "pii",
396
+ category: `pii.${p.type}`,
397
+ start: m.index,
398
+ end: m.index + value.length,
399
+ score: p.score,
400
+ value,
401
+ spanned: true
402
+ });
403
+ }
404
+ }
405
+ return out;
406
+ }
407
+ __name(detectPii, "detectPii");
408
+
409
+ // src/guardrails/detectors/secrets.ts
410
+ function entropy(s) {
411
+ if (!s) return 0;
412
+ const counts = /* @__PURE__ */ new Map();
413
+ for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1);
414
+ let h = 0;
415
+ for (const c of counts.values()) {
416
+ const p = c / s.length;
417
+ h -= p * Math.log2(p);
418
+ }
419
+ return h;
420
+ }
421
+ __name(entropy, "entropy");
422
+ var PATTERNS2 = [
423
+ {
424
+ type: "private_key",
425
+ re: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----|$)/g,
426
+ score: 0.99
427
+ },
428
+ {
429
+ type: "anthropic_key",
430
+ re: /(?<![\w-])sk-ant-(?:api|admin|oat)\d{0,2}-?[A-Za-z0-9_-]{20,}/g,
431
+ score: 0.99
432
+ },
433
+ {
434
+ type: "openai_key",
435
+ re: /(?<![\w-])sk-(?!ant-)(?:proj-|svcacct-|admin-|None-)?[A-Za-z0-9_-]{20,}/g,
436
+ score: 0.95,
437
+ valid: /* @__PURE__ */ __name((v) => entropy(v) > 3.5, "valid")
438
+ },
439
+ {
440
+ type: "aws_access_key",
441
+ re: /(?<![A-Z0-9])(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}(?![A-Z0-9])/g,
442
+ score: 0.98
443
+ },
444
+ {
445
+ type: "aws_secret_key",
446
+ re: /(?:aws_?secret_?access_?key|aws_?secret|secretaccesskey)["']?\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,
447
+ group: 1,
448
+ score: 0.97
449
+ },
450
+ {
451
+ type: "github_token",
452
+ re: /(?<![\w])(?:(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}|github_pat_[A-Za-z0-9_]{22,255})/g,
453
+ score: 0.99
454
+ },
455
+ {
456
+ type: "gitlab_token",
457
+ re: /(?<![\w-])glpat-[A-Za-z0-9_-]{20,}/g,
458
+ score: 0.99
459
+ },
460
+ {
461
+ type: "slack_token",
462
+ re: /(?<![\w-])(?:xox[abprseo]-[A-Za-z0-9-]{10,}|https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/]{20,})/g,
463
+ score: 0.98
464
+ },
465
+ {
466
+ type: "google_api_key",
467
+ re: /(?<![\w-])AIza[0-9A-Za-z_-]{35}(?![\w-])/g,
468
+ score: 0.97
469
+ },
470
+ {
471
+ type: "stripe_key",
472
+ re: /(?<![\w])(?:sk|rk|pk)_(?:live|test)_[0-9A-Za-z]{16,}/g,
473
+ score: 0.97
474
+ },
475
+ {
476
+ type: "jwt",
477
+ re: /(?<![\w-])eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?![\w-])/g,
478
+ score: 0.9
479
+ },
480
+ {
481
+ type: "bearer_token",
482
+ re: /\bbearer\s+([A-Za-z0-9._~+/-]{20,}=*)/gi,
483
+ group: 1,
484
+ score: 0.85,
485
+ valid: /* @__PURE__ */ __name((v) => entropy(v) > 3.5, "valid")
486
+ },
487
+ {
488
+ type: "generic_secret",
489
+ re: /\b(?:api[_-]?key|api[_-]?secret|client[_-]?secret|secret[_-]?key|access[_-]?token|auth[_-]?token|password|passwd|senha)\b["']?\s*[:=]\s*["']?([^\s"',;]{8,200})/gi,
490
+ group: 1,
491
+ score: 0.7,
492
+ // Words and placeholders ("changeme", "<your-key>", "${API_KEY}") are not secrets.
493
+ valid: /* @__PURE__ */ __name((v) => entropy(v) >= 3 && !/^[<{$[]/.test(v) && !/^(?:x+|\*+|\.+)$/i.test(v), "valid")
494
+ }
495
+ ];
496
+ function detectSecrets(text, types = SECRET_TYPES) {
497
+ const want = new Set(types);
498
+ const out = [];
499
+ for (const p of PATTERNS2) {
500
+ if (!want.has(p.type)) continue;
501
+ p.re.lastIndex = 0;
502
+ for (let m = p.re.exec(text); m; m = p.re.exec(text)) {
503
+ if (m[0].length === 0) {
504
+ p.re.lastIndex++;
505
+ continue;
506
+ }
507
+ const group = p.group ?? 0;
508
+ const value = m[group];
509
+ if (!value) continue;
510
+ if (p.valid && !p.valid(value)) continue;
511
+ const start = m.index + (group === 0 ? 0 : m[0].lastIndexOf(value));
512
+ out.push({
513
+ detector: "secrets",
514
+ category: `secret.${p.type}`,
515
+ start,
516
+ end: start + value.length,
517
+ score: p.score,
518
+ value,
519
+ spanned: true
520
+ });
521
+ }
522
+ }
523
+ return out;
524
+ }
525
+ __name(detectSecrets, "detectSecrets");
526
+
527
+ // src/guardrails/detectors/injection.ts
528
+ var W = String.raw`[\s\S]{0,40}?`;
529
+ var INJECTION_SIGNALS = [
530
+ // Instruction override.
531
+ {
532
+ id: "ignore_previous",
533
+ re: new RegExp(String.raw`\b(?:ignore|disregard|forget|override|bypass|skip)\b${W}\b(?:all\s+|any\s+|the\s+|your\s+|my\s+)?(?:previous|prior|above|earlier|preceding|original|system|initial)\b${W}\b(?:instructions?|prompts?|rules?|directions?|guidelines?|messages?|context)\b`, "gi"),
534
+ weight: 0.75
535
+ },
536
+ {
537
+ id: "ignore_previous_pt",
538
+ re: new RegExp(String.raw`\b(?:ignore|ignora|desconsidere|desconsidera|esque[çc]a|esquece|descarte)\b${W}\b(?:as\s+|todas\s+as\s+|suas\s+|tuas\s+)?(?:instru[çc][õo]es|regras|orienta[çc][õo]es|mensagens|prompts?)\b${W}\b(?:anteriores|acima|pr[ée]vias|do\s+sistema|originais)\b`, "gi"),
539
+ weight: 0.75
540
+ },
541
+ {
542
+ id: "ignore_previous_es",
543
+ re: new RegExp(String.raw`\b(?:ignora|ignore|olvida|olvide|descarta)\b${W}\b(?:las\s+|todas\s+las\s+|tus\s+)?(?:instrucciones|reglas|indicaciones)\b${W}\b(?:anteriores|previas|del\s+sistema)\b`, "gi"),
544
+ weight: 0.75
545
+ },
546
+ {
547
+ id: "new_instructions",
548
+ re: /\b(?:new|updated|real|actual|true)\s+(?:system\s+)?instructions?\s*(?::|follow|are)|\bnovas\s+instru[çc][õo]es\s*:|\bnuevas\s+instrucciones\s*:/gi,
549
+ weight: 0.45
550
+ },
551
+ // Role hijack / persona jailbreaks.
552
+ {
553
+ id: "role_hijack",
554
+ re: /\b(?:you\s+are\s+now|from\s+now\s+on\s+you\s+(?:are|will)|act\s+as\s+(?:an?\s+)?(?:unrestricted|unfiltered|evil|jailbroken)|pretend\s+(?:to\s+be|you\s+are)\s+(?:an?\s+)?(?:unrestricted|unfiltered|different\s+ai)|a\s+partir\s+de\s+agora\s+voc[êe]\s+(?:[ée]|ser[áa])|voc[êe]\s+agora\s+[ée]|ahora\s+eres)\b/gi,
555
+ weight: 0.45
556
+ },
557
+ {
558
+ id: "jailbreak_persona",
559
+ re: /\b(?:DAN|do\s+anything\s+now|developer\s+mode|god\s+mode|jailbreak(?:ed)?|STAN|DUDE\s+mode|AIM\s+mode|no\s+(?:restrictions|filters|guidelines|limits)\s+mode|modo\s+desenvolvedor|sem\s+restri[çc][õo]es)\b/g,
560
+ weight: 0.55
561
+ },
562
+ // Prompt / secret extraction.
563
+ {
564
+ id: "prompt_leak",
565
+ re: /\b(?:reveal|print|show|repeat|output|display|leak|tell\s+me|write\s+out)\b[\s\S]{0,30}?\b(?:your|the)\s+(?:system\s+prompt|initial\s+(?:prompt|instructions)|hidden\s+(?:prompt|instructions)|instructions\s+above|developer\s+message)\b|\b(?:mostre|revele|repita|imprima)\b[\s\S]{0,30}?\b(?:seu|o)\s+prompt\s+(?:do\s+sistema|inicial)\b/gi,
566
+ weight: 0.6
567
+ },
568
+ // Role / delimiter spoofing: chat-template tokens and fake headers.
569
+ {
570
+ id: "role_tokens",
571
+ re: /<\|(?:im_start|im_end|system|endoftext|start_header_id|end_header_id|eot_id)\|>|\[\/?INST\]|<<\/?SYS>>|<\/?(?:system|assistant|developer)>|^\s*#{2,}\s*(?:system|assistant|developer)\s*(?:prompt|message)?\s*:?\s*$/gim,
572
+ weight: 0.55
573
+ },
574
+ {
575
+ id: "fake_system",
576
+ re: /^\s*(?:system|assistant|developer)\s*(?:message|prompt|note|override)?\s*:\s*\S/gim,
577
+ weight: 0.3
578
+ },
579
+ // Indirect injection aimed at the agent reading the content.
580
+ {
581
+ id: "addressed_to_ai",
582
+ re: /\b(?:(?:note|message|instructions?)\s+(?:to|for)\s+(?:the\s+)?(?:ai|assistant|llm|agent|model|chatbot)|(?:ai|assistant|llm|agent)s?\s*(?:reading|processing|summari[sz]ing)\s+this|if\s+you\s+are\s+an?\s+(?:ai|assistant|llm|language\s+model|agent)|aten[çc][ãa]o\s*,?\s*(?:ia|assistente|agente)|se\s+voc[êe]\s+[ée]\s+(?:uma\s+)?(?:ia|intelig[êe]ncia\s+artificial|assistente))\b/gi,
583
+ weight: 0.55
584
+ },
585
+ {
586
+ id: "tool_directive",
587
+ re: /\b(?:call|invoke|use|run|execute|chame|use|execute)\s+(?:the\s+|a\s+|o\s+|a\s+ferramenta\s+)?[`'"]?[\w-]+(?:__[\w-]+)[`'"]?\s*(?:tool|function|ferramenta)?|\b(?:send|forward|email|post|upload|exfiltrate|envie|encaminhe)\b[\s\S]{0,40}?\b(?:(?:all|every|the)\s+)?(?:(?:user'?s?|their|this|todos\s+os|os)\s+)?(?:emails?|files?|documents?|credentials?|passwords?|secrets?|tokens?|keys?|conversation|chat\s+history|dados|arquivos|senhas)\b[\s\S]{0,40}?\b(?:to|para)\s+\S+@\S+|\bto\s+https?:\/\//gi,
588
+ weight: 0.45
589
+ },
590
+ {
591
+ id: "secrecy",
592
+ re: /\b(?:do\s+not|don'?t|never)\s+(?:tell|inform|mention|reveal|show|alert|notify)\s+(?:this\s+to\s+)?(?:the\s+)?user\b|\bwithout\s+(?:telling|informing|notifying)\s+the\s+user\b|\bn[ãa]o\s+(?:conte|informe|mencione|avise)\s+(?:ao|o)\s+usu[áa]rio\b/gi,
593
+ weight: 0.5
594
+ },
595
+ // Exfiltration through rendered markdown/HTML.
596
+ {
597
+ id: "exfil_markdown",
598
+ re: /!\[[^\]]*\]\(\s*https?:\/\/[^)\s]+\?[^)\s]*=[^)\s]*(?:\{|%7B|\$|<)?[^)]*\)|<img\b[^>]*\bsrc\s*=\s*["']?https?:\/\/[^"'>\s]+\?[^"'>\s]*=/gi,
599
+ weight: 0.4
600
+ },
601
+ {
602
+ id: "html_comment_instructions",
603
+ re: /<!--[\s\S]{0,500}?\b(?:ignore|instruction|assistant|ai|agent|system|prompt|instru[çc][ãa]o|assistente)\b[\s\S]{0,500}?-->/gi,
604
+ weight: 0.45
605
+ }
606
+ ];
607
+ var TAG_CHARS = /[\u{E0000}-\u{E007F}]+/gu;
608
+ var BIDI = /[\u202A-\u202E\u2066-\u2069]/g;
609
+ var ZERO_WIDTH_RUN = /[\u200B-\u200D\u2060\uFEFF]{3,}/g;
610
+ function decodeBase64Ascii(b64) {
611
+ try {
612
+ return atob(b64);
613
+ } catch {
614
+ return void 0;
615
+ }
616
+ }
617
+ __name(decodeBase64Ascii, "decodeBase64Ascii");
618
+ function decodeTagChars(s) {
619
+ let out = "";
620
+ for (const ch of s) {
621
+ const cp = ch.codePointAt(0) ?? 0;
622
+ if (cp >= 917536 && cp <= 917630) out += String.fromCharCode(cp - 917504);
623
+ }
624
+ return out;
625
+ }
626
+ __name(decodeTagChars, "decodeTagChars");
627
+ function matchSignals(text, offset, into, weights) {
628
+ for (const s of INJECTION_SIGNALS) {
629
+ s.re.lastIndex = 0;
630
+ for (let m = s.re.exec(text); m; m = s.re.exec(text)) {
631
+ if (m[0].length === 0) {
632
+ s.re.lastIndex++;
633
+ continue;
634
+ }
635
+ weights.set(s.id, Math.max(weights.get(s.id) ?? 0, s.weight));
636
+ const list = into.get(s.id) ?? [];
637
+ list.push({
638
+ detector: "injection",
639
+ category: `injection.${s.id}`,
640
+ start: offset < 0 ? m.index : offset,
641
+ end: offset < 0 ? m.index + m[0].length : offset,
642
+ score: s.weight,
643
+ value: m[0],
644
+ spanned: offset < 0
645
+ });
646
+ into.set(s.id, list);
647
+ }
648
+ }
649
+ }
650
+ __name(matchSignals, "matchSignals");
651
+ function scoreInjection(text, threshold = 0.5) {
652
+ const byId = /* @__PURE__ */ new Map();
653
+ const weights = /* @__PURE__ */ new Map();
654
+ matchSignals(text, -1, byId, weights);
655
+ TAG_CHARS.lastIndex = 0;
656
+ for (let m = TAG_CHARS.exec(text); m; m = TAG_CHARS.exec(text)) {
657
+ weights.set("hidden_unicode", 0.8);
658
+ const list = byId.get("hidden_unicode") ?? [];
659
+ list.push({
660
+ detector: "injection",
661
+ category: "injection.hidden_unicode",
662
+ start: m.index,
663
+ end: m.index + m[0].length,
664
+ score: 0.8,
665
+ value: m[0],
666
+ spanned: true
667
+ });
668
+ byId.set("hidden_unicode", list);
669
+ const decoded = decodeTagChars(m[0]);
670
+ if (decoded) matchSignals(decoded, m.index, byId, weights);
671
+ }
672
+ for (const re of [
673
+ BIDI,
674
+ ZERO_WIDTH_RUN
675
+ ]) {
676
+ re.lastIndex = 0;
677
+ for (let m = re.exec(text); m; m = re.exec(text)) {
678
+ weights.set("invisible_chars", Math.max(weights.get("invisible_chars") ?? 0, 0.3));
679
+ const list = byId.get("invisible_chars") ?? [];
680
+ list.push({
681
+ detector: "injection",
682
+ category: "injection.invisible_chars",
683
+ start: m.index,
684
+ end: m.index + m[0].length,
685
+ score: 0.3,
686
+ value: m[0],
687
+ spanned: true
688
+ });
689
+ byId.set("invisible_chars", list);
690
+ }
691
+ }
692
+ const B64 = /(?<![A-Za-z0-9+/=])[A-Za-z0-9+/]{40,}={0,2}(?![A-Za-z0-9+/=])/g;
693
+ for (let m = B64.exec(text); m; m = B64.exec(text)) {
694
+ const decoded = decodeBase64Ascii(m[0]);
695
+ if (decoded === void 0) continue;
696
+ if (!/^[\x20-\x7E\s]+$/.test(decoded)) continue;
697
+ const inner = /* @__PURE__ */ new Map();
698
+ const innerWeights = /* @__PURE__ */ new Map();
699
+ matchSignals(decoded, m.index, inner, innerWeights);
700
+ if (innerWeights.size > 0) {
701
+ weights.set("encoded_instructions", 0.6);
702
+ byId.set("encoded_instructions", [
703
+ {
704
+ detector: "injection",
705
+ category: "injection.encoded_instructions",
706
+ start: m.index,
707
+ end: m.index + m[0].length,
708
+ score: 0.6,
709
+ value: m[0],
710
+ spanned: true
711
+ }
712
+ ]);
713
+ }
714
+ }
715
+ let keep = 1;
716
+ for (const w of weights.values()) keep *= 1 - w;
717
+ const score = Math.round((1 - keep) * 1e3) / 1e3;
718
+ if (score < threshold) return {
719
+ score,
720
+ findings: []
721
+ };
722
+ const findings = [
723
+ ...byId.values()
724
+ ].flat().map((f) => ({
725
+ ...f,
726
+ score: Math.max(f.score, score)
727
+ }));
728
+ return {
729
+ score,
730
+ findings
731
+ };
732
+ }
733
+ __name(scoreInjection, "scoreInjection");
734
+
735
+ // src/guardrails/detectors/tool-poisoning.ts
736
+ var POISON_SIGNALS = [
737
+ {
738
+ id: "important_tag",
739
+ re: /<\s*\/?\s*(?:important|system|instructions?|secret|hidden|critical)\s*>/gi,
740
+ weight: 0.55
741
+ },
742
+ {
743
+ id: "sensitive_files",
744
+ re: /(?:~\/\.ssh|id_rsa|id_ed25519|\.aws\/credentials|\.env\b|mcp\.json|\.netrc|\.npmrc|\.pypirc|\.git-credentials|\/etc\/passwd|\/etc\/shadow|kubeconfig|\.kube\/config|credentials\.json|cookies\.sqlite|wallet\.dat)/gi,
745
+ weight: 0.65
746
+ },
747
+ {
748
+ id: "read_before_use",
749
+ re: /\b(?:before|prior\s+to)\s+(?:using|calling|invoking|running)\s+(?:this|any|the)\s+(?:tool|function)\b[\s\S]{0,80}?\b(?:read|open|cat|fetch|call|send|include|pass|load)\b/gi,
750
+ weight: 0.5
751
+ },
752
+ {
753
+ id: "hidden_parameter",
754
+ re: /\b(?:pass|put|include|add|send|store)\b[\s\S]{0,40}?\b(?:content|contents|value|output|result|key|token|file)s?\b[\s\S]{0,40}?\b(?:as|in|into)\s+(?:the\s+)?[`'"]?(?:sidenote|side_note|note|notes|metadata|context|extra|comment|debug)[`'"]?\b/gi,
755
+ weight: 0.55
756
+ },
757
+ {
758
+ id: "cross_tool",
759
+ re: /\b(?:when|whenever|if)\s+(?:the\s+)?[`'"]?[\w.-]+[`'"]?\s+(?:tool|function)\s+is\s+(?:called|used|invoked)\b|\b(?:instead\s+of|rather\s+than)\s+(?:calling|using)\s+(?:the\s+)?[`'"]?[\w.-]+[`'"]?\s*(?:tool|function)?\b|\b(?:all|every)\s+(?:emails?|messages?)\s+(?:must|should)\s+(?:be\s+)?(?:sent|forwarded|bcc'?d|cc'?d)\s+to\b/gi,
760
+ weight: 0.5
761
+ },
762
+ {
763
+ id: "override_behaviour",
764
+ re: /\b(?:this\s+(?:tool|instruction)\s+(?:takes|has)\s+(?:precedence|priority)|overrides?\s+(?:all\s+)?(?:other|previous)\s+(?:tools?|instructions?)|very\s+(?:very\s+)?important\s*:?|you\s+must\s+(?:always|first|never))\b/gi,
765
+ weight: 0.35
766
+ }
767
+ ];
768
+ var LONG_DESCRIPTION = 2e3;
769
+ function scoreToolText(text, threshold = 0.5) {
770
+ const weights = /* @__PURE__ */ new Map();
771
+ const findings = [];
772
+ for (const s of POISON_SIGNALS) {
773
+ s.re.lastIndex = 0;
774
+ for (let m = s.re.exec(text); m; m = s.re.exec(text)) {
775
+ weights.set(s.id, s.weight);
776
+ findings.push({
777
+ detector: "tool_poisoning",
778
+ category: `tool_poisoning.${s.id}`,
779
+ start: m.index,
780
+ end: m.index + m[0].length,
781
+ score: s.weight,
782
+ value: m[0],
783
+ spanned: true
784
+ });
785
+ }
786
+ }
787
+ const injection = scoreInjection(text, 0);
788
+ for (const f of injection.findings) {
789
+ const id = f.category.slice("injection.".length);
790
+ const sig = INJECTION_SIGNALS.find((s) => s.id === id);
791
+ weights.set(`inj:${id}`, sig?.weight ?? f.score);
792
+ findings.push({
793
+ ...f,
794
+ detector: "tool_poisoning",
795
+ category: `tool_poisoning.${id}`
796
+ });
797
+ }
798
+ if (text.length > LONG_DESCRIPTION) weights.set("long_description", 0.2);
799
+ let keep = 1;
800
+ for (const w of weights.values()) keep *= 1 - w;
801
+ const score = Math.round((1 - keep) * 1e3) / 1e3;
802
+ return score >= threshold ? {
803
+ score,
804
+ findings
805
+ } : {
806
+ score,
807
+ findings: []
808
+ };
809
+ }
810
+ __name(scoreToolText, "scoreToolText");
811
+ function toolText(tool) {
812
+ const parts = [];
813
+ if (tool.title) parts.push(tool.title);
814
+ if (tool.description) parts.push(tool.description);
815
+ const walk = /* @__PURE__ */ __name((schema, depth) => {
816
+ if (!schema || typeof schema !== "object" || depth > 6) return;
817
+ const s = schema;
818
+ if (typeof s.description === "string") parts.push(s.description);
819
+ if (typeof s.title === "string") parts.push(s.title);
820
+ for (const key of [
821
+ "properties",
822
+ "definitions",
823
+ "$defs"
824
+ ]) {
825
+ const props = s[key];
826
+ if (props && typeof props === "object") for (const [name, child] of Object.entries(props)) {
827
+ parts.push(name);
828
+ walk(child, depth + 1);
829
+ }
830
+ }
831
+ for (const key of [
832
+ "items",
833
+ "additionalProperties"
834
+ ]) walk(s[key], depth + 1);
835
+ for (const key of [
836
+ "anyOf",
837
+ "oneOf",
838
+ "allOf"
839
+ ]) {
840
+ const list = s[key];
841
+ if (Array.isArray(list)) for (const child of list) walk(child, depth + 1);
842
+ }
843
+ if (Array.isArray(s.enum)) {
844
+ for (const e of s.enum) if (typeof e === "string") parts.push(e);
845
+ }
846
+ if (typeof s.default === "string") parts.push(s.default);
847
+ }, "walk");
848
+ walk(tool.inputSchema, 0);
849
+ return parts.join("\n");
850
+ }
851
+ __name(toolText, "toolText");
852
+
853
+ // src/guardrails/detectors/custom.ts
854
+ function compileFlagged(pattern) {
855
+ return pattern.startsWith("(?i)") ? new RegExp(pattern.slice(4), "i") : new RegExp(pattern);
856
+ }
857
+ __name(compileFlagged, "compileFlagged");
858
+ var compiled = /* @__PURE__ */ new Map();
859
+ function compileGuardPattern(pattern) {
860
+ let re = compiled.get(pattern);
861
+ if (!re) {
862
+ const base = compileFlagged(pattern);
863
+ re = new RegExp(base.source, `${base.flags.replace("g", "")}g`);
864
+ if (compiled.size > 5e3) compiled.clear();
865
+ compiled.set(pattern, re);
866
+ }
867
+ return re;
868
+ }
869
+ __name(compileGuardPattern, "compileGuardPattern");
870
+ function detectRegex(text, patterns, label = "custom") {
871
+ const out = [];
872
+ for (const p of patterns) {
873
+ let re;
874
+ try {
875
+ re = compileGuardPattern(p);
876
+ } catch {
877
+ continue;
878
+ }
879
+ re.lastIndex = 0;
880
+ let guard = 0;
881
+ for (let m = re.exec(text); m && guard < 1e3; m = re.exec(text), guard++) {
882
+ if (m[0].length === 0) {
883
+ re.lastIndex++;
884
+ continue;
885
+ }
886
+ out.push({
887
+ detector: "regex",
888
+ category: `regex.${label}`,
889
+ start: m.index,
890
+ end: m.index + m[0].length,
891
+ score: 1,
892
+ value: m[0],
893
+ spanned: true
894
+ });
895
+ }
896
+ }
897
+ return out;
898
+ }
899
+ __name(detectRegex, "detectRegex");
900
+ var COMBINING = /[\u0300-\u036f]/g;
901
+ function fold(s) {
902
+ let out = "";
903
+ for (const ch of s) {
904
+ const base = ch.normalize("NFD").replace(COMBINING, "");
905
+ const c = (base.length === ch.length ? base : ch).toLowerCase();
906
+ out += c.length === ch.length ? c : ch;
907
+ }
908
+ return out;
909
+ }
910
+ __name(fold, "fold");
911
+ var escapeRe = /* @__PURE__ */ __name((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escapeRe");
912
+ function detectKeywords(text, words, label = "keywords") {
913
+ const clean = words.map((w) => fold(w.trim())).filter(Boolean);
914
+ if (clean.length === 0) return [];
915
+ const re = new RegExp(`(?<![\\p{L}\\p{N}_])(?:${clean.map(escapeRe).join("|")})(?![\\p{L}\\p{N}_])`, "gu");
916
+ const folded = fold(text);
917
+ const out = [];
918
+ for (let m = re.exec(folded); m; m = re.exec(folded)) {
919
+ out.push({
920
+ detector: "keywords",
921
+ category: `keywords.${label}`,
922
+ start: m.index,
923
+ end: m.index + m[0].length,
924
+ score: 1,
925
+ value: text.slice(m.index, m.index + m[0].length),
926
+ spanned: true
927
+ });
928
+ }
929
+ return out;
930
+ }
931
+ __name(detectKeywords, "detectKeywords");
932
+
933
+ // src/guardrails/glob.ts
934
+ var cache = /* @__PURE__ */ new Map();
935
+ function globToRegExp(pattern) {
936
+ let re = cache.get(pattern);
937
+ if (!re) {
938
+ const body = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
939
+ re = new RegExp(`^${body}$`);
940
+ cache.set(pattern, re);
941
+ }
942
+ return re;
943
+ }
944
+ __name(globToRegExp, "globToRegExp");
945
+ function matchesAny(value, patterns) {
946
+ if (!patterns || patterns.length === 0) return true;
947
+ return patterns.some((p) => globToRegExp(p).test(value));
948
+ }
949
+ __name(matchesAny, "matchesAny");
950
+
951
+ // src/guardrails/vault.ts
952
+ var Vault = class _Vault {
953
+ static {
954
+ __name(this, "Vault");
955
+ }
956
+ byValue = /* @__PURE__ */ new Map();
957
+ byToken = /* @__PURE__ */ new Map();
958
+ counters = /* @__PURE__ */ new Map();
959
+ /**
960
+ * Placeholder for `value` under `label` (e.g. `CREDIT_CARD`). Reversible placeholders look like
961
+ * `[CREDIT_CARD_1]`; one-way ones (tool results, answers, rules with `restore: false`) like
962
+ * `[REDACTED_CREDIT_CARD_1]`, so they can never be mistaken for a reversible one.
963
+ */
964
+ tokenFor(label, value, restorable) {
965
+ const ns = restorable ? label : `REDACTED_${label}`;
966
+ const key = `${ns}\0${value}`;
967
+ const existing = this.byValue.get(key);
968
+ if (existing) return existing;
969
+ const n = (this.counters.get(ns) ?? 0) + 1;
970
+ this.counters.set(ns, n);
971
+ const token = `[${ns}_${n}]`;
972
+ this.byValue.set(key, token);
973
+ this.byToken.set(token, {
974
+ value,
975
+ restorable
976
+ });
977
+ return token;
978
+ }
979
+ get size() {
980
+ return this.byToken.size;
981
+ }
982
+ get restorableCount() {
983
+ let n = 0;
984
+ for (const e of this.byToken.values()) if (e.restorable) n++;
985
+ return n;
986
+ }
987
+ /** Longest placeholder, for stream hold-back windows. */
988
+ get maxTokenLength() {
989
+ let n = 0;
990
+ for (const t of this.byToken.keys()) n = Math.max(n, t.length);
991
+ return n;
992
+ }
993
+ /** Puts restorable values back. `jsonString`: the text is inside a JSON string literal. */
994
+ restore(text, jsonString = false) {
995
+ if (this.byToken.size === 0 || !text.includes("[")) return text;
996
+ return text.replace(/\[([A-Z][A-Z0-9_]*_\d+)\]/g, (whole) => {
997
+ const entry = this.byToken.get(whole);
998
+ if (!entry?.restorable) return whole;
999
+ return jsonString ? JSON.stringify(entry.value).slice(1, -1) : entry.value;
1000
+ });
1001
+ }
1002
+ /** Whether `text` ends with what could be the beginning of a placeholder (streaming). */
1003
+ pendingPrefix(text) {
1004
+ const i = text.lastIndexOf("[");
1005
+ if (i === -1) return 0;
1006
+ const tail = text.slice(i);
1007
+ if (tail.length > this.maxTokenLength) return 0;
1008
+ return /^\[[A-Z0-9_]*$/.test(tail) ? tail.length : 0;
1009
+ }
1010
+ /** Every entry, in the order the placeholders were minted. */
1011
+ toJSON() {
1012
+ return {
1013
+ entries: [
1014
+ ...this.byToken
1015
+ ].map(([token, e]) => ({
1016
+ token,
1017
+ value: e.value,
1018
+ restorable: e.restorable
1019
+ }))
1020
+ };
1021
+ }
1022
+ /** Rebuilds a vault from {@link toJSON}; placeholders keep their numbers and counters resume. */
1023
+ static fromJSON(snapshot) {
1024
+ const vault = new _Vault();
1025
+ for (const { token, value, restorable } of snapshot.entries) {
1026
+ const m = /^\[([A-Z][A-Z0-9_]*)_(\d+)\]$/.exec(token);
1027
+ if (!m) continue;
1028
+ const ns = m[1] ?? "";
1029
+ const n = Number(m[2]);
1030
+ vault.byValue.set(`${ns}\0${value}`, token);
1031
+ vault.byToken.set(token, {
1032
+ value,
1033
+ restorable
1034
+ });
1035
+ vault.counters.set(ns, Math.max(vault.counters.get(ns) ?? 0, n));
1036
+ }
1037
+ return vault;
1038
+ }
1039
+ };
1040
+ function labelFor(category) {
1041
+ const tail = category.includes(".") ? category.slice(category.indexOf(".") + 1) : category;
1042
+ const label = tail.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
1043
+ return label || "REDACTED";
1044
+ }
1045
+ __name(labelFor, "labelFor");
1046
+
1047
+ // src/guardrails/engine.ts
1048
+ var DEFAULT_MESSAGES = {
1049
+ llm_request: "This message was blocked by your organization's AI guardrails.",
1050
+ llm_response: "The answer was withheld by your organization's AI guardrails.",
1051
+ tool_args: "This tool call was blocked by your organization's AI guardrails.",
1052
+ tool_result: "The tool's result was withheld by your organization's AI guardrails.",
1053
+ tool_description: "This tool was quarantined by your organization's AI guardrails."
1054
+ };
1055
+ var DEFAULT_APPROVAL_MESSAGE = "This needs approval under your organization's AI guardrails before it can go ahead.";
1056
+ var INJECTION_REPLACEMENT = "[removed: suspected prompt injection]";
1057
+ var ruleName = /* @__PURE__ */ __name((rule) => rule.name ?? rule.id, "ruleName");
1058
+ function ruleApplies(rule, ctx) {
1059
+ if (rule.enabled === false || !rule.stages.includes(ctx.stage)) return false;
1060
+ const tools = rule.match?.tools;
1061
+ if (tools?.length) {
1062
+ const toolStage = ctx.stage === "tool_args" || ctx.stage === "tool_result" || ctx.stage === "tool_description";
1063
+ if (!toolStage || !matchesAny(ctx.tool ?? "", tools)) return false;
1064
+ }
1065
+ return rule.when === void 0 || rule.when(ctx);
1066
+ }
1067
+ __name(ruleApplies, "ruleApplies");
1068
+ function orderRules(rules) {
1069
+ return [
1070
+ ...rules
1071
+ ].sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100) || ruleName(a).localeCompare(ruleName(b)));
1072
+ }
1073
+ __name(orderRules, "orderRules");
1074
+ async function runDetector(spec, text, ctx) {
1075
+ if (!text) return [];
1076
+ switch (spec.kind) {
1077
+ case "pii":
1078
+ return detectPii(text, spec.types?.length ? spec.types : void 0);
1079
+ case "secrets":
1080
+ return detectSecrets(text, spec.types?.length ? spec.types : void 0);
1081
+ case "injection":
1082
+ return scoreInjection(text, spec.threshold ?? 0.5).findings;
1083
+ case "tool_poisoning":
1084
+ return scoreToolText(text, spec.threshold ?? 0.5).findings;
1085
+ case "regex":
1086
+ return detectRegex(text, spec.patterns, spec.label ?? "custom");
1087
+ case "keywords":
1088
+ return detectKeywords(text, spec.words, spec.label ?? "keywords");
1089
+ case "custom":
1090
+ return spec.detect(text, ctx);
1091
+ }
1092
+ }
1093
+ __name(runDetector, "runDetector");
1094
+ function spanLocator(rules) {
1095
+ const specs = rules.flatMap((r) => r.detectors).filter((d) => d.kind !== "custom" && d.kind !== "tool_poisoning");
1096
+ if (specs.length === 0) return void 0;
1097
+ return (text) => specs.flatMap((d) => {
1098
+ switch (d.kind) {
1099
+ case "pii":
1100
+ return detectPii(text, d.types?.length ? d.types : void 0);
1101
+ case "secrets":
1102
+ return detectSecrets(text, d.types?.length ? d.types : void 0);
1103
+ case "regex":
1104
+ return detectRegex(text, d.patterns);
1105
+ case "keywords":
1106
+ return detectKeywords(text, d.words);
1107
+ case "injection":
1108
+ return scoreInjection(text, 0).findings;
1109
+ default:
1110
+ return [];
1111
+ }
1112
+ });
1113
+ }
1114
+ __name(spanLocator, "spanLocator");
1115
+ function resolveOverlaps(findings, text) {
1116
+ const sorted = [
1117
+ ...findings
1118
+ ].filter((f) => f.spanned !== false && f.end > f.start).sort((a, b) => a.start - b.start || b.end - a.end || b.score - a.score);
1119
+ const out = [];
1120
+ for (const f of sorted) {
1121
+ const last = out[out.length - 1];
1122
+ if (!last || f.start >= last.end) {
1123
+ out.push({
1124
+ ...f
1125
+ });
1126
+ continue;
1127
+ }
1128
+ const longer = f.end - f.start > last.end - last.start || f.end - f.start === last.end - last.start && f.score > last.score ? f : last;
1129
+ const merged = {
1130
+ ...longer,
1131
+ start: last.start,
1132
+ end: Math.max(last.end, f.end)
1133
+ };
1134
+ if (text !== void 0) merged.value = text.slice(merged.start, merged.end);
1135
+ out[out.length - 1] = merged;
1136
+ }
1137
+ return out;
1138
+ }
1139
+ __name(resolveOverlaps, "resolveOverlaps");
1140
+ function replacementFor(f, vault, restorable) {
1141
+ if (f.replacement !== void 0) return f.replacement;
1142
+ if (f.detector === "injection" || f.detector === "tool_poisoning") return INJECTION_REPLACEMENT;
1143
+ return vault.tokenFor(labelFor(f.category), f.value, restorable);
1144
+ }
1145
+ __name(replacementFor, "replacementFor");
1146
+ function applyRedactions(text, findings, vault, restorable) {
1147
+ const whole = findings.find((f) => f.spanned === false);
1148
+ if (whole) {
1149
+ return {
1150
+ text: whole.replacement ?? `[removed: ${whole.category}]`,
1151
+ count: 1
1152
+ };
1153
+ }
1154
+ const spans = resolveOverlaps(findings, text);
1155
+ if (spans.length === 0) return {
1156
+ text,
1157
+ count: 0
1158
+ };
1159
+ let out = "";
1160
+ let at = 0;
1161
+ for (const f of spans) {
1162
+ out += text.slice(at, f.start) + replacementFor(f, vault, restorable);
1163
+ at = f.end;
1164
+ }
1165
+ return {
1166
+ text: out + text.slice(at),
1167
+ count: spans.length
1168
+ };
1169
+ }
1170
+ __name(applyRedactions, "applyRedactions");
1171
+ function memoKey(spec) {
1172
+ return spec.kind === "custom" ? `custom\0${spec.name}` : JSON.stringify(spec);
1173
+ }
1174
+ __name(memoKey, "memoKey");
1175
+ async function scan(rules, ctx, segments, vault = new Vault(), options = {}) {
1176
+ const started = performance.now();
1177
+ const applicable = orderRules(rules).filter((r) => ruleApplies(r, ctx));
1178
+ const result = {
1179
+ stage: ctx.stage,
1180
+ action: "allow",
1181
+ segments: segments.map((s) => s.text),
1182
+ changed: false,
1183
+ hits: [],
1184
+ vault,
1185
+ redactions: 0,
1186
+ latencyMs: 0
1187
+ };
1188
+ if (applicable.length === 0 || segments.length === 0) {
1189
+ result.latencyMs = performance.now() - started;
1190
+ return result;
1191
+ }
1192
+ const stageMessage = options.messages?.[ctx.stage] ?? DEFAULT_MESSAGES[ctx.stage];
1193
+ const restorableStage = (options.restorableStages ?? [
1194
+ "llm_request"
1195
+ ]).includes(ctx.stage);
1196
+ const memo = /* @__PURE__ */ new Map();
1197
+ const detect = /* @__PURE__ */ __name((spec, index) => {
1198
+ const key = `${index}\0${memoKey(spec)}`;
1199
+ let p = memo.get(key);
1200
+ if (!p) {
1201
+ p = Promise.resolve().then(() => runDetector(spec, segments[index]?.text ?? "", ctx));
1202
+ memo.set(key, p);
1203
+ }
1204
+ return p;
1205
+ }, "detect");
1206
+ const outcomes = [];
1207
+ for (const rule of applicable) {
1208
+ const sources = rule.match?.sources;
1209
+ const indexes = segments.map((s, i) => ({
1210
+ s,
1211
+ i
1212
+ })).filter(({ s }) => !sources?.length || !s.source || sources.includes(s.source)).map(({ i }) => i);
1213
+ const outcome = {
1214
+ rule,
1215
+ bySegment: /* @__PURE__ */ new Map()
1216
+ };
1217
+ if (rule.detectors.length === 0) {
1218
+ if (rule.action === "allow" || rule.action === "block" || rule.action === "approve") outcomes.push(outcome);
1219
+ if (rule.action === "allow") break;
1220
+ continue;
1221
+ }
1222
+ const jobs = rule.detectors.flatMap((spec) => indexes.map(async (i) => ({
1223
+ i,
1224
+ found: await detect(spec, i)
1225
+ })));
1226
+ const settled = await Promise.allSettled(jobs);
1227
+ for (const s of settled) {
1228
+ if (s.status === "rejected") {
1229
+ outcome.failed = s.reason?.message ?? String(s.reason);
1230
+ continue;
1231
+ }
1232
+ if (s.value.found.length === 0) continue;
1233
+ const list = outcome.bySegment.get(s.value.i) ?? [];
1234
+ list.push(...s.value.found);
1235
+ outcome.bySegment.set(s.value.i, list);
1236
+ }
1237
+ if (outcome.bySegment.size === 0 && !outcome.failed) continue;
1238
+ outcomes.push(outcome);
1239
+ if (rule.action === "allow" && outcome.bySegment.size > 0) break;
1240
+ }
1241
+ const redactBySegment = /* @__PURE__ */ new Map();
1242
+ for (const o of outcomes) {
1243
+ const { rule } = o;
1244
+ const name = ruleName(rule);
1245
+ const failMode = rule.options?.failMode ?? "open";
1246
+ let action = rule.action;
1247
+ if (o.failed) {
1248
+ result.hits.push({
1249
+ ruleId: rule.id,
1250
+ ruleName: name,
1251
+ action: failMode === "closed" ? "block" : "log",
1252
+ detector: "detector_error",
1253
+ category: "error.detector_unavailable",
1254
+ score: 0,
1255
+ count: 1,
1256
+ values: [],
1257
+ fresh: true,
1258
+ error: o.failed.slice(0, 300),
1259
+ failMode
1260
+ });
1261
+ if (failMode === "closed" && o.bySegment.size === 0) action = "block";
1262
+ else if (o.bySegment.size === 0) continue;
1263
+ }
1264
+ const historyOnly = (action === "block" || action === "approve") && o.bySegment.size > 0 && [
1265
+ ...o.bySegment.keys()
1266
+ ].every((i) => segments[i]?.fresh === false);
1267
+ if (historyOnly) {
1268
+ action = "redact";
1269
+ for (const [i, findings] of o.bySegment) {
1270
+ const entry = redactBySegment.get(i) ?? {
1271
+ findings: [],
1272
+ restorable: false
1273
+ };
1274
+ entry.findings.push(...findings);
1275
+ redactBySegment.set(i, entry);
1276
+ }
1277
+ }
1278
+ if (ACTION_SEVERITY[action] > ACTION_SEVERITY[result.action]) {
1279
+ result.action = action;
1280
+ if (action === "block" || action === "approve") {
1281
+ result.decisive = {
1282
+ id: rule.id,
1283
+ name,
1284
+ message: rule.options?.message || (o.failed && o.bySegment.size === 0 ? `${stageMessage} (a required check could not run)` : action === "approve" ? options.approvalMessage ?? DEFAULT_APPROVAL_MESSAGE : stageMessage)
1285
+ };
1286
+ }
1287
+ }
1288
+ if (o.bySegment.size === 0 && rule.detectors.length === 0) {
1289
+ result.hits.push({
1290
+ ruleId: rule.id,
1291
+ ruleName: name,
1292
+ action,
1293
+ detector: "scope",
1294
+ category: "scope.match",
1295
+ score: 1,
1296
+ count: 1,
1297
+ values: [],
1298
+ fresh: true
1299
+ });
1300
+ continue;
1301
+ }
1302
+ const grouped = /* @__PURE__ */ new Map();
1303
+ for (const [i, findings] of o.bySegment) {
1304
+ for (const f of findings) {
1305
+ const hit = grouped.get(f.category) ?? {
1306
+ ruleId: rule.id,
1307
+ ruleName: name,
1308
+ action,
1309
+ detector: f.detector,
1310
+ category: f.category,
1311
+ score: 0,
1312
+ count: 0,
1313
+ values: [],
1314
+ fresh: false
1315
+ };
1316
+ hit.count++;
1317
+ hit.score = Math.max(hit.score, f.score);
1318
+ if (f.value && !hit.values.includes(f.value) && hit.values.length < 20) hit.values.push(f.value);
1319
+ if (segments[i]?.fresh !== false) hit.fresh = true;
1320
+ grouped.set(f.category, hit);
1321
+ }
1322
+ if (action === "redact" && !historyOnly) {
1323
+ const entry = redactBySegment.get(i) ?? {
1324
+ findings: [],
1325
+ restorable: false
1326
+ };
1327
+ entry.findings.push(...findings);
1328
+ entry.restorable ||= restorableStage && rule.options?.restore !== false;
1329
+ redactBySegment.set(i, entry);
1330
+ }
1331
+ }
1332
+ result.hits.push(...grouped.values());
1333
+ }
1334
+ if (result.action !== "block" && result.action !== "approve") {
1335
+ for (const [i, { findings, restorable }] of redactBySegment) {
1336
+ const original = segments[i]?.text ?? "";
1337
+ const { text, count } = applyRedactions(original, findings, vault, restorable);
1338
+ if (count > 0 && text !== original) {
1339
+ result.segments[i] = text;
1340
+ result.redactions += count;
1341
+ result.changed = true;
1342
+ }
1343
+ }
1344
+ }
1345
+ result.latencyMs = performance.now() - started;
1346
+ return result;
1347
+ }
1348
+ __name(scan, "scan");
1349
+
1350
+ // src/guardrails/segments.ts
1351
+ var MAX_SLOTS = 2e3;
1352
+ function sourceOf(role) {
1353
+ if (role === "system" || role === "developer") return "system";
1354
+ if (role === "assistant") return "assistant";
1355
+ if (role === "tool" || role === "function") return "tool";
1356
+ return "user";
1357
+ }
1358
+ __name(sourceOf, "sourceOf");
1359
+ function freshFrom(messages) {
1360
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i]?.role === "assistant") return i + 1;
1361
+ return 0;
1362
+ }
1363
+ __name(freshFrom, "freshFrom");
1364
+ function jsonSlots(root, source, fresh, replaceRoot) {
1365
+ const out = [];
1366
+ const walk = /* @__PURE__ */ __name((value, set, depth) => {
1367
+ if (out.length >= MAX_SLOTS || depth > 10) return;
1368
+ if (typeof value === "string") {
1369
+ if (value) out.push({
1370
+ segment: {
1371
+ text: value,
1372
+ source,
1373
+ fresh
1374
+ },
1375
+ set
1376
+ });
1377
+ return;
1378
+ }
1379
+ if (Array.isArray(value)) {
1380
+ for (let i = 0; i < value.length; i++) {
1381
+ walk(value[i], (t) => {
1382
+ value[i] = t;
1383
+ }, depth + 1);
1384
+ }
1385
+ return;
1386
+ }
1387
+ if (value && typeof value === "object") {
1388
+ const o = value;
1389
+ for (const k of Object.keys(o)) walk(o[k], (t) => {
1390
+ o[k] = t;
1391
+ }, depth + 1);
1392
+ }
1393
+ }, "walk");
1394
+ walk(root, replaceRoot ?? (() => void 0), 0);
1395
+ return out;
1396
+ }
1397
+ __name(jsonSlots, "jsonSlots");
1398
+ function jsonStringSlot(holder, key, source, fresh) {
1399
+ const v = holder[key];
1400
+ if (typeof v !== "string" || !v) return [];
1401
+ return [
1402
+ {
1403
+ segment: {
1404
+ text: v,
1405
+ source,
1406
+ fresh
1407
+ },
1408
+ set: /* @__PURE__ */ __name((t) => {
1409
+ holder[key] = t;
1410
+ }, "set"),
1411
+ json: true
1412
+ }
1413
+ ];
1414
+ }
1415
+ __name(jsonStringSlot, "jsonStringSlot");
1416
+ function contentSlots(holder, source, fresh) {
1417
+ const content = holder.content;
1418
+ if (typeof content === "string") {
1419
+ return content ? [
1420
+ {
1421
+ segment: {
1422
+ text: content,
1423
+ source,
1424
+ fresh
1425
+ },
1426
+ set: /* @__PURE__ */ __name((t) => {
1427
+ holder.content = t;
1428
+ }, "set")
1429
+ }
1430
+ ] : [];
1431
+ }
1432
+ if (!Array.isArray(content)) return [];
1433
+ const out = [];
1434
+ for (const part of content) {
1435
+ if (!part || typeof part !== "object") continue;
1436
+ const p = part;
1437
+ if ((p.type === "text" || p.type === "input_text" || p.type === "output_text") && typeof p.text === "string") {
1438
+ out.push({
1439
+ segment: {
1440
+ text: p.text,
1441
+ source,
1442
+ fresh
1443
+ },
1444
+ set: /* @__PURE__ */ __name((t) => {
1445
+ p.text = t;
1446
+ }, "set")
1447
+ });
1448
+ } else if (p.type === "tool_result") {
1449
+ out.push(...contentSlots(p, "tool", fresh));
1450
+ } else if (p.type === "tool_use" && p.input && typeof p.input === "object") {
1451
+ out.push(...jsonSlots(p.input, "assistant", fresh));
1452
+ }
1453
+ }
1454
+ return out;
1455
+ }
1456
+ __name(contentSlots, "contentSlots");
1457
+ function requestSlots(api, body) {
1458
+ const out = [];
1459
+ if (api === "anthropic") {
1460
+ if (typeof body.system === "string" && body.system) {
1461
+ out.push({
1462
+ segment: {
1463
+ text: body.system,
1464
+ source: "system",
1465
+ fresh: true
1466
+ },
1467
+ set: /* @__PURE__ */ __name((t) => {
1468
+ body.system = t;
1469
+ }, "set")
1470
+ });
1471
+ } else if (Array.isArray(body.system)) {
1472
+ out.push(...contentSlots({
1473
+ content: body.system
1474
+ }, "system", true));
1475
+ }
1476
+ }
1477
+ if (Array.isArray(body.messages)) {
1478
+ const messages = body.messages;
1479
+ const from = freshFrom(messages);
1480
+ messages.forEach((m, i) => {
1481
+ if (!m || typeof m !== "object") return;
1482
+ const fresh = i >= from || m.role === "system";
1483
+ const source = sourceOf(m.role);
1484
+ out.push(...contentSlots(m, source, fresh));
1485
+ if (Array.isArray(m.tool_calls)) {
1486
+ for (const call of m.tool_calls) if (call?.function) out.push(...jsonStringSlot(call.function, "arguments", "assistant", fresh));
1487
+ }
1488
+ });
1489
+ }
1490
+ if (typeof body.input === "string" || Array.isArray(body.input)) {
1491
+ const holder = body;
1492
+ if (typeof holder.input === "string") {
1493
+ out.push({
1494
+ segment: {
1495
+ text: holder.input,
1496
+ source: "user",
1497
+ fresh: true
1498
+ },
1499
+ set: /* @__PURE__ */ __name((t) => {
1500
+ holder.input = t;
1501
+ }, "set")
1502
+ });
1503
+ } else {
1504
+ const list = holder.input;
1505
+ list.forEach((v, i) => {
1506
+ if (typeof v === "string" && v) out.push({
1507
+ segment: {
1508
+ text: v,
1509
+ source: "user",
1510
+ fresh: true
1511
+ },
1512
+ set: /* @__PURE__ */ __name((t) => {
1513
+ list[i] = t;
1514
+ }, "set")
1515
+ });
1516
+ });
1517
+ }
1518
+ }
1519
+ return out.slice(0, MAX_SLOTS);
1520
+ }
1521
+ __name(requestSlots, "requestSlots");
1522
+ function responseSlots(api, body) {
1523
+ if (!body || typeof body !== "object") return [];
1524
+ const b = body;
1525
+ const out = [];
1526
+ if (api === "anthropic") {
1527
+ if (Array.isArray(b.content)) out.push(...contentSlots(b, "assistant", true));
1528
+ return out;
1529
+ }
1530
+ for (const choice of Array.isArray(b.choices) ? b.choices : []) {
1531
+ const msg = choice?.message;
1532
+ if (!msg || typeof msg !== "object") continue;
1533
+ out.push(...contentSlots(msg, "assistant", true));
1534
+ if (Array.isArray(msg.tool_calls)) {
1535
+ for (const call of msg.tool_calls) if (call?.function) out.push(...jsonStringSlot(call.function, "arguments", "assistant", true));
1536
+ }
1537
+ }
1538
+ return out;
1539
+ }
1540
+ __name(responseSlots, "responseSlots");
1541
+ function refuseResponse(api, body, message) {
1542
+ if (!body || typeof body !== "object") return body;
1543
+ const b = body;
1544
+ if (api === "anthropic") {
1545
+ return {
1546
+ ...b,
1547
+ content: [
1548
+ {
1549
+ type: "text",
1550
+ text: message
1551
+ }
1552
+ ],
1553
+ stop_reason: "refusal"
1554
+ };
1555
+ }
1556
+ return {
1557
+ ...b,
1558
+ choices: (Array.isArray(b.choices) && b.choices.length > 0 ? b.choices : [
1559
+ {
1560
+ index: 0
1561
+ }
1562
+ ]).map((c) => ({
1563
+ ...c,
1564
+ message: {
1565
+ role: "assistant",
1566
+ content: message
1567
+ },
1568
+ finish_reason: "content_filter"
1569
+ }))
1570
+ };
1571
+ }
1572
+ __name(refuseResponse, "refuseResponse");
1573
+ function toolResultSlots(result) {
1574
+ const out = [];
1575
+ if (Array.isArray(result.content)) {
1576
+ for (const item of result.content) {
1577
+ if (!item || typeof item !== "object") continue;
1578
+ if (item.type === "text" && typeof item.text === "string") {
1579
+ out.push({
1580
+ segment: {
1581
+ text: item.text,
1582
+ source: "tool",
1583
+ fresh: true
1584
+ },
1585
+ set: /* @__PURE__ */ __name((t) => {
1586
+ item.text = t;
1587
+ }, "set")
1588
+ });
1589
+ } else if (item.type === "resource" && item.resource && typeof item.resource.text === "string") {
1590
+ out.push({
1591
+ segment: {
1592
+ text: item.resource.text,
1593
+ source: "tool",
1594
+ fresh: true
1595
+ },
1596
+ set: /* @__PURE__ */ __name((t) => {
1597
+ item.resource.text = t;
1598
+ }, "set")
1599
+ });
1600
+ }
1601
+ }
1602
+ }
1603
+ if (result.structuredContent && typeof result.structuredContent === "object") out.push(...jsonSlots(result.structuredContent, "tool", true));
1604
+ return out.slice(0, MAX_SLOTS);
1605
+ }
1606
+ __name(toolResultSlots, "toolResultSlots");
1607
+
1608
+ // src/guardrails/stream-guard.ts
1609
+ var Channel = class Channel2 {
1610
+ static {
1611
+ __name(this, "Channel");
1612
+ }
1613
+ json;
1614
+ pending = "";
1615
+ constructor(json) {
1616
+ this.json = json;
1617
+ }
1618
+ };
1619
+ var StreamGuard = class {
1620
+ static {
1621
+ __name(this, "StreamGuard");
1622
+ }
1623
+ opts;
1624
+ buffer = "";
1625
+ decoder = new TextDecoder();
1626
+ channels = /* @__PURE__ */ new Map();
1627
+ window;
1628
+ holdback;
1629
+ blocked;
1630
+ template;
1631
+ openBlocks = /* @__PURE__ */ new Set();
1632
+ maxBlock = -1;
1633
+ done = false;
1634
+ constructor(opts) {
1635
+ this.opts = opts;
1636
+ this.window = opts.window ?? 256;
1637
+ this.holdback = opts.holdback ?? 64;
1638
+ }
1639
+ /** Whether the guard changes anything (else the caller can pass bytes straight through). */
1640
+ get active() {
1641
+ return !!this.opts.scan || this.opts.restore && this.opts.vault.restorableCount > 0;
1642
+ }
1643
+ async push(chunk) {
1644
+ this.buffer += typeof chunk === "string" ? chunk : this.decoder.decode(chunk, {
1645
+ stream: true
1646
+ });
1647
+ let out = "";
1648
+ for (; ; ) {
1649
+ const m = /\r?\n\r?\n/.exec(this.buffer);
1650
+ if (!m) break;
1651
+ const raw = this.buffer.slice(0, m.index + m[0].length);
1652
+ this.buffer = this.buffer.slice(m.index + m[0].length);
1653
+ out += await this.frame(parseFrame(raw));
1654
+ }
1655
+ return out;
1656
+ }
1657
+ async end() {
1658
+ let out = "";
1659
+ if (this.buffer.trim()) out += await this.frame(parseFrame(`${this.buffer}
1660
+
1661
+ `));
1662
+ this.buffer = "";
1663
+ if (!this.blocked && !this.done) out += await this.flushAll();
1664
+ return out;
1665
+ }
1666
+ async frame(f) {
1667
+ if (this.blocked) return "";
1668
+ if (this.opts.api === "openai") return this.openaiFrame(f);
1669
+ return this.anthropicFrame(f);
1670
+ }
1671
+ // ---- OpenAI chat-completion chunks ---------------------------------------------------------
1672
+ async openaiFrame(f) {
1673
+ if (!f.data) return f.raw;
1674
+ if (f.data.trim() === "[DONE]") {
1675
+ const tail = await this.flushAll();
1676
+ this.done = true;
1677
+ return tail + f.raw;
1678
+ }
1679
+ let chunk;
1680
+ try {
1681
+ chunk = JSON.parse(f.data);
1682
+ } catch {
1683
+ return f.raw;
1684
+ }
1685
+ this.template ??= {
1686
+ id: chunk.id,
1687
+ object: chunk.object,
1688
+ created: chunk.created,
1689
+ model: chunk.model
1690
+ };
1691
+ if (!Array.isArray(chunk.choices) || chunk.choices.length === 0) return f.raw;
1692
+ let prefix = "";
1693
+ let keep = false;
1694
+ for (const choice of chunk.choices) {
1695
+ const delta = choice.delta ?? {};
1696
+ const ci = choice.index ?? 0;
1697
+ if (typeof delta.content === "string" && delta.content) {
1698
+ const r = await this.feed(`c:${ci}`, delta.content, false);
1699
+ if (this.blocked) return prefix + this.openaiBlock(ci);
1700
+ delta.content = r;
1701
+ if (r) keep = true;
1702
+ }
1703
+ if (Array.isArray(delta.tool_calls)) {
1704
+ for (const call of delta.tool_calls) {
1705
+ const fn = call.function;
1706
+ if (fn && typeof fn.arguments === "string" && fn.arguments) {
1707
+ const r = await this.feed(`t:${ci}:${call.index ?? 0}`, fn.arguments, true);
1708
+ if (this.blocked) return prefix + this.openaiBlock(ci);
1709
+ fn.arguments = r;
1710
+ }
1711
+ if (call.id || fn?.name || fn?.arguments) keep = true;
1712
+ }
1713
+ }
1714
+ if (delta.role || delta.reasoning_content || delta.refusal) keep = true;
1715
+ if (choice.finish_reason) {
1716
+ prefix += await this.flushChoice(ci);
1717
+ if (this.blocked) return prefix + this.openaiBlock(ci);
1718
+ keep = true;
1719
+ }
1720
+ }
1721
+ if (chunk.usage) keep = true;
1722
+ return keep ? `${prefix}data: ${JSON.stringify(chunk)}
1723
+
1724
+ ` : prefix;
1725
+ }
1726
+ openaiChunk(ci, delta, finish = null) {
1727
+ return `data: ${JSON.stringify({
1728
+ ...this.template ?? {
1729
+ object: "chat.completion.chunk"
1730
+ },
1731
+ choices: [
1732
+ {
1733
+ index: ci,
1734
+ delta,
1735
+ finish_reason: finish
1736
+ }
1737
+ ]
1738
+ })}
1739
+
1740
+ `;
1741
+ }
1742
+ openaiBlock(ci) {
1743
+ const msg = this.blocked?.message ?? "";
1744
+ this.done = true;
1745
+ return `${this.openaiChunk(ci, {
1746
+ content: `
1747
+
1748
+ ${msg}`
1749
+ })}${this.openaiChunk(ci, {}, "content_filter")}data: [DONE]
1750
+
1751
+ `;
1752
+ }
1753
+ async flushChoice(ci) {
1754
+ let out = "";
1755
+ for (const [key, ch] of this.channels) {
1756
+ if (!key.startsWith(`c:${ci}`) && !key.startsWith(`t:${ci}:`)) continue;
1757
+ const text = await this.release(key, ch, true);
1758
+ if (this.blocked) return out;
1759
+ if (!text) continue;
1760
+ out += key.startsWith("c:") ? this.openaiChunk(ci, {
1761
+ content: text
1762
+ }) : this.openaiChunk(ci, {
1763
+ tool_calls: [
1764
+ {
1765
+ index: Number(key.split(":")[2]),
1766
+ function: {
1767
+ arguments: text
1768
+ }
1769
+ }
1770
+ ]
1771
+ });
1772
+ }
1773
+ return out;
1774
+ }
1775
+ // ---- Anthropic Messages events -------------------------------------------------------------
1776
+ async anthropicFrame(f) {
1777
+ if (!f.data) return f.raw;
1778
+ let ev;
1779
+ try {
1780
+ ev = JSON.parse(f.data);
1781
+ } catch {
1782
+ return f.raw;
1783
+ }
1784
+ const type = ev.type ?? f.event;
1785
+ if (type === "content_block_start") {
1786
+ this.openBlocks.add(ev.index);
1787
+ this.maxBlock = Math.max(this.maxBlock, ev.index);
1788
+ return f.raw;
1789
+ }
1790
+ if (type === "content_block_delta") {
1791
+ const d = ev.delta ?? {};
1792
+ if (d.type === "text_delta" && typeof d.text === "string") {
1793
+ const r = await this.feed(`b:${ev.index}`, d.text, false);
1794
+ if (this.blocked) return this.anthropicBlock(ev.index);
1795
+ if (!r) return "";
1796
+ d.text = r;
1797
+ return sseEvent("content_block_delta", ev);
1798
+ }
1799
+ if (d.type === "input_json_delta" && typeof d.partial_json === "string") {
1800
+ const r = await this.feed(`b:${ev.index}`, d.partial_json, true);
1801
+ if (this.blocked) return this.anthropicBlock(ev.index);
1802
+ if (!r) return "";
1803
+ d.partial_json = r;
1804
+ return sseEvent("content_block_delta", ev);
1805
+ }
1806
+ return f.raw;
1807
+ }
1808
+ if (type === "content_block_stop") {
1809
+ const pre = await this.flushBlock(ev.index);
1810
+ if (this.blocked) return pre + this.anthropicBlock(ev.index);
1811
+ this.openBlocks.delete(ev.index);
1812
+ return pre + f.raw;
1813
+ }
1814
+ if (type === "message_delta" || type === "message_stop") {
1815
+ let pre = "";
1816
+ for (const i of [
1817
+ ...this.openBlocks
1818
+ ]) {
1819
+ pre += await this.flushBlock(i);
1820
+ if (this.blocked) return pre + this.anthropicBlock(i);
1821
+ }
1822
+ if (type === "message_stop") this.done = true;
1823
+ return pre + f.raw;
1824
+ }
1825
+ return f.raw;
1826
+ }
1827
+ async flushBlock(index) {
1828
+ const key = `b:${index}`;
1829
+ const ch = this.channels.get(key);
1830
+ if (!ch) return "";
1831
+ const text = await this.release(key, ch, true);
1832
+ if (this.blocked || !text) return "";
1833
+ return sseEvent("content_block_delta", {
1834
+ type: "content_block_delta",
1835
+ index,
1836
+ delta: ch.json ? {
1837
+ type: "input_json_delta",
1838
+ partial_json: text
1839
+ } : {
1840
+ type: "text_delta",
1841
+ text
1842
+ }
1843
+ });
1844
+ }
1845
+ anthropicBlock(index) {
1846
+ const msg = this.blocked?.message ?? "";
1847
+ this.done = true;
1848
+ let out = "";
1849
+ let i = index;
1850
+ const ch = this.channels.get(`b:${index}`);
1851
+ if (!this.openBlocks.has(index) || ch?.json) {
1852
+ if (this.openBlocks.has(index)) out += sseEvent("content_block_stop", {
1853
+ type: "content_block_stop",
1854
+ index
1855
+ });
1856
+ i = this.maxBlock + 1;
1857
+ out += sseEvent("content_block_start", {
1858
+ type: "content_block_start",
1859
+ index: i,
1860
+ content_block: {
1861
+ type: "text",
1862
+ text: ""
1863
+ }
1864
+ });
1865
+ }
1866
+ out += sseEvent("content_block_delta", {
1867
+ type: "content_block_delta",
1868
+ index: i,
1869
+ delta: {
1870
+ type: "text_delta",
1871
+ text: `
1872
+
1873
+ ${msg}`
1874
+ }
1875
+ });
1876
+ out += sseEvent("content_block_stop", {
1877
+ type: "content_block_stop",
1878
+ index: i
1879
+ });
1880
+ out += sseEvent("message_delta", {
1881
+ type: "message_delta",
1882
+ delta: {
1883
+ stop_reason: "refusal",
1884
+ stop_sequence: null
1885
+ },
1886
+ usage: {
1887
+ output_tokens: 0
1888
+ }
1889
+ });
1890
+ out += sseEvent("message_stop", {
1891
+ type: "message_stop"
1892
+ });
1893
+ return out;
1894
+ }
1895
+ // ---- windows -------------------------------------------------------------------------------
1896
+ async flushAll() {
1897
+ if (this.opts.api === "openai") {
1898
+ const choices = new Set([
1899
+ ...this.channels.keys()
1900
+ ].map((k) => Number(k.split(":")[1])));
1901
+ let out2 = "";
1902
+ for (const ci of choices) {
1903
+ out2 += await this.flushChoice(ci);
1904
+ if (this.blocked) return out2 + this.openaiBlock(ci);
1905
+ }
1906
+ return out2;
1907
+ }
1908
+ let out = "";
1909
+ for (const i of [
1910
+ ...this.openBlocks
1911
+ ]) {
1912
+ out += await this.flushBlock(i);
1913
+ if (this.blocked) return out + this.anthropicBlock(i);
1914
+ }
1915
+ return out;
1916
+ }
1917
+ async feed(key, text, json) {
1918
+ let ch = this.channels.get(key);
1919
+ if (!ch) {
1920
+ ch = new Channel(json);
1921
+ this.channels.set(key, ch);
1922
+ }
1923
+ ch.pending += text;
1924
+ if (ch.pending.length < this.window + this.holdback) return "";
1925
+ return this.release(key, ch, false);
1926
+ }
1927
+ /** Releases a window (everything when `all`), scanned and restored. */
1928
+ async release(key, ch, all) {
1929
+ if (!ch.pending) return "";
1930
+ let cut = all ? ch.pending.length : ch.pending.length - this.holdback;
1931
+ if (!all) {
1932
+ const spans = resolveOverlaps(this.opts.locate?.(ch.pending) ?? []);
1933
+ const straddling = spans.find((f) => f.start < cut && f.end > cut);
1934
+ if (straddling) cut = straddling.end < ch.pending.length ? straddling.end : straddling.start;
1935
+ const head = ch.pending.slice(0, cut);
1936
+ cut -= this.opts.vault.pendingPrefix(head);
1937
+ if (cut <= 0) return "";
1938
+ }
1939
+ let text = ch.pending.slice(0, cut);
1940
+ ch.pending = ch.pending.slice(cut);
1941
+ if (this.opts.scan) {
1942
+ const verdict = await this.opts.scan(text, key);
1943
+ if (verdict.block) {
1944
+ this.blocked = verdict.block;
1945
+ return "";
1946
+ }
1947
+ text = verdict.text;
1948
+ }
1949
+ return this.opts.restore ? this.opts.vault.restore(text, ch.json) : text;
1950
+ }
1951
+ };
1952
+ function parseFrame(raw) {
1953
+ let event;
1954
+ const data = [];
1955
+ for (const line of raw.split(/\r?\n/)) {
1956
+ if (line.startsWith("event:")) event = line.slice(6).trim();
1957
+ else if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /, ""));
1958
+ }
1959
+ return {
1960
+ event,
1961
+ data: data.join("\n"),
1962
+ raw
1963
+ };
1964
+ }
1965
+ __name(parseFrame, "parseFrame");
1966
+ function sseEvent(event, data) {
1967
+ return `event: ${event}
1968
+ data: ${JSON.stringify(data)}
1969
+
1970
+ `;
1971
+ }
1972
+ __name(sseEvent, "sseEvent");
1973
+
1974
+ // src/guardrails/agent.ts
1975
+ var InMemoryVaultStore = class {
1976
+ static {
1977
+ __name(this, "InMemoryVaultStore");
1978
+ }
1979
+ maxThreads;
1980
+ vaults = /* @__PURE__ */ new Map();
1981
+ constructor(maxThreads = 1e3) {
1982
+ this.maxThreads = maxThreads;
1983
+ }
1984
+ load(threadId) {
1985
+ const vault = this.vaults.get(threadId);
1986
+ if (vault !== void 0) {
1987
+ this.vaults.delete(threadId);
1988
+ this.vaults.set(threadId, vault);
1989
+ }
1990
+ return vault;
1991
+ }
1992
+ save(threadId, vault) {
1993
+ this.vaults.delete(threadId);
1994
+ this.vaults.set(threadId, vault);
1995
+ while (this.vaults.size > this.maxThreads) {
1996
+ const oldest = this.vaults.keys().next().value;
1997
+ if (oldest === void 0) break;
1998
+ this.vaults.delete(oldest);
1999
+ }
2000
+ }
2001
+ };
2002
+ var GuardrailBlockedError = class extends Error {
2003
+ static {
2004
+ __name(this, "GuardrailBlockedError");
2005
+ }
2006
+ stage;
2007
+ userMessage;
2008
+ rule;
2009
+ constructor(stage, userMessage, rule) {
2010
+ super(userMessage), this.stage = stage, this.userMessage = userMessage, this.rule = rule;
2011
+ this.name = "GuardrailBlockedError";
2012
+ }
2013
+ };
2014
+ var ALL_CONTENT = [
2015
+ "llm_request",
2016
+ "tool_args",
2017
+ "tool_result",
2018
+ "llm_response"
2019
+ ];
2020
+ var RESTORABLE_STAGES = [
2021
+ "llm_request",
2022
+ "tool_result"
2023
+ ];
2024
+ var PII_STAGES = [
2025
+ "llm_request",
2026
+ "tool_result",
2027
+ "llm_response"
2028
+ ];
2029
+ function shorthandRules(options) {
2030
+ const rules = [];
2031
+ if (options.pii !== void 0) {
2032
+ const pii = typeof options.pii === "string" ? {
2033
+ action: options.pii
2034
+ } : options.pii;
2035
+ rules.push({
2036
+ id: "pii",
2037
+ name: "PII",
2038
+ stages: pii.stages ?? PII_STAGES,
2039
+ detectors: [
2040
+ {
2041
+ kind: "pii",
2042
+ ...pii.types !== void 0 ? {
2043
+ types: pii.types
2044
+ } : {}
2045
+ }
2046
+ ],
2047
+ action: pii.action,
2048
+ options: {
2049
+ restore: pii.restore ?? true
2050
+ }
2051
+ });
2052
+ }
2053
+ if (options.secrets !== void 0) {
2054
+ const secrets = typeof options.secrets === "string" ? {
2055
+ action: options.secrets
2056
+ } : options.secrets;
2057
+ rules.push({
2058
+ id: "secrets",
2059
+ name: "Secrets",
2060
+ stages: secrets.stages ?? ALL_CONTENT,
2061
+ detectors: [
2062
+ {
2063
+ kind: "secrets",
2064
+ ...secrets.types !== void 0 ? {
2065
+ types: secrets.types
2066
+ } : {}
2067
+ }
2068
+ ],
2069
+ action: secrets.action,
2070
+ // A secret sent to a model is not handed back: the reader should not see it echoed either.
2071
+ options: {
2072
+ restore: false
2073
+ }
2074
+ });
2075
+ }
2076
+ if (options.injection !== void 0) {
2077
+ const injection = typeof options.injection === "string" ? {
2078
+ action: options.injection
2079
+ } : options.injection;
2080
+ rules.push({
2081
+ id: "injection",
2082
+ name: "Prompt injection",
2083
+ // The prompt (a jailbreak typed by the user) and tool results (indirect injection).
2084
+ stages: injection.stages ?? [
2085
+ "llm_request",
2086
+ "tool_result"
2087
+ ],
2088
+ detectors: [
2089
+ {
2090
+ kind: "injection",
2091
+ ...injection.threshold !== void 0 ? {
2092
+ threshold: injection.threshold
2093
+ } : {}
2094
+ }
2095
+ ],
2096
+ action: injection.action ?? "block",
2097
+ // What the user typed and what tools returned. Not the system prompt — the app's own text,
2098
+ // which legitimately carries role tags and imperatives — nor the model's earlier answers.
2099
+ match: {
2100
+ sources: [
2101
+ "user",
2102
+ "tool"
2103
+ ]
2104
+ }
2105
+ });
2106
+ }
2107
+ if (options.toolPoisoning !== void 0 && options.toolPoisoning !== false) {
2108
+ const poisoning = options.toolPoisoning === true ? {} : options.toolPoisoning;
2109
+ rules.push({
2110
+ id: "tool_poisoning",
2111
+ name: "Tool poisoning",
2112
+ stages: [
2113
+ "tool_description"
2114
+ ],
2115
+ detectors: [
2116
+ {
2117
+ kind: "tool_poisoning",
2118
+ ...poisoning.threshold !== void 0 ? {
2119
+ threshold: poisoning.threshold
2120
+ } : {}
2121
+ }
2122
+ ],
2123
+ action: poisoning.action ?? "block"
2124
+ });
2125
+ }
2126
+ return rules;
2127
+ }
2128
+ __name(shorthandRules, "shorthandRules");
2129
+ var isRefusal = /* @__PURE__ */ __name((action) => action === "block" || action === "approve", "isRefusal");
2130
+ function sourceOf2(role) {
2131
+ return role === "system" ? "system" : role === "assistant" ? "assistant" : "user";
2132
+ }
2133
+ __name(sourceOf2, "sourceOf");
2134
+ function turnStart(messages) {
2135
+ for (let i = messages.length - 1; i >= 0; i--) {
2136
+ const message = messages[i];
2137
+ if (message?.role === "user" && message.content) return i;
2138
+ }
2139
+ return 0;
2140
+ }
2141
+ __name(turnStart, "turnStart");
2142
+ function cloneJson(value) {
2143
+ return value === void 0 ? value : structuredClone(value);
2144
+ }
2145
+ __name(cloneJson, "cloneJson");
2146
+ function writeBack(slots, result) {
2147
+ if (!result.changed) return;
2148
+ slots.forEach((slot, i) => {
2149
+ const text = result.segments[i];
2150
+ if (text !== void 0 && text !== slot.segment.text) slot.set(text);
2151
+ });
2152
+ }
2153
+ __name(writeBack, "writeBack");
2154
+ var Guardrails = class {
2155
+ static {
2156
+ __name(this, "Guardrails");
2157
+ }
2158
+ options;
2159
+ /** Scans every prompt; redacts in place, withholds tool results, throws on a blocked prompt. */
2160
+ input;
2161
+ /** Scans every answer and the tool calls it asks for; redacts, restores placeholders, or rejects. */
2162
+ output;
2163
+ shorthand;
2164
+ vaults;
2165
+ /** Per thread: hits already reported this turn, so re-scans of the same content stay quiet. */
2166
+ reported = /* @__PURE__ */ new Map();
2167
+ constructor(options = {}) {
2168
+ this.options = options;
2169
+ this.shorthand = shorthandRules(options);
2170
+ this.vaults = options.vaults ?? new InMemoryVaultStore();
2171
+ const name = options.name ?? "guardrails";
2172
+ this.input = {
2173
+ name,
2174
+ process: /* @__PURE__ */ __name((prompt, ctx) => this.processInput(prompt, ctx), "process")
2175
+ };
2176
+ const incremental = options.incremental === false ? void 0 : {
2177
+ lookbackChars: options.incremental?.lookbackChars ?? 256
2178
+ };
2179
+ this.output = {
2180
+ name,
2181
+ ...incremental !== void 0 ? {
2182
+ incremental
2183
+ } : {},
2184
+ process: /* @__PURE__ */ __name((answer, ctx) => this.processOutput(answer, ctx), "process")
2185
+ };
2186
+ }
2187
+ /** Every rule in force for `ctx`: the shorthands, then {@link GuardrailsOptions.rules}. */
2188
+ async rulesFor(ctx) {
2189
+ const extra = this.options.rules;
2190
+ const resolved = typeof extra === "function" ? await extra(ctx) : extra ?? [];
2191
+ return [
2192
+ ...this.shorthand,
2193
+ ...resolved
2194
+ ];
2195
+ }
2196
+ /**
2197
+ * Scans arbitrary segments at one stage with the configured rules, reporting through `onEvent`.
2198
+ * What the processors use; also the entry point for traffic that never meets the loop.
2199
+ */
2200
+ async scan(ctx, segments, vault = new Vault()) {
2201
+ const rules = await this.rulesFor(ctx);
2202
+ const result = await scan(rules, ctx, segments, vault, {
2203
+ ...this.options.messages !== void 0 ? {
2204
+ messages: this.options.messages
2205
+ } : {},
2206
+ // A tool result is the app's own data: a value redacted out of it is handed back to the
2207
+ // reader and to the next tool call like one the user typed. `restore: false` opts a rule out.
2208
+ restorableStages: RESTORABLE_STAGES
2209
+ });
2210
+ await this.report(ctx, result);
2211
+ return result;
2212
+ }
2213
+ /**
2214
+ * Screens one tool definition (name, title, description, and every description in its input
2215
+ * schema) for hidden instructions. `allowed: false` means a `block`/`approve` rule fired — leave
2216
+ * the tool out of the catalog. Shaped to be handed straight to an MCP importer's screen hook.
2217
+ */
2218
+ async screenTool(tool) {
2219
+ const ctx = {
2220
+ stage: "tool_description",
2221
+ tool: tool.name,
2222
+ ...tool.server !== void 0 ? {
2223
+ server: tool.server
2224
+ } : {}
2225
+ };
2226
+ const result = await this.scan(ctx, [
2227
+ {
2228
+ text: toolText(tool)
2229
+ }
2230
+ ]);
2231
+ if (isRefusal(result.action)) {
2232
+ const categories = [
2233
+ ...new Set(result.hits.map((h) => h.category))
2234
+ ].join(", ");
2235
+ return {
2236
+ allowed: false,
2237
+ reason: `${result.decisive?.message ?? "quarantined by guardrails"} (${categories})`
2238
+ };
2239
+ }
2240
+ return {
2241
+ allowed: true
2242
+ };
2243
+ }
2244
+ /**
2245
+ * Wraps a tool handler so its arguments are guarded where the loop cannot rewrite them: the
2246
+ * thread's placeholders are restored (the model only ever saw `[EMAIL_1]`, the tool needs the
2247
+ * address), then the `tool_args` rules run — `redact` rewrites what the tool receives, `block`
2248
+ * throws a {@link GuardrailBlockedError}, which the model reads as the tool's failure.
2249
+ */
2250
+ wrapTool(toolName, handler) {
2251
+ return {
2252
+ execute: /* @__PURE__ */ __name(async (input, ctx) => {
2253
+ const vault = await this.vaults.load(ctx.threadId) ?? new Vault();
2254
+ const box = {
2255
+ value: cloneJson(input)
2256
+ };
2257
+ const slots = jsonSlots(box.value, "assistant", true, (text) => {
2258
+ box.value = text;
2259
+ });
2260
+ for (const slot of slots) slot.set(vault.restore(slot.segment.text));
2261
+ const guardCtx = {
2262
+ stage: "tool_args",
2263
+ tool: toolName,
2264
+ threadId: ctx.threadId,
2265
+ actor: ctx.actor,
2266
+ ...ctx.agentName !== void 0 ? {
2267
+ agentName: ctx.agentName
2268
+ } : {}
2269
+ };
2270
+ const restoredSlots = jsonSlots(box.value, "assistant", true, (text) => {
2271
+ box.value = text;
2272
+ });
2273
+ const result = await this.scan(guardCtx, restoredSlots.map((s) => s.segment), vault);
2274
+ if (isRefusal(result.action)) {
2275
+ throw new GuardrailBlockedError("tool_args", result.decisive?.message ?? "blocked", result.decisive);
2276
+ }
2277
+ writeBack(restoredSlots, result);
2278
+ return handler.execute(box.value, ctx);
2279
+ }, "execute"),
2280
+ ...handler.isEnabled !== void 0 ? {
2281
+ isEnabled: /* @__PURE__ */ __name(() => handler.isEnabled?.call(handler) ?? true, "isEnabled")
2282
+ } : {},
2283
+ ...handler.canUse !== void 0 ? {
2284
+ canUse: /* @__PURE__ */ __name((actor) => handler.canUse?.call(handler, actor) ?? true, "canUse")
2285
+ } : {}
2286
+ };
2287
+ }
2288
+ contextOf(stage, ctx, tool) {
2289
+ return {
2290
+ stage,
2291
+ threadId: ctx.threadId,
2292
+ actor: ctx.actor,
2293
+ step: ctx.step,
2294
+ ...ctx.agentName !== void 0 ? {
2295
+ agentName: ctx.agentName
2296
+ } : {},
2297
+ ...tool !== void 0 ? {
2298
+ tool
2299
+ } : {}
2300
+ };
2301
+ }
2302
+ async processInput(prompt, pctx) {
2303
+ if (pctx.step === 0) {
2304
+ this.reported.delete(pctx.threadId);
2305
+ }
2306
+ const vault = await this.vaults.load(pctx.threadId) ?? new Vault();
2307
+ const out = {
2308
+ system: prompt.system,
2309
+ messages: cloneJson(prompt.messages)
2310
+ };
2311
+ const from = turnStart(out.messages);
2312
+ const slots = [];
2313
+ if (out.system) {
2314
+ slots.push({
2315
+ segment: {
2316
+ text: out.system,
2317
+ source: "system",
2318
+ fresh: true
2319
+ },
2320
+ set: /* @__PURE__ */ __name((text) => {
2321
+ out.system = text;
2322
+ }, "set")
2323
+ });
2324
+ }
2325
+ out.messages.forEach((message, i) => {
2326
+ const fresh = i >= from;
2327
+ if (message.content) {
2328
+ slots.push({
2329
+ segment: {
2330
+ text: message.content,
2331
+ source: sourceOf2(message.role),
2332
+ fresh
2333
+ },
2334
+ set: /* @__PURE__ */ __name((text) => {
2335
+ message.content = text;
2336
+ }, "set")
2337
+ });
2338
+ }
2339
+ for (const call of message.toolCalls ?? []) {
2340
+ slots.push(...jsonSlots(call.input, "assistant", fresh, (text) => {
2341
+ call.input = text;
2342
+ }));
2343
+ }
2344
+ });
2345
+ const request = await this.scan(this.contextOf("llm_request", pctx), slots.map((s) => s.segment), vault);
2346
+ if (isRefusal(request.action)) {
2347
+ await this.vaults.save(pctx.threadId, vault);
2348
+ throw new GuardrailBlockedError("llm_request", request.decisive?.message ?? "blocked", request.decisive);
2349
+ }
2350
+ writeBack(slots, request);
2351
+ for (const [i, message] of out.messages.entries()) {
2352
+ for (const result of message.toolResults ?? []) {
2353
+ await this.guardToolResult(result, i >= from, pctx, vault);
2354
+ }
2355
+ }
2356
+ await this.vaults.save(pctx.threadId, vault);
2357
+ return out;
2358
+ }
2359
+ async guardToolResult(result, fresh, pctx, vault) {
2360
+ const slots = jsonSlots(result.output, "tool", fresh, (text) => {
2361
+ result.output = text;
2362
+ });
2363
+ if (result.error) {
2364
+ slots.push({
2365
+ segment: {
2366
+ text: result.error,
2367
+ source: "tool",
2368
+ fresh
2369
+ },
2370
+ set: /* @__PURE__ */ __name((text) => {
2371
+ result.error = text;
2372
+ }, "set")
2373
+ });
2374
+ }
2375
+ if (slots.length === 0) return;
2376
+ const scanned = await this.scan(this.contextOf("tool_result", pctx, result.name), slots.map((s) => s.segment), vault);
2377
+ if (isRefusal(scanned.action)) {
2378
+ result.output = scanned.decisive?.message ?? "withheld";
2379
+ if (result.error !== void 0) result.error = result.output;
2380
+ return;
2381
+ }
2382
+ writeBack(slots, scanned);
2383
+ }
2384
+ async processOutput(answer, pctx) {
2385
+ for (const call of answer.toolCalls) {
2386
+ const slots = jsonSlots(cloneJson(call.input), "assistant", true);
2387
+ if (slots.length === 0) continue;
2388
+ const args = await this.scan(this.contextOf("tool_args", pctx, call.name), slots.map((s) => s.segment));
2389
+ if (isRefusal(args.action)) {
2390
+ return {
2391
+ action: "reject",
2392
+ reason: args.decisive?.message ?? "blocked"
2393
+ };
2394
+ }
2395
+ }
2396
+ if (!answer.text) return {
2397
+ action: "pass"
2398
+ };
2399
+ const response = await this.scan(this.contextOf("llm_response", pctx), [
2400
+ {
2401
+ text: answer.text,
2402
+ source: "assistant"
2403
+ }
2404
+ ], new Vault());
2405
+ if (isRefusal(response.action)) {
2406
+ return {
2407
+ action: "reject",
2408
+ reason: response.decisive?.message ?? "blocked"
2409
+ };
2410
+ }
2411
+ const vault = await this.vaults.load(pctx.threadId);
2412
+ const redacted = response.segments[0] ?? answer.text;
2413
+ const text = vault !== void 0 ? vault.restore(redacted) : redacted;
2414
+ return text === answer.text ? {
2415
+ action: "pass"
2416
+ } : {
2417
+ action: "replace",
2418
+ text
2419
+ };
2420
+ }
2421
+ async report(ctx, result) {
2422
+ const onEvent = this.options.onEvent;
2423
+ if (onEvent === void 0 || result.hits.length === 0) return;
2424
+ const seen = ctx.threadId !== void 0 ? this.reported.get(ctx.threadId) ?? this.reported.set(ctx.threadId, /* @__PURE__ */ new Set()).get(ctx.threadId) : void 0;
2425
+ const fingerprint = this.options.fingerprint;
2426
+ const hits = [];
2427
+ for (const { values, ...hit } of result.hits) {
2428
+ if (!hit.fresh) continue;
2429
+ const key = `${ctx.stage}\0${ctx.tool ?? ""}\0${hit.ruleId}\0${hit.category}\0${values.join("")}`;
2430
+ if (seen?.has(key)) continue;
2431
+ seen?.add(key);
2432
+ hits.push({
2433
+ ...hit,
2434
+ fingerprints: fingerprint !== void 0 ? values.map(fingerprint) : []
2435
+ });
2436
+ }
2437
+ if (hits.length === 0) return;
2438
+ if (this.reported.size > 1e3) {
2439
+ const oldest = this.reported.keys().next().value;
2440
+ if (oldest !== void 0) this.reported.delete(oldest);
2441
+ }
2442
+ try {
2443
+ await onEvent({
2444
+ stage: result.stage,
2445
+ action: result.action,
2446
+ ...result.decisive !== void 0 ? {
2447
+ decisive: result.decisive
2448
+ } : {},
2449
+ hits,
2450
+ redactions: result.redactions,
2451
+ latencyMs: result.latencyMs,
2452
+ context: ctx
2453
+ });
2454
+ } catch {
2455
+ }
2456
+ }
2457
+ };
2458
+ function createGuardrails(options = {}) {
2459
+ return new Guardrails(options);
2460
+ }
2461
+ __name(createGuardrails, "createGuardrails");
2462
+ function screenToolDefinition(tool, threshold = 0.5) {
2463
+ return scoreToolText(toolText(tool), threshold);
2464
+ }
2465
+ __name(screenToolDefinition, "screenToolDefinition");
2466
+ // Annotate the CommonJS export names for ESM import in node:
2467
+ 0 && (module.exports = {
2468
+ ACTION_SEVERITY,
2469
+ DEFAULT_APPROVAL_MESSAGE,
2470
+ DEFAULT_MESSAGES,
2471
+ GUARDRAIL_STAGES,
2472
+ GuardrailBlockedError,
2473
+ Guardrails,
2474
+ INJECTION_REPLACEMENT,
2475
+ INJECTION_SIGNALS,
2476
+ InMemoryVaultStore,
2477
+ PII_TYPES,
2478
+ SECRET_TYPES,
2479
+ StreamGuard,
2480
+ Vault,
2481
+ applyRedactions,
2482
+ cardBrand,
2483
+ cnpjValid,
2484
+ compileGuardPattern,
2485
+ cpfValid,
2486
+ createGuardrails,
2487
+ decodeTagChars,
2488
+ detectKeywords,
2489
+ detectPii,
2490
+ detectRegex,
2491
+ detectSecrets,
2492
+ entropy,
2493
+ fold,
2494
+ globToRegExp,
2495
+ ibanValid,
2496
+ isIPv4,
2497
+ jsonSlots,
2498
+ labelFor,
2499
+ luhnValid,
2500
+ matchesAny,
2501
+ orderRules,
2502
+ phoneValid,
2503
+ refuseResponse,
2504
+ requestSlots,
2505
+ resolveOverlaps,
2506
+ responseSlots,
2507
+ ruleApplies,
2508
+ runDetector,
2509
+ scan,
2510
+ scoreInjection,
2511
+ scoreToolText,
2512
+ screenToolDefinition,
2513
+ shorthandRules,
2514
+ spanLocator,
2515
+ ssnValid,
2516
+ toolResultSlots,
2517
+ toolText
2518
+ });
2519
+ //# sourceMappingURL=index.cjs.map