@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/README.md +18 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters.d.ts +150 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +393 -0
- package/dist/adapters.js.map +1 -0
- package/dist/decision.d.ts +45 -1
- package/dist/decision.d.ts.map +1 -1
- package/dist/decision.js +32 -2
- package/dist/decision.js.map +1 -1
- package/dist/index.d.ts +38 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +229 -40
- package/dist/index.js.map +1 -1
- package/dist/judge.d.ts +68 -0
- package/dist/judge.d.ts.map +1 -0
- package/dist/judge.js +115 -0
- package/dist/judge.js.map +1 -0
- package/dist/policy.d.ts +41 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +209 -0
- package/dist/policy.js.map +1 -0
- package/dist/redteam.d.ts +63 -0
- package/dist/redteam.d.ts.map +1 -0
- package/dist/redteam.js +154 -0
- package/dist/redteam.js.map +1 -0
- package/dist/rules.d.ts +17 -3
- package/dist/rules.d.ts.map +1 -1
- package/dist/rules.js +46 -6
- package/dist/rules.js.map +1 -1
- package/dist/semantic.d.ts +52 -0
- package/dist/semantic.d.ts.map +1 -0
- package/dist/semantic.js +100 -0
- package/dist/semantic.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,OAAO,EAQR,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,iBAAiB,EAGlB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,OAAO,EAQR,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,iBAAiB,EAGlB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,KAAK,EACL,SAAS,EACT,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAElE,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AA2GD;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,OAAO,GACZ,UAAU,CAuBZ;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,OAAO,GACZ,OAAO,CAAC,UAAU,CAAC,CAiBrB;AAED,+FAA+F;AAC/F,wBAAgB,KAAK,CACnB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,OAAO,GACZ,iBAAiB,EAAE,CAErB;AAED,0CAA0C;AAC1C,wBAAsB,UAAU,CAC9B,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,OAAO,GACZ,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAE9B;AA4CD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAS9D;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,IAAI,IAAI,CAKhC;AA6DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAsBhB;AAQD,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQzD"}
|
package/dist/index.js
CHANGED
|
@@ -11,9 +11,15 @@
|
|
|
11
11
|
* Imports ONLY `@cendor/core` (constitution rule 2). See docs/guardrails.md.
|
|
12
12
|
*/
|
|
13
13
|
import { LLMCall, MISS, Reroute, ToolCall, addInterceptor, bus, currentTraceId, removeInterceptor, } from '@cendor/core';
|
|
14
|
-
import { GuardrailDecision, GuardrailTripped, } from './decision.js';
|
|
15
|
-
export { STAGES, ACTIONS, Verdict, GuardrailDecision, GuardrailTripped, defineGuardrail, normalizeStages, } from './decision.js';
|
|
14
|
+
import { GuardrailDecision, GuardrailTripped, Verdict, } from './decision.js';
|
|
15
|
+
export { STAGES, ACTIONS, ON_ERROR, Verdict, GuardrailDecision, GuardrailTripped, defineGuardrail, normalizeStages, validateExecutionPolicy, } from './decision.js';
|
|
16
16
|
export * as rules from './rules.js';
|
|
17
|
+
export * as judge from './judge.js';
|
|
18
|
+
export * as adapters from './adapters.js';
|
|
19
|
+
export * as semantic from './semantic.js';
|
|
20
|
+
export * as redteam from './redteam.js';
|
|
21
|
+
export { loadPolicy, POLICY_RULE_NAMES } from './policy.js';
|
|
22
|
+
export { loadCorpus, runRedteam, runRedteamAsync, RedTeamReport } from './redteam.js';
|
|
17
23
|
function applicable(guardrails, stage) {
|
|
18
24
|
return guardrails.filter((g) => g.stages.includes(stage));
|
|
19
25
|
}
|
|
@@ -27,7 +33,9 @@ function emit(g, stage, verdict, ctx) {
|
|
|
27
33
|
tool: ctx.tool ?? '',
|
|
28
34
|
traceId: ctx.traceId || currentTraceId(),
|
|
29
35
|
ts: new Date(),
|
|
30
|
-
metadata
|
|
36
|
+
// the guardrail's static metadata (e.g. loadPolicy's policy_hash/version) is the base; the
|
|
37
|
+
// caller's per-call Context.metadata layers on top and wins any key clash.
|
|
38
|
+
metadata: { ...(g.metadata ?? {}), ...(ctx.metadata ?? {}) },
|
|
31
39
|
});
|
|
32
40
|
bus.emit(decision); // acttrace (if attached) chains this as a guardrail_decision entry
|
|
33
41
|
return decision;
|
|
@@ -45,32 +53,109 @@ function handle(verdict, g, stage, payload, ctx, decisions) {
|
|
|
45
53
|
function isPromise(x) {
|
|
46
54
|
return x != null && typeof x.then === 'function';
|
|
47
55
|
}
|
|
56
|
+
const ERROR_REASON_MAX = 200;
|
|
57
|
+
/** A timeout marker error — a check that outran its `timeout` on the async path. */
|
|
58
|
+
class GuardrailTimeoutError extends Error {
|
|
59
|
+
constructor(message) {
|
|
60
|
+
super(message);
|
|
61
|
+
this.name = 'TimeoutError';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Map a check error/timeout to a verdict per `g.onError`. Either way this becomes a
|
|
66
|
+
* `GuardrailDecision`, so the audit chain records that the check could not run — never a silently
|
|
67
|
+
* swallowed exception. The reason carries the error *name* + a truncated message, never the payload
|
|
68
|
+
* the check saw.
|
|
69
|
+
*/
|
|
70
|
+
function onErrorVerdict(g, exc) {
|
|
71
|
+
const name = exc?.name ??
|
|
72
|
+
exc?.constructor?.name ??
|
|
73
|
+
'Error';
|
|
74
|
+
const message = exc?.message ?? String(exc);
|
|
75
|
+
let detail = `${name}: ${message}`;
|
|
76
|
+
if (detail.length > ERROR_REASON_MAX)
|
|
77
|
+
detail = `${detail.slice(0, ERROR_REASON_MAX)}…`;
|
|
78
|
+
if ((g.onError ?? 'fail_closed') === 'fail_open') {
|
|
79
|
+
return new Verdict('flag', `check errored (fail-open): ${detail}`);
|
|
80
|
+
}
|
|
81
|
+
return new Verdict('block', `check errored (fail-closed): ${detail}`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run one guardrail's check on the async path, bounding an `async` check to `g.timeout` via
|
|
85
|
+
* `Promise.race`. A *sync* check runs inline (a sync `timeout` cannot be enforced without threads —
|
|
86
|
+
* see {@link Guardrail}). Throws propagate to the caller's `onError` mapping.
|
|
87
|
+
*/
|
|
88
|
+
async function runCheckAsync(g, payload, ctx) {
|
|
89
|
+
const result = g.check(payload, ctx);
|
|
90
|
+
if (isPromise(result) && g.timeout !== undefined) {
|
|
91
|
+
const seconds = g.timeout;
|
|
92
|
+
let handle;
|
|
93
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
94
|
+
handle = setTimeout(() => reject(new GuardrailTimeoutError(`guardrail ${JSON.stringify(g.name)} check exceeded ${seconds}s`)), seconds * 1000);
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
return (await Promise.race([result, timeout]));
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
if (handle !== undefined)
|
|
101
|
+
clearTimeout(handle);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return (await result);
|
|
105
|
+
}
|
|
48
106
|
/**
|
|
49
107
|
* Run the `stage` guardrails over `payload` **synchronously**. Returns `{ payload, decisions }`
|
|
50
108
|
* with any redactions applied in order; throws `GuardrailTripped` on the first block. An `async`
|
|
51
|
-
* check throws here — use {@link evaluateAsync}.
|
|
109
|
+
* check throws here — use {@link evaluateAsync}. A *throwing* sync check honours its `onError`
|
|
110
|
+
* policy; `timeout` applies to the async path only (no sync threads in JS).
|
|
52
111
|
*/
|
|
53
112
|
export function evaluate(guardrails, stage, payload, ctx) {
|
|
54
113
|
const context = ctx ?? { stage };
|
|
55
114
|
const decisions = [];
|
|
56
115
|
let current = payload;
|
|
57
116
|
for (const g of applicable(guardrails, stage)) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = g.check(current, context);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err instanceof GuardrailTripped)
|
|
123
|
+
throw err;
|
|
124
|
+
// a THROWING sync check → its on_error policy (recorded), never a swallowed exception
|
|
125
|
+
current = handle(onErrorVerdict(g, err), g, stage, current, context, decisions);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// An async check on the sync path is a misuse: this TypeError is raised OUTSIDE the on_error
|
|
129
|
+
// try above so it always propagates (parity with Python `_invoke_sync`), never mapped to a block.
|
|
130
|
+
if (isPromise(result)) {
|
|
131
|
+
void result.catch(() => { }); // swallow the rejection of the discarded promise
|
|
61
132
|
throw new TypeError(`guardrail ${JSON.stringify(g.name)} is async; use evaluateAsync`);
|
|
62
133
|
}
|
|
63
|
-
current = handle(
|
|
134
|
+
current = handle(result, g, stage, current, context, decisions);
|
|
64
135
|
}
|
|
65
136
|
return { payload: current, decisions };
|
|
66
137
|
}
|
|
67
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Async counterpart of {@link evaluate}: awaits `async` checks (bounded by each guardrail's
|
|
140
|
+
* `timeout`), calls sync ones directly, and applies each guardrail's `onError` policy on a
|
|
141
|
+
* throw/timeout.
|
|
142
|
+
*/
|
|
68
143
|
export async function evaluateAsync(guardrails, stage, payload, ctx) {
|
|
69
144
|
const context = ctx ?? { stage };
|
|
70
145
|
const decisions = [];
|
|
71
146
|
let current = payload;
|
|
72
147
|
for (const g of applicable(guardrails, stage)) {
|
|
73
|
-
|
|
148
|
+
let verdict;
|
|
149
|
+
try {
|
|
150
|
+
verdict = await runCheckAsync(g, current, context);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
if (err instanceof GuardrailTripped)
|
|
154
|
+
throw err; // a block from a nested handle propagates
|
|
155
|
+
verdict = onErrorVerdict(g, err); // throw / timeout → on_error policy (recorded)
|
|
156
|
+
}
|
|
157
|
+
// handle() throws GuardrailTripped on a block — OUTSIDE the try, so a block is never caught as
|
|
158
|
+
// an on_error (only the CHECK's own throws map to on_error).
|
|
74
159
|
current = handle(verdict, g, stage, current, context, decisions);
|
|
75
160
|
}
|
|
76
161
|
return { payload: current, decisions };
|
|
@@ -86,46 +171,58 @@ export async function applyAsync(guardrails, stage, payload, ctx) {
|
|
|
86
171
|
// --------------------------------------------------------------------------- standalone wiring
|
|
87
172
|
let installedInterceptor = null;
|
|
88
173
|
let installedSubscriber = null;
|
|
174
|
+
/**
|
|
175
|
+
* The shared interceptor body used by {@link install} and {@link scoped} — gate an `LLMCall` at the
|
|
176
|
+
* `input` stage (redact reroutes, block raises, pass declines) and a `ToolCall` at the `tool_call`
|
|
177
|
+
* stage (block raises; redact/flag recorded but the call proceeds).
|
|
178
|
+
*/
|
|
179
|
+
function gateInterceptor(gl, event) {
|
|
180
|
+
if (event instanceof LLMCall) {
|
|
181
|
+
const ctx = { stage: 'input', traceId: event.traceId };
|
|
182
|
+
const { payload, decisions } = evaluate(gl, 'input', event.messages, ctx);
|
|
183
|
+
if (decisions.some((d) => d.action === 'redact'))
|
|
184
|
+
return new Reroute({ messages: payload });
|
|
185
|
+
return MISS;
|
|
186
|
+
}
|
|
187
|
+
if (event instanceof ToolCall) {
|
|
188
|
+
const ctx = {
|
|
189
|
+
stage: 'tool_call',
|
|
190
|
+
tool: event.name,
|
|
191
|
+
toolArgs: event.arguments,
|
|
192
|
+
traceId: event.traceId,
|
|
193
|
+
};
|
|
194
|
+
evaluate(gl, 'tool_call', event.arguments, ctx); // block throws; else record + proceed
|
|
195
|
+
return MISS;
|
|
196
|
+
}
|
|
197
|
+
return MISS;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The shared post-flight output subscriber body: gate the completed `LLMCall`'s response text at the
|
|
201
|
+
* `output` stage (block raises after the call ran).
|
|
202
|
+
*/
|
|
203
|
+
function gateOutput(gl, event) {
|
|
204
|
+
if (!(event instanceof LLMCall))
|
|
205
|
+
return;
|
|
206
|
+
const text = responseText(event);
|
|
207
|
+
if (text === null)
|
|
208
|
+
return;
|
|
209
|
+
const ctx = { stage: 'output', traceId: event.traceId };
|
|
210
|
+
evaluate(gl, 'output', text, ctx); // block throws post-flight
|
|
211
|
+
}
|
|
89
212
|
/**
|
|
90
213
|
* Gate every instrumented call by registering ONE `@cendor/core` interceptor (+ an output
|
|
91
214
|
* subscriber). Framework-independent. Input: a block raises (nothing spends), a redact reroutes the
|
|
92
215
|
* cleaned messages, a pass declines. tool_call: a block raises; else record + proceed (tools have
|
|
93
216
|
* no message-rewrite seam). Output: a bus subscriber raises **post-flight** on a block (same
|
|
94
217
|
* overshoot semantics as tokenguard's `onExceed:"raise"`). Runs sync checks only. Call
|
|
95
|
-
* {@link uninstall} to remove.
|
|
218
|
+
* {@link uninstall} to remove. `install()` is **process-global** — for a concurrent server that
|
|
219
|
+
* varies guardrails per request, use {@link scoped} instead.
|
|
96
220
|
*/
|
|
97
221
|
export function install(guardrails) {
|
|
98
222
|
uninstall();
|
|
99
223
|
const gl = [...guardrails];
|
|
100
|
-
const interceptor = (event) =>
|
|
101
|
-
|
|
102
|
-
const ctx = { stage: 'input', traceId: event.traceId };
|
|
103
|
-
const { payload, decisions } = evaluate(gl, 'input', event.messages, ctx);
|
|
104
|
-
if (decisions.some((d) => d.action === 'redact'))
|
|
105
|
-
return new Reroute({ messages: payload });
|
|
106
|
-
return MISS;
|
|
107
|
-
}
|
|
108
|
-
if (event instanceof ToolCall) {
|
|
109
|
-
const ctx = {
|
|
110
|
-
stage: 'tool_call',
|
|
111
|
-
tool: event.name,
|
|
112
|
-
toolArgs: event.arguments,
|
|
113
|
-
traceId: event.traceId,
|
|
114
|
-
};
|
|
115
|
-
evaluate(gl, 'tool_call', event.arguments, ctx); // block throws; else record + proceed
|
|
116
|
-
return MISS;
|
|
117
|
-
}
|
|
118
|
-
return MISS;
|
|
119
|
-
};
|
|
120
|
-
const subscriber = (event) => {
|
|
121
|
-
if (!(event instanceof LLMCall))
|
|
122
|
-
return;
|
|
123
|
-
const text = responseText(event);
|
|
124
|
-
if (text === null)
|
|
125
|
-
return;
|
|
126
|
-
const ctx = { stage: 'output', traceId: event.traceId };
|
|
127
|
-
evaluate(gl, 'output', text, ctx); // block throws post-flight
|
|
128
|
-
};
|
|
224
|
+
const interceptor = (event) => gateInterceptor(gl, event);
|
|
225
|
+
const subscriber = (event) => gateOutput(gl, event);
|
|
129
226
|
addInterceptor(interceptor);
|
|
130
227
|
bus.subscribe(subscriber);
|
|
131
228
|
installedInterceptor = interceptor;
|
|
@@ -140,6 +237,98 @@ export function uninstall() {
|
|
|
140
237
|
installedInterceptor = null;
|
|
141
238
|
installedSubscriber = null;
|
|
142
239
|
}
|
|
240
|
+
//: The guardrails active in *this* execution context, or `undefined` outside any `scoped` block.
|
|
241
|
+
//: With a real `AsyncLocalStorage` installed (Node), overlapping async tasks each see their own
|
|
242
|
+
//: value; the ambient fallback is single-context (documented — not concurrency-safe).
|
|
243
|
+
let scopeStore;
|
|
244
|
+
let ambientScope;
|
|
245
|
+
let alsRequested = false;
|
|
246
|
+
/**
|
|
247
|
+
* Lazily install a real `AsyncLocalStorage`-backed scope store (Node only), so overlapping async
|
|
248
|
+
* requests stay isolated. Best-effort + idempotent: on edge/browser (`node:async_hooks` missing) it
|
|
249
|
+
* quietly keeps the ambient fallback, so `@cendor/guardrails` stays all-runtime (no `node:*` at the
|
|
250
|
+
* module top). Mirrors the spirit of `@cendor/core`'s `installTraceContext`.
|
|
251
|
+
*/
|
|
252
|
+
async function ensureAls() {
|
|
253
|
+
if (alsRequested)
|
|
254
|
+
return;
|
|
255
|
+
alsRequested = true;
|
|
256
|
+
try {
|
|
257
|
+
const { AsyncLocalStorage } = await import('node:async_hooks');
|
|
258
|
+
if (!scopeStore)
|
|
259
|
+
scopeStore = new AsyncLocalStorage();
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// node:async_hooks unavailable (edge/browser) — keep the ambient fallback (single-context)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
void ensureAls(); // best-effort auto-install so real Node usage gets concurrency-correct isolation
|
|
266
|
+
function activeScope() {
|
|
267
|
+
return scopeStore ? scopeStore.getStore() : ambientScope;
|
|
268
|
+
}
|
|
269
|
+
function scopedInterceptor(event) {
|
|
270
|
+
const gl = activeScope();
|
|
271
|
+
if (!gl || gl.length === 0)
|
|
272
|
+
return MISS; // no active scope in this context — decline
|
|
273
|
+
return gateInterceptor(gl, event);
|
|
274
|
+
}
|
|
275
|
+
function scopedSubscriber(event) {
|
|
276
|
+
const gl = activeScope();
|
|
277
|
+
if (!gl || gl.length === 0)
|
|
278
|
+
return;
|
|
279
|
+
gateOutput(gl, event);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Register the context-gated interceptor + subscriber (idempotent — `addInterceptor` / `bus.subscribe`
|
|
283
|
+
* de-dupe). They stay registered and are no-ops outside a {@link scoped} block, so leaving them
|
|
284
|
+
* installed costs a store lookup per call and needs no teardown.
|
|
285
|
+
*/
|
|
286
|
+
function ensureScopedSeam() {
|
|
287
|
+
addInterceptor(scopedInterceptor);
|
|
288
|
+
bus.subscribe(scopedSubscriber);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Gate every instrumented call for the duration of `fn` — like {@link install}, but **scoped to the
|
|
292
|
+
* current execution context** rather than process-global. Runs `fn` with `guardrails` active and
|
|
293
|
+
* returns its result (sync or a `Promise`); the previous scope is restored on exit.
|
|
294
|
+
*
|
|
295
|
+
* With a real `AsyncLocalStorage` (auto-installed on Node), a concurrent server (async tasks) can
|
|
296
|
+
* vary guardrails per request without one request's set leaking into another. On edge/browser the
|
|
297
|
+
* fallback is a save/restore module variable — correct for sequential/nested use, **not**
|
|
298
|
+
* concurrency-safe. Nest freely — an inner `scoped` replaces the set for its block. Runs sync checks
|
|
299
|
+
* only (the seam is sync).
|
|
300
|
+
*
|
|
301
|
+
* ```ts
|
|
302
|
+
* await scoped([rules.keywordDeny(['secret'], { action: 'block' })], async () => {
|
|
303
|
+
* await client.chat.completions.create(...); // gated here
|
|
304
|
+
* });
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
export function scoped(guardrails, fn) {
|
|
308
|
+
ensureScopedSeam();
|
|
309
|
+
void ensureAls();
|
|
310
|
+
const gl = [...guardrails];
|
|
311
|
+
if (scopeStore)
|
|
312
|
+
return scopeStore.run(gl, fn);
|
|
313
|
+
// Ambient fallback (single-context): save/restore, honouring sync or async `fn` like core's trace.
|
|
314
|
+
const previous = ambientScope;
|
|
315
|
+
ambientScope = gl;
|
|
316
|
+
let result;
|
|
317
|
+
try {
|
|
318
|
+
result = fn();
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
ambientScope = previous;
|
|
322
|
+
throw err;
|
|
323
|
+
}
|
|
324
|
+
if (result instanceof Promise) {
|
|
325
|
+
return result.finally(() => {
|
|
326
|
+
ambientScope = previous;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
ambientScope = previous;
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
143
332
|
function get(obj, name) {
|
|
144
333
|
return obj != null && typeof obj === 'object'
|
|
145
334
|
? obj[name]
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,GAAG,EACH,cAAc,EACd,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,GACR,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAStF,SAAS,UAAU,CAAC,UAAgC,EAAE,KAAa;IACjE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,IAAI,CAAC,CAAY,EAAE,KAAa,EAAE,OAAgB,EAAE,GAAY;IACvE,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC;QACrC,SAAS,EAAE,CAAC,CAAC,IAAI;QACjB,KAAK;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;QACpB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,cAAc,EAAE;QACxC,EAAE,EAAE,IAAI,IAAI,EAAE;QACd,2FAA2F;QAC3F,2EAA2E;QAC3E,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;KAC7D,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,mEAAmE;IACvF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CACb,OAAuB,EACvB,CAAY,EACZ,KAAa,EACb,OAAgB,EAChB,GAAY,EACZ,SAA8B;IAE9B,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IACrC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO;QAAE,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,WAAW,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC,WAAW,CAAC;IAC3F,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAAC,CAAU;IAC3B,OAAO,CAAC,IAAI,IAAI,IAAI,OAAQ,CAAwB,CAAC,IAAI,KAAK,UAAU,CAAC;AAC3E,CAAC;AAED,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,oFAAoF;AACpF,MAAM,qBAAsB,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,CAAY,EAAE,GAAY;IAChD,MAAM,IAAI,GACP,GAAyB,EAAE,IAAI;QAC/B,GAA2C,EAAE,WAAW,EAAE,IAAI;QAC/D,OAAO,CAAC;IACV,MAAM,OAAO,GAAI,GAA4B,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;IACtE,IAAI,MAAM,GAAG,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,GAAG,gBAAgB;QAAE,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC;IACvF,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,aAAa,CAAC,KAAK,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,8BAA8B,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,gCAAgC,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAC1B,CAAY,EACZ,OAAgB,EAChB,GAAY;IAEZ,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC1B,IAAI,MAAiD,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACtD,MAAM,GAAG,UAAU,CACjB,GAAG,EAAE,CACH,MAAM,CACJ,IAAI,qBAAqB,CACvB,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,OAAO,GAAG,CACjE,CACF,EACH,OAAO,GAAG,IAAI,CACf,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAmB,CAAC;QACnE,CAAC;gBAAS,CAAC;YACT,IAAI,MAAM,KAAK,SAAS;gBAAE,YAAY,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,CAAC,MAAM,MAAM,CAAmB,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CACtB,UAAgC,EAChC,KAAa,EACb,OAAgB,EAChB,GAAa;IAEb,MAAM,OAAO,GAAY,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAwB,EAAE,CAAC;IAC1C,IAAI,OAAO,GAAG,OAAO,CAAC;IACtB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9C,IAAI,MAAgD,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,gBAAgB;gBAAE,MAAM,GAAG,CAAC;YAC/C,sFAAsF;YACtF,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YAChF,SAAS;QACX,CAAC;QACD,6FAA6F;QAC7F,kGAAkG;QAClG,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,iDAAiD;YAC9E,MAAM,IAAI,SAAS,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAgC,EAChC,KAAa,EACb,OAAgB,EAChB,GAAa;IAEb,MAAM,OAAO,GAAY,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAwB,EAAE,CAAC;IAC1C,IAAI,OAAO,GAAG,OAAO,CAAC;IACtB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9C,IAAI,OAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,gBAAgB;gBAAE,MAAM,GAAG,CAAC,CAAC,0CAA0C;YAC1F,OAAO,GAAG,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,+CAA+C;QACnF,CAAC;QACD,+FAA+F;QAC/F,6DAA6D;QAC7D,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACzC,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,KAAK,CACnB,UAAgC,EAChC,KAAa,EACb,OAAgB,EAChB,GAAa;IAEb,OAAO,QAAQ,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAED,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,UAAgC,EAChC,KAAa,EACb,OAAgB,EAChB,GAAa;IAEb,OAAO,CAAC,MAAM,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1E,CAAC;AAED,gGAAgG;AAEhG,IAAI,oBAAoB,GAAyC,IAAI,CAAC;AACtE,IAAI,mBAAmB,GAAsC,IAAI,CAAC;AAElE;;;;GAIG;AACH,SAAS,eAAe,CAAC,EAAwB,EAAE,KAAc;IAC/D,IAAI,KAAK,YAAY,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAY,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAChE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC1E,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC;YAAE,OAAO,IAAI,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAY;YACnB,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,SAAS;YACzB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;QACF,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,sCAAsC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,EAAwB,EAAE,KAAc;IAC1D,IAAI,CAAC,CAAC,KAAK,YAAY,OAAO,CAAC;QAAE,OAAO;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO;IAC1B,MAAM,GAAG,GAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACjE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,2BAA2B;AAChE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,UAAgC;IACtD,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;IAC3B,MAAM,WAAW,GAAG,CAAC,KAAc,EAAW,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,KAAc,EAAQ,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACnE,cAAc,CAAC,WAAW,CAAC,CAAC;IAC5B,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC1B,oBAAoB,GAAG,WAAW,CAAC;IACnC,mBAAmB,GAAG,UAAU,CAAC;AACnC,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,SAAS;IACvB,IAAI,oBAAoB;QAAE,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;IAClE,IAAI,mBAAmB;QAAE,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAC9D,oBAAoB,GAAG,IAAI,CAAC;IAC5B,mBAAmB,GAAG,IAAI,CAAC;AAC7B,CAAC;AAUD,iGAAiG;AACjG,gGAAgG;AAChG,sFAAsF;AACtF,IAAI,UAAkC,CAAC;AACvC,IAAI,YAA8C,CAAC;AACnD,IAAI,YAAY,GAAG,KAAK,CAAC;AAEzB;;;;;GAKG;AACH,KAAK,UAAU,SAAS;IACtB,IAAI,YAAY;QAAE,OAAO;IACzB,YAAY,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU;YAAE,UAAU,GAAG,IAAI,iBAAiB,EAAwB,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,2FAA2F;IAC7F,CAAC;AACH,CAAC;AACD,KAAK,SAAS,EAAE,CAAC,CAAC,iFAAiF;AAEnG,SAAS,WAAW;IAClB,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;AAC3D,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc;IACvC,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,4CAA4C;IACrF,OAAO,eAAe,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACnC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB;IACvB,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAClC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CACpB,UAAgC,EAChC,EAAwB;IAExB,gBAAgB,EAAE,CAAC;IACnB,KAAK,SAAS,EAAE,CAAC;IACjB,MAAM,EAAE,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;IAC3B,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9C,mGAAmG;IACnG,MAAM,QAAQ,GAAG,YAAY,CAAC;IAC9B,YAAY,GAAG,EAAE,CAAC;IAClB,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,EAAE,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,GAAG,QAAQ,CAAC;QACxB,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;YACzB,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IACD,YAAY,GAAG,QAAQ,CAAC;IACxB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,GAAG,CAAC,GAAY,EAAE,IAAY;IACrC,OAAO,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAC3C,CAAC,CAAE,GAA+B,CAAC,IAAI,CAAC;QACxC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACzC,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,8CAA8C;IAC7D,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,QAAiB;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,qCAAqC;IACtF,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IACpE,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,6BAA6B;IACvE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QAC3D,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;IAClD,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,2BAA2B;IACrE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;QAC1E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS;IACnD,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACrC,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS;IAC7C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAClD,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,mBAAmB;IAC/E,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/judge.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-judge helpers — turn a bring-your-own model call into a usable guardrail. The TS port of
|
|
3
|
+
* `cendor.guardrails.judge`.
|
|
4
|
+
*
|
|
5
|
+
* cendor ships **no** classifier. `rules.llmJudge` is an adapter *contract*: you supply the model
|
|
6
|
+
* call. These helpers give that call the boring, error-prone parts so you don't hand-roll them —
|
|
7
|
+
*
|
|
8
|
+
* - {@link verdictPrompt} — a strict system instruction that pins the model to a two-field JSON
|
|
9
|
+
* verdict (`{"trip": <bool>, "reason": "<short>"}`), so the output is parseable, not prose.
|
|
10
|
+
* - {@link parseVerdict} — strict JSON → `Verdict` (or `null` to pass). Malformed output *throws* so
|
|
11
|
+
* the guardrail's `onError` policy decides (a garbled judge fails **closed** by default).
|
|
12
|
+
* - {@link judge} — glue the two around your `respond(system, user)` callable (sync or async) into a
|
|
13
|
+
* check you hand straight to `rules.llmJudge`.
|
|
14
|
+
*
|
|
15
|
+
* The judge's own spend is budgeted and audited: make the call through an `instrument()`-ed client
|
|
16
|
+
* and its tokens + cost land in tokenguard / acttrace like any other call.
|
|
17
|
+
*/
|
|
18
|
+
import { type Context, Verdict } from './decision.js';
|
|
19
|
+
/** A `respond(system, user)` model callable (sync or async) — returns the assistant's reply string. */
|
|
20
|
+
export type Respond = (system: string, user: string) => string | Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* The default judge instruction. `{policy}` is filled with your risk description. It pins the model
|
|
23
|
+
* to strict JSON so {@link parseVerdict} can read it — no chain-of-thought, no prose.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DEFAULT_SYSTEM: string;
|
|
26
|
+
/**
|
|
27
|
+
* Build the judge's system instruction for `policy` (a plain-language description of what should
|
|
28
|
+
* trip). Override `template` to customise, keeping the `{policy}` placeholder and the strict-JSON
|
|
29
|
+
* verdict contract {@link parseVerdict} expects.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verdictPrompt(policy: string, template?: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Parse a strict-JSON judge reply into a `Verdict` (trip) or `null` (pass). Expects
|
|
34
|
+
* `{"trip": <bool>, "reason": "<short>"}`. Trips with `action` (default `"block"`) and the model's
|
|
35
|
+
* reason. **Throws** on malformed output — deliberately: a judge whose output can't be read must not
|
|
36
|
+
* silently pass, so the caller's `onError` policy (fail-closed by default) decides. See {@link judge}.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseVerdict(text: string, opts?: {
|
|
39
|
+
action?: 'block' | 'redact' | 'flag';
|
|
40
|
+
}): Verdict | null;
|
|
41
|
+
/**
|
|
42
|
+
* Compose {@link verdictPrompt} + your model call + {@link parseVerdict} into a check ready for
|
|
43
|
+
* `rules.llmJudge`.
|
|
44
|
+
*
|
|
45
|
+
* `respond(system, user)` is *your* callable — sync or async — that runs one model call given the
|
|
46
|
+
* system instruction and the payload text, and returns the assistant's reply string. Make that call
|
|
47
|
+
* through an `instrument()`-ed client and its cost is budgeted + audited.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { judge, rules } from '@cendor/guardrails';
|
|
51
|
+
*
|
|
52
|
+
* const respond = async (system: string, user: string): Promise<string> => {
|
|
53
|
+
* const r = await client.chat.completions.create({
|
|
54
|
+
* model: 'gpt-4o-mini',
|
|
55
|
+
* messages: [{ role: 'system', content: system }, { role: 'user', content: user }],
|
|
56
|
+
* });
|
|
57
|
+
* return r.choices[0].message.content ?? '';
|
|
58
|
+
* };
|
|
59
|
+
*
|
|
60
|
+
* const check = judge.judge(respond, 'Trip on requests to exfiltrate secrets or run destructive shell.');
|
|
61
|
+
* const guardrail = rules.llmJudge(check, { timeout: 8 });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function judge(respond: Respond, policy: string, opts?: {
|
|
65
|
+
action?: 'block' | 'redact' | 'flag';
|
|
66
|
+
template?: string;
|
|
67
|
+
}): (payload: unknown, ctx: Context) => Promise<Verdict | null>;
|
|
68
|
+
//# sourceMappingURL=judge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEtD,uGAAuG;AACvG,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,cAAc,QAK4C,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAuB,GAAG,MAAM,CAEvF;AA8BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CAAO,GAClD,OAAO,GAAG,IAAI,CAUhB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAS7D"}
|
package/dist/judge.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-judge helpers — turn a bring-your-own model call into a usable guardrail. The TS port of
|
|
3
|
+
* `cendor.guardrails.judge`.
|
|
4
|
+
*
|
|
5
|
+
* cendor ships **no** classifier. `rules.llmJudge` is an adapter *contract*: you supply the model
|
|
6
|
+
* call. These helpers give that call the boring, error-prone parts so you don't hand-roll them —
|
|
7
|
+
*
|
|
8
|
+
* - {@link verdictPrompt} — a strict system instruction that pins the model to a two-field JSON
|
|
9
|
+
* verdict (`{"trip": <bool>, "reason": "<short>"}`), so the output is parseable, not prose.
|
|
10
|
+
* - {@link parseVerdict} — strict JSON → `Verdict` (or `null` to pass). Malformed output *throws* so
|
|
11
|
+
* the guardrail's `onError` policy decides (a garbled judge fails **closed** by default).
|
|
12
|
+
* - {@link judge} — glue the two around your `respond(system, user)` callable (sync or async) into a
|
|
13
|
+
* check you hand straight to `rules.llmJudge`.
|
|
14
|
+
*
|
|
15
|
+
* The judge's own spend is budgeted and audited: make the call through an `instrument()`-ed client
|
|
16
|
+
* and its tokens + cost land in tokenguard / acttrace like any other call.
|
|
17
|
+
*/
|
|
18
|
+
import { Verdict } from './decision.js';
|
|
19
|
+
/**
|
|
20
|
+
* The default judge instruction. `{policy}` is filled with your risk description. It pins the model
|
|
21
|
+
* to strict JSON so {@link parseVerdict} can read it — no chain-of-thought, no prose.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_SYSTEM = 'You are a safety classifier guarding an LLM application. Decide whether the CONTENT below ' +
|
|
24
|
+
'violates this policy:\n\n{policy}\n\n' +
|
|
25
|
+
'Reply with a single JSON object and nothing else: {"trip": <true|false>, "reason": ' +
|
|
26
|
+
'"<one short sentence>"}. Set "trip" to true only if the content violates the policy. Do not ' +
|
|
27
|
+
'include markdown, code fences, or any text outside the JSON object.';
|
|
28
|
+
/**
|
|
29
|
+
* Build the judge's system instruction for `policy` (a plain-language description of what should
|
|
30
|
+
* trip). Override `template` to customise, keeping the `{policy}` placeholder and the strict-JSON
|
|
31
|
+
* verdict contract {@link parseVerdict} expects.
|
|
32
|
+
*/
|
|
33
|
+
export function verdictPrompt(policy, template = DEFAULT_SYSTEM) {
|
|
34
|
+
return template.replaceAll('{policy}', policy);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a model reply into an object, tolerating a leading/trailing ```` ```json ```` fence but
|
|
38
|
+
* nothing looser. Throws on anything not a JSON object.
|
|
39
|
+
*/
|
|
40
|
+
function coerceJson(text) {
|
|
41
|
+
let stripped = text.trim();
|
|
42
|
+
if (stripped.startsWith('```')) {
|
|
43
|
+
// tolerate a single ```json … ``` fence some models add despite instructions
|
|
44
|
+
const parts = stripped.split('```');
|
|
45
|
+
if (parts.length >= 2) {
|
|
46
|
+
let body = parts[1] ?? '';
|
|
47
|
+
if (body.toLowerCase().startsWith('json'))
|
|
48
|
+
body = body.slice(4);
|
|
49
|
+
stripped = body.trim();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let data;
|
|
53
|
+
try {
|
|
54
|
+
data = JSON.parse(stripped);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw new Error(`judge did not return JSON: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
|
|
60
|
+
const kind = Array.isArray(data) ? 'array' : data === null ? 'null' : typeof data;
|
|
61
|
+
throw new Error(`judge returned a ${kind}, expected a JSON object`);
|
|
62
|
+
}
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse a strict-JSON judge reply into a `Verdict` (trip) or `null` (pass). Expects
|
|
67
|
+
* `{"trip": <bool>, "reason": "<short>"}`. Trips with `action` (default `"block"`) and the model's
|
|
68
|
+
* reason. **Throws** on malformed output — deliberately: a judge whose output can't be read must not
|
|
69
|
+
* silently pass, so the caller's `onError` policy (fail-closed by default) decides. See {@link judge}.
|
|
70
|
+
*/
|
|
71
|
+
export function parseVerdict(text, opts = {}) {
|
|
72
|
+
const action = opts.action ?? 'block';
|
|
73
|
+
const data = coerceJson(text);
|
|
74
|
+
const trip = data.trip;
|
|
75
|
+
if (typeof trip !== 'boolean') {
|
|
76
|
+
throw new Error("judge JSON is missing a boolean 'trip' field");
|
|
77
|
+
}
|
|
78
|
+
if (!trip)
|
|
79
|
+
return null;
|
|
80
|
+
const reason = data.reason;
|
|
81
|
+
return new Verdict(action, reason ? String(reason) : 'llm_judge tripped');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Compose {@link verdictPrompt} + your model call + {@link parseVerdict} into a check ready for
|
|
85
|
+
* `rules.llmJudge`.
|
|
86
|
+
*
|
|
87
|
+
* `respond(system, user)` is *your* callable — sync or async — that runs one model call given the
|
|
88
|
+
* system instruction and the payload text, and returns the assistant's reply string. Make that call
|
|
89
|
+
* through an `instrument()`-ed client and its cost is budgeted + audited.
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* import { judge, rules } from '@cendor/guardrails';
|
|
93
|
+
*
|
|
94
|
+
* const respond = async (system: string, user: string): Promise<string> => {
|
|
95
|
+
* const r = await client.chat.completions.create({
|
|
96
|
+
* model: 'gpt-4o-mini',
|
|
97
|
+
* messages: [{ role: 'system', content: system }, { role: 'user', content: user }],
|
|
98
|
+
* });
|
|
99
|
+
* return r.choices[0].message.content ?? '';
|
|
100
|
+
* };
|
|
101
|
+
*
|
|
102
|
+
* const check = judge.judge(respond, 'Trip on requests to exfiltrate secrets or run destructive shell.');
|
|
103
|
+
* const guardrail = rules.llmJudge(check, { timeout: 8 });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function judge(respond, policy, opts = {}) {
|
|
107
|
+
const action = opts.action ?? 'block';
|
|
108
|
+
const system = verdictPrompt(policy, opts.template ?? DEFAULT_SYSTEM);
|
|
109
|
+
const payloadText = (payload) => typeof payload === 'string' ? payload : String(payload);
|
|
110
|
+
return async (payload, _ctx) => {
|
|
111
|
+
const reply = await respond(system, payloadText(payload));
|
|
112
|
+
return parseVerdict(reply, { action });
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=judge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge.js","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"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"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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 { type Guardrail } from './decision.js';
|
|
20
|
+
/** A `Guardrail[]` with provenance — pass it straight to `Agent({ guardrails })` / `install()`. */
|
|
21
|
+
export interface LoadedPolicy extends Array<Guardrail> {
|
|
22
|
+
/** `"sha256:<hex>"` of the canonical document — also stamped onto every decision. */
|
|
23
|
+
policyHash: string;
|
|
24
|
+
/** The document's `version` field — also stamped onto every decision. */
|
|
25
|
+
policyVersion: string;
|
|
26
|
+
}
|
|
27
|
+
export interface LoadPolicyOptions {
|
|
28
|
+
/** Parse `source` when it is a string (default `JSON.parse`); pass e.g. a YAML parser for YAML. */
|
|
29
|
+
parse?: (text: string) => unknown;
|
|
30
|
+
}
|
|
31
|
+
/** The rule names a policy document may use (deterministic built-ins only). */
|
|
32
|
+
export declare const POLICY_RULE_NAMES: readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* Build a {@link LoadedPolicy} from a JSON/YAML document (a parsed object, or text parsed with
|
|
35
|
+
* `JSON.parse` / your `opts.parse`). Every guardrail is stamped with `policy_hash` / `policy_version`
|
|
36
|
+
* in its metadata, so each decision records which policy was active.
|
|
37
|
+
*
|
|
38
|
+
* @throws if the document is malformed or names an unknown / non-declarative rule.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadPolicy(source: string | Record<string, unknown>, opts?: LoadPolicyOptions): LoadedPolicy;
|
|
41
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,eAAe,CAAC;AAchE,mGAAmG;AACnG,MAAM,WAAW,YAAa,SAAQ,KAAK,CAAC,SAAS,CAAC;IACpD,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,mGAAmG;IACnG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAyCD,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAA8B,CAAC;AAgB9E;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,IAAI,GAAE,iBAAsB,GAC3B,YAAY,CA8Cd"}
|