@cendor/guardrails 0.1.0 → 0.2.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/dist/policy.js ADDED
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Config-as-data — build a guardrail list from a versioned JSON/YAML policy. The TS port of
3
+ * `cendor.guardrails.policy`.
4
+ *
5
+ * `loadPolicy` reads a policy **document or text** (there is no filesystem in an all-runtime package —
6
+ * read the file yourself and pass the text/object) and builds the `Guardrail[]` you hand to
7
+ * `Agent({ guardrails })` / `install()`. The point is **evidence**: the document's content hash and
8
+ * declared version are stamped into every decision's `metadata` (`policy_hash` / `policy_version`), so
9
+ * the audit chain proves *which* policy was active. Only the deterministic built-ins are constructible
10
+ * from data. JSON is built in; for YAML pass a `parse` (e.g. `yaml`'s `parse`).
11
+ *
12
+ * ```ts
13
+ * import { loadPolicy } from '@cendor/guardrails';
14
+ * const policy = loadPolicy(jsonText); // or loadPolicy(text, { parse: YAML.parse })
15
+ * agent = new Agent({ guardrails: policy });
16
+ * policy.policyHash; // "sha256:…" policy.policyVersion; // "2026-07-09"
17
+ * ```
18
+ */
19
+ import { normalizeStages } from './decision.js';
20
+ import { jsonSchema, keywordDeny, lengthBounds, regexRule, urlAllowlist, urlDeny, } from './rules.js';
21
+ const arr = (v) => (Array.isArray(v) ? v.map(String) : []);
22
+ const bool = (v) => (v === undefined ? undefined : Boolean(v));
23
+ /** The rules constructible from data alone (deterministic; no callable / client / embedding fn). */
24
+ const POLICY_RULES = {
25
+ keyword_deny: (a, c) => keywordDeny(arr(a.words), {
26
+ stage: c.stage,
27
+ action: c.action,
28
+ name: c.name,
29
+ ignoreCase: bool(a.ignore_case ?? a.ignoreCase),
30
+ }),
31
+ regex_rule: (a, c) => regexRule(String(a.pattern ?? ''), {
32
+ stage: c.stage,
33
+ action: c.action,
34
+ name: c.name,
35
+ replacement: a.replacement === undefined ? undefined : String(a.replacement),
36
+ }),
37
+ url_allowlist: (a, c) => urlAllowlist(arr(a.domains), { stage: c.stage, action: c.action, name: c.name }),
38
+ url_deny: (a, c) => urlDeny(arr(a.domains), { stage: c.stage, action: c.action, name: c.name }),
39
+ length_bounds: (a, c) => lengthBounds({
40
+ stage: c.stage,
41
+ action: c.action,
42
+ name: c.name,
43
+ maxChars: (a.max_chars ?? a.maxChars),
44
+ maxTokens: (a.max_tokens ?? a.maxTokens),
45
+ model: a.model,
46
+ }),
47
+ json_schema: (a, c) => jsonSchema((a.schema ?? {}), {
48
+ stage: c.stage,
49
+ action: c.action,
50
+ name: c.name,
51
+ }),
52
+ };
53
+ /** The rule names a policy document may use (deterministic built-ins only). */
54
+ export const POLICY_RULE_NAMES = Object.keys(POLICY_RULES);
55
+ function canonical(v) {
56
+ if (v === null || typeof v !== 'object')
57
+ return JSON.stringify(v) ?? 'null';
58
+ if (Array.isArray(v))
59
+ return `[${v.map(canonical).join(',')}]`;
60
+ const obj = v;
61
+ const keys = Object.keys(obj).sort();
62
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(',')}}`;
63
+ }
64
+ function coerceStage(stage) {
65
+ if (Array.isArray(stage))
66
+ return stage.map(String);
67
+ if (typeof stage === 'string')
68
+ return stage;
69
+ return undefined;
70
+ }
71
+ /**
72
+ * Build a {@link LoadedPolicy} from a JSON/YAML document (a parsed object, or text parsed with
73
+ * `JSON.parse` / your `opts.parse`). Every guardrail is stamped with `policy_hash` / `policy_version`
74
+ * in its metadata, so each decision records which policy was active.
75
+ *
76
+ * @throws if the document is malformed or names an unknown / non-declarative rule.
77
+ */
78
+ export function loadPolicy(source, opts = {}) {
79
+ const config = typeof source === 'string' ? (opts.parse ?? JSON.parse)(source) : source;
80
+ if (config === null || typeof config !== 'object' || Array.isArray(config)) {
81
+ throw new Error('policy document must be an object');
82
+ }
83
+ const doc = config;
84
+ const entries = doc.guardrails;
85
+ if (!Array.isArray(entries))
86
+ throw new Error("policy document must have a 'guardrails' array");
87
+ const policyHash = `sha256:${sha256Hex(canonical(doc))}`;
88
+ const policyVersion = String(doc.version ?? '');
89
+ const stamp = { policy_hash: policyHash, policy_version: policyVersion };
90
+ const guardrails = entries.map((entry, i) => {
91
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
92
+ throw new Error(`guardrails[${i}] must be an object`);
93
+ }
94
+ const e = entry;
95
+ const rule = String(e.rule);
96
+ const build = POLICY_RULES[rule];
97
+ if (build === undefined) {
98
+ throw new Error(`guardrails[${i}]: unknown or non-declarative rule ${JSON.stringify(rule)}; policy documents support ${POLICY_RULE_NAMES.join(', ')} (rules needing a callable or a client are wired in code)`);
99
+ }
100
+ const common = {
101
+ stage: coerceStage(e.stage),
102
+ action: e.action,
103
+ name: e.name,
104
+ };
105
+ let g;
106
+ try {
107
+ g = build(e.args ?? {}, common);
108
+ }
109
+ catch (err) {
110
+ throw new Error(`guardrails[${i}] (${rule}): bad arguments — ${err.message}`);
111
+ }
112
+ // touch normalizeStages defensively so an explicit bad stage array fails here with the index
113
+ if (common.stage !== undefined)
114
+ normalizeStages(common.stage);
115
+ g.metadata = { ...(g.metadata ?? {}), ...stamp };
116
+ return g;
117
+ });
118
+ const policy = guardrails;
119
+ policy.policyHash = policyHash;
120
+ policy.policyVersion = policyVersion;
121
+ return policy;
122
+ }
123
+ // --------------------------------------------------------------------------- SHA-256 (all-runtime)
124
+ //
125
+ // A compact, dependency-free SHA-256 so `loadPolicy` stays SYNC and all-runtime (no node:crypto, no
126
+ // async crypto.subtle). Verified against the standard "" / "abc" vectors in the tests. The hash is
127
+ // canonical-per-language (over TS's canonical JSON); it is not promised byte-identical to Python's.
128
+ const K = new Uint32Array([
129
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
130
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
131
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
132
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
133
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
134
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
135
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
136
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
137
+ ]);
138
+ function rotr(x, n) {
139
+ return (x >>> n) | (x << (32 - n));
140
+ }
141
+ function sha256Hex(input) {
142
+ const bytes = new TextEncoder().encode(input);
143
+ const l = bytes.length;
144
+ const withOne = l + 1;
145
+ const pad = (56 - (withOne % 64) + 64) % 64;
146
+ const total = withOne + pad + 8;
147
+ const msg = new Uint8Array(total);
148
+ msg.set(bytes);
149
+ msg[l] = 0x80;
150
+ const dv = new DataView(msg.buffer);
151
+ // 64-bit big-endian bit length; inputs here are well under 2^32 bits, so the high word is 0.
152
+ dv.setUint32(total - 4, (l * 8) >>> 0, false);
153
+ let h0 = 0x6a09e667;
154
+ let h1 = 0xbb67ae85;
155
+ let h2 = 0x3c6ef372;
156
+ let h3 = 0xa54ff53a;
157
+ let h4 = 0x510e527f;
158
+ let h5 = 0x9b05688c;
159
+ let h6 = 0x1f83d9ab;
160
+ let h7 = 0x5be0cd19;
161
+ const w = new Uint32Array(64);
162
+ for (let i = 0; i < total; i += 64) {
163
+ for (let t = 0; t < 16; t++)
164
+ w[t] = dv.getUint32(i + t * 4, false);
165
+ for (let t = 16; t < 64; t++) {
166
+ const w15 = w[t - 15];
167
+ const w2 = w[t - 2];
168
+ const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);
169
+ const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);
170
+ w[t] = (w[t - 16] + s0 + w[t - 7] + s1) >>> 0;
171
+ }
172
+ let a = h0;
173
+ let b = h1;
174
+ let c = h2;
175
+ let d = h3;
176
+ let e = h4;
177
+ let f = h5;
178
+ let g = h6;
179
+ let h = h7;
180
+ for (let t = 0; t < 64; t++) {
181
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
182
+ const ch = (e & f) ^ (~e & g);
183
+ const t1 = (h + s1 + ch + K[t] + w[t]) >>> 0;
184
+ const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
185
+ const maj = (a & b) ^ (a & c) ^ (b & c);
186
+ const t2 = (s0 + maj) >>> 0;
187
+ h = g;
188
+ g = f;
189
+ f = e;
190
+ e = (d + t1) >>> 0;
191
+ d = c;
192
+ c = b;
193
+ b = a;
194
+ a = (t1 + t2) >>> 0;
195
+ }
196
+ h0 = (h0 + a) >>> 0;
197
+ h1 = (h1 + b) >>> 0;
198
+ h2 = (h2 + c) >>> 0;
199
+ h3 = (h3 + d) >>> 0;
200
+ h4 = (h4 + e) >>> 0;
201
+ h5 = (h5 + f) >>> 0;
202
+ h6 = (h6 + g) >>> 0;
203
+ h7 = (h7 + h) >>> 0;
204
+ }
205
+ return [h0, h1, h2, h3, h4, h5, h6, h7]
206
+ .map((x) => (x >>> 0).toString(16).padStart(8, '0'))
207
+ .join('');
208
+ }
209
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Red-team evaluation — measure a guardrail's trip rate against a labeled corpus. The TS port of
3
+ * `cendor.guardrails.redteam`.
4
+ *
5
+ * The honest path to *any* detection number: run **your** guardrails over a labeled corpus and
6
+ * publish the per-category trip rate + false-positive rate, naming the corpus. cendor **vends no
7
+ * attack data** — `loadCorpus` parses records / text **you** supplied (public sets like AdvBench /
8
+ * JailbreakBench / HackAPrompt are referenced in the docs; you fetch them under their own licenses).
9
+ * There is no `node:fs` here — read the file yourself and pass the text or the parsed array. The
10
+ * report is a measurement tool, not a claim.
11
+ *
12
+ * Deterministic guardrails make the run offline + reproducible; a run with an `llmJudge` / hosted
13
+ * rail should be cassette-recorded so CI stays offline. Imports only `./decision` + the engine.
14
+ */
15
+ import { type Guardrail } from './decision.js';
16
+ export declare const ATTACK = "attack";
17
+ export declare const BENIGN = "benign";
18
+ /** One labeled probe. `label` is `"attack"` (should trip) or `"benign"` (should pass). */
19
+ export interface AttackCase {
20
+ text: string;
21
+ label?: string;
22
+ category?: string;
23
+ id?: string;
24
+ }
25
+ /** Counts + rates from a red-team run — no shipped claim; it describes the corpus you name. */
26
+ export declare class RedTeamReport {
27
+ total: number;
28
+ attacks: number;
29
+ benign: number;
30
+ caught: number;
31
+ falsePositives: number;
32
+ /** category → `[attacks, caught]`. */
33
+ byCategory: Record<string, [number, number]>;
34
+ /** Recall on attack cases (caught / attacks); `0` when there are no attacks. */
35
+ get tripRate(): number;
36
+ /** Fraction of benign cases that tripped; `0` when there are no benign cases. */
37
+ get falsePositiveRate(): number;
38
+ /** A one-line, corpus-agnostic summary (safe to log — no case text). */
39
+ summary(): string;
40
+ }
41
+ export interface LoadCorpusOptions {
42
+ /** For a text `source`: `"jsonl"` / `"json"` / `"csv"` (default `"jsonl"`). Ignored for an array. */
43
+ format?: 'jsonl' | 'json' | 'csv';
44
+ }
45
+ /**
46
+ * Build a labeled corpus from an **array** of records (already parsed) or a **text** blob you read
47
+ * yourself — `jsonl` (one object per line), `json` (an array), or `csv` (a header row). Each record
48
+ * needs a `text` field; `label` (default `"attack"`) / `category` / `id` are optional. No `node:fs`.
49
+ */
50
+ export declare function loadCorpus(source: readonly unknown[] | string, opts?: LoadCorpusOptions): AttackCase[];
51
+ /**
52
+ * Run `guardrails` over each case at `stage` and tally trip rate + false positives. A case trips
53
+ * when any guardrail blocks/redacts/flags it (a `block` throws `GuardrailTripped` — counted as a
54
+ * trip, not an error). Sync only — for an `async` check use {@link runRedteamAsync}.
55
+ */
56
+ export declare function runRedteam(guardrails: readonly Guardrail[], cases: Iterable<AttackCase>, opts?: {
57
+ stage?: string;
58
+ }): RedTeamReport;
59
+ /** Async counterpart of {@link runRedteam} — awaits `async` checks (an `llmJudge` / hosted rail). */
60
+ export declare function runRedteamAsync(guardrails: readonly Guardrail[], cases: Iterable<AttackCase>, opts?: {
61
+ stage?: string;
62
+ }): Promise<RedTeamReport>;
63
+ //# sourceMappingURL=redteam.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redteam.d.ts","sourceRoot":"","sources":["../src/redteam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,eAAe,CAAC;AAGjE,eAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,eAAO,MAAM,MAAM,WAAW,CAAC;AAE/B,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,+FAA+F;AAC/F,qBAAa,aAAa;IACxB,KAAK,SAAK;IACV,OAAO,SAAK;IACZ,MAAM,SAAK;IACX,MAAM,SAAK;IACX,cAAc,SAAK;IACnB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAM;IAElD,gFAAgF;IAChF,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,iFAAiF;IACjF,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED,wEAAwE;IACxE,OAAO,IAAI,MAAM;CAOlB;AAgCD,MAAM,WAAW,iBAAiB;IAChC,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EACnC,IAAI,GAAE,iBAAsB,GAC3B,UAAU,EAAE,CAiBd;AAiBD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5B,aAAa,CAcf;AAED,qGAAqG;AACrG,wBAAsB,eAAe,CACnC,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5B,OAAO,CAAC,aAAa,CAAC,CAcxB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Red-team evaluation — measure a guardrail's trip rate against a labeled corpus. The TS port of
3
+ * `cendor.guardrails.redteam`.
4
+ *
5
+ * The honest path to *any* detection number: run **your** guardrails over a labeled corpus and
6
+ * publish the per-category trip rate + false-positive rate, naming the corpus. cendor **vends no
7
+ * attack data** — `loadCorpus` parses records / text **you** supplied (public sets like AdvBench /
8
+ * JailbreakBench / HackAPrompt are referenced in the docs; you fetch them under their own licenses).
9
+ * There is no `node:fs` here — read the file yourself and pass the text or the parsed array. The
10
+ * report is a measurement tool, not a claim.
11
+ *
12
+ * Deterministic guardrails make the run offline + reproducible; a run with an `llmJudge` / hosted
13
+ * rail should be cassette-recorded so CI stays offline. Imports only `./decision` + the engine.
14
+ */
15
+ import { GuardrailTripped } from './decision.js';
16
+ import { apply, applyAsync } from './index.js';
17
+ export const ATTACK = 'attack';
18
+ export const BENIGN = 'benign';
19
+ /** Counts + rates from a red-team run — no shipped claim; it describes the corpus you name. */
20
+ export class RedTeamReport {
21
+ total = 0;
22
+ attacks = 0;
23
+ benign = 0;
24
+ caught = 0; // attack cases that tripped (true positives)
25
+ falsePositives = 0; // benign cases that tripped
26
+ /** category → `[attacks, caught]`. */
27
+ byCategory = {};
28
+ /** Recall on attack cases (caught / attacks); `0` when there are no attacks. */
29
+ get tripRate() {
30
+ return this.attacks ? this.caught / this.attacks : 0;
31
+ }
32
+ /** Fraction of benign cases that tripped; `0` when there are no benign cases. */
33
+ get falsePositiveRate() {
34
+ return this.benign ? this.falsePositives / this.benign : 0;
35
+ }
36
+ /** A one-line, corpus-agnostic summary (safe to log — no case text). */
37
+ summary() {
38
+ const pct = (x) => `${(x * 100).toFixed(1)}%`;
39
+ return (`${this.total} cases: trip rate ${pct(this.tripRate)} (${this.caught}/${this.attacks} attacks), ` +
40
+ `false-positive rate ${pct(this.falsePositiveRate)} (${this.falsePositives}/${this.benign} benign)`);
41
+ }
42
+ }
43
+ function toCase(row) {
44
+ if (row === null || typeof row !== 'object') {
45
+ throw new Error(`each corpus record must be an object, got ${row === null ? 'null' : typeof row}`);
46
+ }
47
+ const r = row;
48
+ if (!('text' in r))
49
+ throw new Error("each corpus record needs a 'text' field");
50
+ return {
51
+ text: String(r.text),
52
+ label: r.label === undefined ? ATTACK : String(r.label),
53
+ category: r.category === undefined ? '' : String(r.category),
54
+ id: r.id === undefined ? '' : String(r.id),
55
+ };
56
+ }
57
+ function parseCsv(text) {
58
+ const lines = text.split(/\r?\n/).filter((l) => l.trim());
59
+ if (lines.length === 0)
60
+ return [];
61
+ const header = lines[0].split(',').map((h) => h.trim());
62
+ return lines.slice(1).map((line) => {
63
+ const cols = line.split(',');
64
+ const row = {};
65
+ header.forEach((h, i) => {
66
+ row[h] = (cols[i] ?? '').trim();
67
+ });
68
+ return row;
69
+ });
70
+ }
71
+ /**
72
+ * Build a labeled corpus from an **array** of records (already parsed) or a **text** blob you read
73
+ * yourself — `jsonl` (one object per line), `json` (an array), or `csv` (a header row). Each record
74
+ * needs a `text` field; `label` (default `"attack"`) / `category` / `id` are optional. No `node:fs`.
75
+ */
76
+ export function loadCorpus(source, opts = {}) {
77
+ if (Array.isArray(source))
78
+ return source.map(toCase);
79
+ const text = source;
80
+ const fmt = opts.format ?? 'jsonl';
81
+ let rows;
82
+ if (fmt === 'jsonl') {
83
+ rows = text
84
+ .split(/\r?\n/)
85
+ .filter((l) => l.trim())
86
+ .map((l) => JSON.parse(l));
87
+ }
88
+ else if (fmt === 'json') {
89
+ const data = JSON.parse(text);
90
+ rows = Array.isArray(data) ? data : [data];
91
+ }
92
+ else {
93
+ rows = parseCsv(text);
94
+ }
95
+ return rows.map(toCase);
96
+ }
97
+ function tally(report, c, tripped) {
98
+ report.total += 1;
99
+ const label = c.label ?? ATTACK;
100
+ if (label === ATTACK) {
101
+ report.attacks += 1;
102
+ if (tripped)
103
+ report.caught += 1;
104
+ const cat = c.category ?? '';
105
+ const [a, caught] = report.byCategory[cat] ?? [0, 0];
106
+ report.byCategory[cat] = [a + 1, caught + (tripped ? 1 : 0)];
107
+ }
108
+ else if (label === BENIGN) {
109
+ report.benign += 1;
110
+ if (tripped)
111
+ report.falsePositives += 1;
112
+ }
113
+ }
114
+ /**
115
+ * Run `guardrails` over each case at `stage` and tally trip rate + false positives. A case trips
116
+ * when any guardrail blocks/redacts/flags it (a `block` throws `GuardrailTripped` — counted as a
117
+ * trip, not an error). Sync only — for an `async` check use {@link runRedteamAsync}.
118
+ */
119
+ export function runRedteam(guardrails, cases, opts = {}) {
120
+ const stage = opts.stage ?? 'input';
121
+ const report = new RedTeamReport();
122
+ for (const c of cases) {
123
+ let tripped;
124
+ try {
125
+ tripped = apply(guardrails, stage, c.text).length > 0;
126
+ }
127
+ catch (err) {
128
+ if (!(err instanceof GuardrailTripped))
129
+ throw err;
130
+ tripped = true;
131
+ }
132
+ tally(report, c, tripped);
133
+ }
134
+ return report;
135
+ }
136
+ /** Async counterpart of {@link runRedteam} — awaits `async` checks (an `llmJudge` / hosted rail). */
137
+ export async function runRedteamAsync(guardrails, cases, opts = {}) {
138
+ const stage = opts.stage ?? 'input';
139
+ const report = new RedTeamReport();
140
+ for (const c of cases) {
141
+ let tripped;
142
+ try {
143
+ tripped = (await applyAsync(guardrails, stage, c.text)).length > 0;
144
+ }
145
+ catch (err) {
146
+ if (!(err instanceof GuardrailTripped))
147
+ throw err;
148
+ tripped = true;
149
+ }
150
+ tally(report, c, tripped);
151
+ }
152
+ return report;
153
+ }
154
+ //# sourceMappingURL=redteam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redteam.js","sourceRoot":"","sources":["../src/redteam.ts"],"names":[],"mappings":"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"}
package/dist/rules.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Check, type Context, type Guardrail, Verdict } from './decision.js';
1
+ import { type Check, type Context, type Guardrail, type OnError, Verdict } from './decision.js';
2
2
  type Stage = string | readonly string[];
3
3
  /** Flatten a payload (string, chat-message array, or a single message object) to scannable text. */
4
4
  export declare function payloadText(payload: unknown): string;
@@ -51,13 +51,26 @@ export declare function jsonSchema(schema: Record<string, unknown>, opts?: JsonS
51
51
  export interface CustomOptions {
52
52
  stage?: Stage;
53
53
  name?: string;
54
+ /** Per-check wall-clock limit in seconds (async path only). */
55
+ timeout?: number;
56
+ /** Error/timeout policy (default `"fail_closed"`). */
57
+ onError?: OnError;
54
58
  }
55
- /** Wrap any `fn(payload, ctx) -> Verdict | null` as a `Guardrail` (sync or async). */
59
+ /**
60
+ * Wrap any `fn(payload, ctx) -> Verdict | null` as a `Guardrail` (sync or async). Your `fn` is
61
+ * arbitrary code, so it can throw or hang: `timeout` (seconds, async path) bounds it and `onError`
62
+ * (`"fail_closed"` default / `"fail_open"`) decides what a throw or timeout does — either way the
63
+ * failure is recorded as a decision.
64
+ */
56
65
  export declare function custom(fn: Check, opts?: CustomOptions): Guardrail;
57
66
  export interface LlmJudgeOptions {
58
67
  stage?: Stage;
59
68
  action?: 'block' | 'redact' | 'flag';
60
69
  name?: string;
70
+ /** Per-check wall-clock limit in seconds (async path only). */
71
+ timeout?: number;
72
+ /** Error/timeout policy; defaults from `action` (flag → `fail_open`, else `fail_closed`). */
73
+ onError?: OnError;
61
74
  }
62
75
  /**
63
76
  * Adapter **contract** for a bring-your-own model judge — not a built-in classifier. `judge` is
@@ -66,5 +79,6 @@ export interface LlmJudgeOptions {
66
79
  * no model here: the extra call costs real tokens and latency — measure it. See "Honest limits".
67
80
  */
68
81
  export declare function llmJudge(judge: (payload: unknown, ctx: Context) => Verdict | boolean | string | null | Promise<Verdict | boolean | string | null>, opts?: LlmJudgeOptions): Guardrail;
69
- export {};
82
+ export { classifier, language, openaiModeration, bedrockGuardrail, azureContentSafety, modelArmor, } from './adapters.js';
83
+ export { groundedness, deniedTopics } from './semantic.js';
70
84
  //# sourceMappingURL=rules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
package/dist/rules.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * adversarial attack — see docs/guardrails.md "Honest limits".
9
9
  */
10
10
  import { tokens } from '@cendor/core';
11
- import { Verdict, normalizeStages } from './decision.js';
11
+ import { Verdict, normalizeStages, validateExecutionPolicy, } from './decision.js';
12
12
  // --------------------------------------------------------------------------- payload helpers
13
13
  /** Flatten a payload (string, chat-message array, or a single message object) to scannable text. */
14
14
  export function payloadText(payload) {
@@ -65,8 +65,28 @@ function redactMessage(msg, sub) {
65
65
  }
66
66
  return out;
67
67
  }
68
- function mkGuardrail(name, stage, check) {
69
- return { name, stages: normalizeStages(stage), check };
68
+ function mkGuardrail(name, stage, check, exec = {}) {
69
+ const g = { name, stages: normalizeStages(stage), check };
70
+ // Deterministic built-ins leave the execution policy unset (the engine defaults on_error to
71
+ // fail_closed); only `custom` / `llmJudge` attach a timeout / on_error.
72
+ if (exec.onError !== undefined || exec.timeout !== undefined) {
73
+ const onError = exec.onError ?? 'fail_closed';
74
+ validateExecutionPolicy(exec.timeout, onError);
75
+ g.onError = onError;
76
+ if (exec.timeout !== undefined)
77
+ g.timeout = exec.timeout;
78
+ }
79
+ return g;
80
+ }
81
+ /**
82
+ * Default the error policy from a guardrail's action: a `block` gate fails **closed** (an errored
83
+ * check is treated as a block), while a `flag` degrades to advisory (`fail_open`). An explicit
84
+ * `onError` always wins.
85
+ */
86
+ function resolveOnError(action, onError) {
87
+ if (onError !== undefined)
88
+ return onError;
89
+ return action === 'flag' ? 'fail_open' : 'fail_closed';
70
90
  }
71
91
  /** Trip when any of `words` appears (substring, case-insensitive by default). */
72
92
  export function keywordDeny(words, opts = {}) {
@@ -193,9 +213,17 @@ export function jsonSchema(schema, opts = {}) {
193
213
  return error === null ? null : new Verdict(action, `schema violation: ${error}`);
194
214
  });
195
215
  }
196
- /** Wrap any `fn(payload, ctx) -> Verdict | null` as a `Guardrail` (sync or async). */
216
+ /**
217
+ * Wrap any `fn(payload, ctx) -> Verdict | null` as a `Guardrail` (sync or async). Your `fn` is
218
+ * arbitrary code, so it can throw or hang: `timeout` (seconds, async path) bounds it and `onError`
219
+ * (`"fail_closed"` default / `"fail_open"`) decides what a throw or timeout does — either way the
220
+ * failure is recorded as a decision.
221
+ */
197
222
  export function custom(fn, opts = {}) {
198
- return mkGuardrail(opts.name ?? (fn.name || 'custom'), opts.stage ?? 'input', fn);
223
+ return mkGuardrail(opts.name ?? (fn.name || 'custom'), opts.stage ?? 'input', fn, {
224
+ timeout: opts.timeout,
225
+ onError: opts.onError ?? 'fail_closed',
226
+ });
199
227
  }
200
228
  /**
201
229
  * Adapter **contract** for a bring-your-own model judge — not a built-in classifier. `judge` is
@@ -206,7 +234,10 @@ export function custom(fn, opts = {}) {
206
234
  export function llmJudge(judge, opts = {}) {
207
235
  const { stage = 'output', action = 'block', name = 'llm_judge' } = opts;
208
236
  const check = async (payload, ctx) => coerce(await judge(payload, ctx), action);
209
- return mkGuardrail(name, stage, check);
237
+ return mkGuardrail(name, stage, check, {
238
+ timeout: opts.timeout,
239
+ onError: resolveOnError(action, opts.onError),
240
+ });
210
241
  }
211
242
  function coerce(result, action) {
212
243
  if (result === null || result === false)
@@ -267,4 +298,13 @@ function typeName(d) {
267
298
  return 'array';
268
299
  return typeof d;
269
300
  }
301
+ // --------------------------------------------------------------------------- detection-tier adapters
302
+ //
303
+ // The opt-in detection tier (local ML classifier / language detector / hosted moderation + the three
304
+ // hosted rails) lives in `./adapters`, and the similarity checks (groundedness / deniedTopics) in
305
+ // `./semantic` — each reuses `payloadText` above. Re-exported here so they read as `rules.classifier`
306
+ // / `rules.bedrockGuardrail` / `rules.groundedness` etc. as one surface (like Python's `rules`). Both
307
+ // import only `./decision` + `payloadText` (a hoisted function), so the cycle is runtime-safe.
308
+ export { classifier, language, openaiModeration, bedrockGuardrail, azureContentSafety, modelArmor, } from './adapters.js';
309
+ export { groundedness, deniedTopics } from './semantic.js';
270
310
  //# sourceMappingURL=rules.js.map