@silmaril-security/sdk 0.1.1 → 0.1.3
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 +30 -0
- package/dist/index.cjs +97 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +97 -28
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -86,6 +86,8 @@ const { text } = await generateText({ model, prompt: "Hello" });
|
|
|
86
86
|
|
|
87
87
|
Middleware is **fail-closed** by default: classification errors bubble up to the caller, matching Vercel AI SDK convention.
|
|
88
88
|
|
|
89
|
+
When `scanOutput: true` is combined with streaming, output is classified in the stream's `flush` after all deltas have been emitted, so blocking is advisory — the consumer has already seen the text by the time an error part is enqueued. Use non-streaming generation if you need to block before the caller observes output.
|
|
90
|
+
|
|
89
91
|
## LangChain.js callback handler
|
|
90
92
|
|
|
91
93
|
```ts
|
|
@@ -119,3 +121,31 @@ Pipeline-stage-aware classification. The model uses `[HOOK:<label>]` and `[TOOL:
|
|
|
119
121
|
| `LLM_OUTPUT` | LLM end |
|
|
120
122
|
|
|
121
123
|
Per-hook threshold overrides via `hookThresholds: { tool_response: 0.3 }`.
|
|
124
|
+
|
|
125
|
+
## Shadow mode
|
|
126
|
+
|
|
127
|
+
Set `shadowMode: true` on the `Firewall` client to turn every downstream adapter into observation-only. The firewall still classifies every hook and still computes the would-block decision against its thresholds, but the adapters suppress `PromptBlockedException` so live traffic is unaffected. Use it to roll the firewall out against real traffic and tune thresholds before committing to enforcement.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const firewall = new Firewall({
|
|
131
|
+
apiKey: process.env.SILMARIL_API_KEY!,
|
|
132
|
+
apiUrl: process.env.SILMARIL_API_URL!,
|
|
133
|
+
shadowMode: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const model = wrapLanguageModel({
|
|
137
|
+
model: openai("gpt-4o-mini"),
|
|
138
|
+
middleware: firewall.asMiddleware({
|
|
139
|
+
scanOutput: true,
|
|
140
|
+
onClassify: ({ hook, result, blocked, shadowMode }) => {
|
|
141
|
+
if (blocked) {
|
|
142
|
+
metrics.increment("firewall.would_block", { hook, shadow: String(shadowMode) });
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Adapter-level `shadowMode` overrides the firewall-level value, so you can enforce on some adapters and shadow on others (for example shadow on `asMiddleware` while enforcing on `asLangChainHandler`). The direct `classify()` / `classifyBatch()` calls never throw on verdicts and are unaffected by the flag.
|
|
150
|
+
|
|
151
|
+
The exported `prependHook` / `prependToolName` helpers mirror the server-side tokenization for offline use (evaluation harnesses, parity checks). You do **not** need to call them before `classify()` — the server prepends `[HOOK:<label>]` and `[TOOL:<name>]` from the `hook` and `tool_name` payload fields.
|
package/dist/index.cjs
CHANGED
|
@@ -151,10 +151,10 @@ var init_hooks = __esm({
|
|
|
151
151
|
};
|
|
152
152
|
DEFAULT_HOOK_THRESHOLDS = {
|
|
153
153
|
[HookLabel.USER_INPUT]: 0.5,
|
|
154
|
-
[HookLabel.SYSTEM_PROMPT]: 0.
|
|
155
|
-
[HookLabel.TOOL_CALL]: 0.
|
|
156
|
-
[HookLabel.TOOL_RESPONSE]: 0.
|
|
157
|
-
[HookLabel.LLM_OUTPUT]: 0.
|
|
154
|
+
[HookLabel.SYSTEM_PROMPT]: 0.5,
|
|
155
|
+
[HookLabel.TOOL_CALL]: 0.5,
|
|
156
|
+
[HookLabel.TOOL_RESPONSE]: 0.5,
|
|
157
|
+
[HookLabel.LLM_OUTPUT]: 0.5,
|
|
158
158
|
[HookLabel.UNKNOWN]: 0.5
|
|
159
159
|
};
|
|
160
160
|
FIREWALL_HOOK_VALUES = new Set(Object.values(FirewallHook));
|
|
@@ -245,6 +245,8 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
245
245
|
});
|
|
246
246
|
const overrideThreshold = options.threshold;
|
|
247
247
|
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
248
|
+
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
249
|
+
const onClassify = options.onClassify;
|
|
248
250
|
const effectiveThreshold = (label) => {
|
|
249
251
|
if (overrideHookThresholds[label] !== void 0) {
|
|
250
252
|
return overrideHookThresholds[label];
|
|
@@ -254,29 +256,47 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
254
256
|
}
|
|
255
257
|
return overrideThreshold ?? firewall.threshold;
|
|
256
258
|
};
|
|
259
|
+
const fireOnClassify = (hookLabel, text, result, blocked, toolName) => {
|
|
260
|
+
if (!onClassify) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
onClassify({
|
|
265
|
+
hook: hookLabel,
|
|
266
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
267
|
+
text,
|
|
268
|
+
result,
|
|
269
|
+
blocked,
|
|
270
|
+
shadowMode
|
|
271
|
+
});
|
|
272
|
+
} catch (err) {
|
|
273
|
+
logger("onClassify callback threw", err);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
257
276
|
const classify = async (text, hookLabel, runId, toolName) => {
|
|
277
|
+
let result;
|
|
258
278
|
try {
|
|
259
|
-
|
|
279
|
+
result = await firewall.classify(text, {
|
|
260
280
|
hook: hookLabel,
|
|
261
281
|
...toolName !== void 0 ? { toolName } : {}
|
|
262
282
|
});
|
|
263
|
-
const threshold = effectiveThreshold(hookLabel);
|
|
264
|
-
if (result.score >= threshold) {
|
|
265
|
-
throw new PromptBlockedException({
|
|
266
|
-
score: result.score,
|
|
267
|
-
threshold,
|
|
268
|
-
promptText: text,
|
|
269
|
-
runId
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
283
|
} catch (err) {
|
|
273
|
-
if (err instanceof PromptBlockedException) {
|
|
274
|
-
throw err;
|
|
275
|
-
}
|
|
276
284
|
if (!failOpen) {
|
|
277
285
|
throw err;
|
|
278
286
|
}
|
|
279
287
|
logger("classification failed, allowing prompt through", err);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const threshold = effectiveThreshold(hookLabel);
|
|
291
|
+
const blocked = result.score >= threshold;
|
|
292
|
+
fireOnClassify(hookLabel, text, result, blocked, toolName);
|
|
293
|
+
if (blocked && !shadowMode) {
|
|
294
|
+
throw new PromptBlockedException({
|
|
295
|
+
score: result.score,
|
|
296
|
+
threshold,
|
|
297
|
+
promptText: text,
|
|
298
|
+
runId
|
|
299
|
+
});
|
|
280
300
|
}
|
|
281
301
|
};
|
|
282
302
|
class SilmarilFirewallHandler extends BaseCallbackHandler {
|
|
@@ -323,8 +343,7 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
323
343
|
return;
|
|
324
344
|
}
|
|
325
345
|
const toolName = tool?.name;
|
|
326
|
-
|
|
327
|
-
await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);
|
|
346
|
+
await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);
|
|
328
347
|
}
|
|
329
348
|
async handleRetrieverStart(_retriever, query, runId) {
|
|
330
349
|
if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {
|
|
@@ -355,8 +374,7 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
355
374
|
return;
|
|
356
375
|
}
|
|
357
376
|
const toolName = _kwargs?.name;
|
|
358
|
-
|
|
359
|
-
await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);
|
|
377
|
+
await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);
|
|
360
378
|
}
|
|
361
379
|
async handleRetrieverEnd(documents, runId) {
|
|
362
380
|
if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {
|
|
@@ -473,6 +491,7 @@ function createMiddleware(firewall, options = {}) {
|
|
|
473
491
|
const scanOutput = options.scanOutput ?? false;
|
|
474
492
|
const overrideThreshold = options.threshold;
|
|
475
493
|
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
494
|
+
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
476
495
|
const effectiveThreshold = (label) => {
|
|
477
496
|
if (overrideHookThresholds[label] !== void 0) {
|
|
478
497
|
return overrideHookThresholds[label];
|
|
@@ -497,9 +516,10 @@ function createMiddleware(firewall, options = {}) {
|
|
|
497
516
|
...toolName !== void 0 ? { toolName } : {},
|
|
498
517
|
text,
|
|
499
518
|
result,
|
|
500
|
-
blocked
|
|
519
|
+
blocked,
|
|
520
|
+
shadowMode
|
|
501
521
|
});
|
|
502
|
-
if (blocked) {
|
|
522
|
+
if (blocked && !shadowMode) {
|
|
503
523
|
const err = new PromptBlockedException({
|
|
504
524
|
score: result.score,
|
|
505
525
|
threshold,
|
|
@@ -639,12 +659,38 @@ var DEFAULT_THRESHOLD = 0.5;
|
|
|
639
659
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
640
660
|
var DEFAULT_MAX_RETRIES = 5;
|
|
641
661
|
var MAX_BACKOFF_SECONDS = 30;
|
|
662
|
+
function blockResultFromResponse(data) {
|
|
663
|
+
const result = {
|
|
664
|
+
prediction: data.prediction,
|
|
665
|
+
score: Number(data.score)
|
|
666
|
+
};
|
|
667
|
+
if (data.primary_outcome !== void 0) {
|
|
668
|
+
result.primaryOutcome = data.primary_outcome;
|
|
669
|
+
}
|
|
670
|
+
if (data.outcome_scores !== void 0) {
|
|
671
|
+
result.outcomeScores = Object.freeze(
|
|
672
|
+
Object.fromEntries(Object.entries(data.outcome_scores).map(([k, v]) => [k, Number(v)]))
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (data.detector_scores !== void 0) {
|
|
676
|
+
result.detectorScores = Object.freeze(
|
|
677
|
+
Object.fromEntries(Object.entries(data.detector_scores).map(([k, v]) => [k, Number(v)]))
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
if (data.detector_counts !== void 0) {
|
|
681
|
+
result.detectorCounts = Object.freeze(
|
|
682
|
+
Object.fromEntries(Object.entries(data.detector_counts).map(([k, v]) => [k, Number(v)]))
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
return Object.freeze(result);
|
|
686
|
+
}
|
|
642
687
|
var Firewall = class {
|
|
643
688
|
apiKey;
|
|
644
689
|
apiUrl;
|
|
645
690
|
threshold;
|
|
646
691
|
timeoutMs;
|
|
647
692
|
hookThresholds;
|
|
693
|
+
shadowMode;
|
|
648
694
|
headers;
|
|
649
695
|
constructor(options) {
|
|
650
696
|
if (!options.apiKey) {
|
|
@@ -657,7 +703,8 @@ var Firewall = class {
|
|
|
657
703
|
this.apiUrl = options.apiUrl;
|
|
658
704
|
this.threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
659
705
|
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
660
|
-
this.hookThresholds = options.hookThresholds ?? {};
|
|
706
|
+
this.hookThresholds = Object.freeze({ ...options.hookThresholds ?? {} });
|
|
707
|
+
this.shadowMode = options.shadowMode ?? false;
|
|
661
708
|
this.headers = Object.freeze({
|
|
662
709
|
"x-api-key": this.apiKey,
|
|
663
710
|
"content-type": "application/json"
|
|
@@ -673,8 +720,11 @@ var Firewall = class {
|
|
|
673
720
|
if (options.toolName !== void 0) {
|
|
674
721
|
payload.tool_name = options.toolName;
|
|
675
722
|
}
|
|
723
|
+
if (options.metadata !== void 0) {
|
|
724
|
+
payload.metadata = options.metadata;
|
|
725
|
+
}
|
|
676
726
|
const data = await this.postWithRetry(payload);
|
|
677
|
-
return
|
|
727
|
+
return blockResultFromResponse(data);
|
|
678
728
|
}
|
|
679
729
|
const batchOpts = {};
|
|
680
730
|
if (options.hook !== void 0) {
|
|
@@ -683,10 +733,28 @@ var Firewall = class {
|
|
|
683
733
|
if (options.toolName !== void 0) {
|
|
684
734
|
batchOpts.toolNames = chunks.map(() => options.toolName);
|
|
685
735
|
}
|
|
736
|
+
if (options.metadata !== void 0) {
|
|
737
|
+
batchOpts.metadata = chunks.map(() => options.metadata);
|
|
738
|
+
}
|
|
686
739
|
const results = await this.classifyBatch(chunks, batchOpts);
|
|
687
740
|
return results.reduce((best, r) => r.score > best.score ? r : best);
|
|
688
741
|
}
|
|
689
742
|
async classifyBatch(texts, options = {}) {
|
|
743
|
+
if (options.hooks !== void 0 && options.hooks.length !== texts.length) {
|
|
744
|
+
throw new Error(
|
|
745
|
+
`Firewall: hooks length ${options.hooks.length} does not match texts length ${texts.length}`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
if (options.toolNames !== void 0 && options.toolNames.length !== texts.length) {
|
|
749
|
+
throw new Error(
|
|
750
|
+
`Firewall: toolNames length ${options.toolNames.length} does not match texts length ${texts.length}`
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
if (options.metadata !== void 0 && options.metadata.length !== texts.length) {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`Firewall: metadata length ${options.metadata.length} does not match texts length ${texts.length}`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
690
758
|
const payload = { texts };
|
|
691
759
|
if (options.hooks && options.hooks.length > 0) {
|
|
692
760
|
payload.hooks = options.hooks.map((h) => String(h));
|
|
@@ -694,10 +762,11 @@ var Firewall = class {
|
|
|
694
762
|
if (options.toolNames && options.toolNames.length > 0) {
|
|
695
763
|
payload.tool_names = options.toolNames.map((t) => t === void 0 ? null : t);
|
|
696
764
|
}
|
|
765
|
+
if (options.metadata && options.metadata.length > 0) {
|
|
766
|
+
payload.metadata = options.metadata.map((m) => m === void 0 ? null : m);
|
|
767
|
+
}
|
|
697
768
|
const data = await this.postWithRetry(payload);
|
|
698
|
-
return data.predictions.map(
|
|
699
|
-
(p) => Object.freeze({ prediction: p.prediction, score: Number(p.score) })
|
|
700
|
-
);
|
|
769
|
+
return data.predictions.map((p) => blockResultFromResponse(p));
|
|
701
770
|
}
|
|
702
771
|
asLangChainHandler(options = {}) {
|
|
703
772
|
return Promise.resolve().then(() => (init_langchain(), langchain_exports)).then((m) => m.createLangChainHandler(this, options));
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/exceptions.ts","../src/hooks.ts","../src/utils/extract.ts","../src/adapters/langchain.ts","../src/index.ts","../src/adapters/vercel.ts","../src/chunking.ts","../src/firewall.ts"],"sourcesContent":["// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst MAX_PROMPT_DISPLAY_LEN = 100;\n\nexport class PromptBlockedException extends Error {\n readonly score: number;\n readonly threshold: number;\n readonly promptText: string;\n readonly runId: string | undefined;\n\n constructor(params: {\n score: number;\n threshold: number;\n promptText: string;\n runId?: string;\n }) {\n super(PromptBlockedException.formatMessage(params));\n this.name = \"PromptBlockedException\";\n this.score = params.score;\n this.threshold = params.threshold;\n this.promptText = params.promptText;\n this.runId = params.runId;\n Object.setPrototypeOf(this, PromptBlockedException.prototype);\n }\n\n private static formatMessage(params: {\n score: number;\n threshold: number;\n promptText: string;\n }): string {\n const truncated =\n params.promptText.length > MAX_PROMPT_DISPLAY_LEN\n ? `${params.promptText.slice(0, MAX_PROMPT_DISPLAY_LEN)}...`\n : params.promptText;\n return (\n `Prompt blocked by Silmaril Firewall ` +\n `(score=${params.score.toFixed(4)}, threshold=${params.threshold.toFixed(4)}): ` +\n `'${truncated}'`\n );\n }\n}\n\nexport class SilmarilApiError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly body: string;\n\n constructor(params: { status: number; statusText: string; body: string }) {\n super(`Silmaril API error ${params.status} ${params.statusText}: ${params.body}`);\n this.name = \"SilmarilApiError\";\n this.status = params.status;\n this.statusText = params.statusText;\n this.body = params.body;\n Object.setPrototypeOf(this, SilmarilApiError.prototype);\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport const HookLabel = {\n USER_INPUT: \"user_input\",\n SYSTEM_PROMPT: \"system_prompt\",\n TOOL_CALL: \"tool_call\",\n TOOL_RESPONSE: \"tool_response\",\n LLM_OUTPUT: \"llm_output\",\n UNKNOWN: \"unknown\",\n} as const;\n\nexport type HookLabel = (typeof HookLabel)[keyof typeof HookLabel];\n\nexport const FirewallHook = {\n LLM_START: \"on_llm_start\",\n CHAT_MODEL_START: \"on_chat_model_start\",\n TOOL_START: \"on_tool_start\",\n RETRIEVER_START: \"on_retriever_start\",\n LLM_END: \"on_llm_end\",\n TOOL_END: \"on_tool_end\",\n RETRIEVER_END: \"on_retriever_end\",\n} as const;\n\nexport type FirewallHook = (typeof FirewallHook)[keyof typeof FirewallHook];\n\nexport const DEFAULT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n]);\n\nexport const INPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.RETRIEVER_START,\n]);\n\nexport const OUTPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_END,\n FirewallHook.TOOL_END,\n FirewallHook.RETRIEVER_END,\n]);\n\nexport const ALL_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n ...INPUT_HOOKS,\n ...OUTPUT_HOOKS,\n]);\n\nexport const FIREWALL_HOOK_TO_LABEL: Readonly<Record<FirewallHook, HookLabel>> = {\n [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,\n [FirewallHook.LLM_START]: HookLabel.USER_INPUT,\n [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,\n [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,\n [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT,\n};\n\nexport const DEFAULT_HOOK_THRESHOLDS: Readonly<Record<HookLabel, number>> = {\n [HookLabel.USER_INPUT]: 0.5,\n [HookLabel.SYSTEM_PROMPT]: 0.3,\n [HookLabel.TOOL_CALL]: 0.4,\n [HookLabel.TOOL_RESPONSE]: 0.3,\n [HookLabel.LLM_OUTPUT]: 0.4,\n [HookLabel.UNKNOWN]: 0.5,\n};\n\nconst FIREWALL_HOOK_VALUES: ReadonlySet<string> = new Set(Object.values(FirewallHook));\n\nexport function resolveHooks(\n hooks: Iterable<FirewallHook | string> | undefined,\n): ReadonlySet<FirewallHook> {\n if (hooks === undefined) {\n return DEFAULT_HOOKS;\n }\n const resolved = new Set<FirewallHook>();\n for (const h of hooks) {\n if (!FIREWALL_HOOK_VALUES.has(h)) {\n throw new Error(`Invalid FirewallHook value: ${String(h)}`);\n }\n resolved.add(h as FirewallHook);\n }\n return resolved;\n}\n\nexport function prependHook(text: string, hook: HookLabel | string | undefined | null): string {\n if (hook === undefined || hook === null) {\n return text;\n }\n if (hook === HookLabel.UNKNOWN) {\n return text;\n }\n return `[HOOK:${hook}] ${text}`;\n}\n\nexport function prependToolName(text: string, toolName: string | undefined | null): string {\n if (toolName === undefined || toolName === null) {\n return text;\n }\n return `[TOOL:${toolName}] ${text}`;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst SKIP_ROLES: ReadonlySet<string> = new Set([\"ai\", \"assistant\"]);\nconst SYSTEM_ROLES: ReadonlySet<string> = new Set([\"system\"]);\nconst TOOL_ROLES: ReadonlySet<string> = new Set([\"tool\", \"function\"]);\n\ntype MessageContent = string | ReadonlyArray<string | { type?: string; text?: string }>;\n\ninterface DucktypedMessage {\n role?: string;\n type?: string;\n content?: MessageContent;\n}\n\ninterface DucktypedGeneration {\n text?: string;\n}\n\ninterface DucktypedLLMResult {\n generations?: ReadonlyArray<ReadonlyArray<DucktypedGeneration>>;\n}\n\ninterface DucktypedDocument {\n pageContent?: string;\n}\n\nfunction extractContentText(content: MessageContent | undefined): string {\n if (typeof content === \"string\") {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && typeof block === \"object\" && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n }\n return \"\";\n}\n\nfunction getRole(message: DucktypedMessage): string {\n if (message.role !== undefined) {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nexport function extractTextFromMessages(\n messages: ReadonlyArray<DucktypedMessage>,\n options: { includeSystem?: boolean; includeTool?: boolean } = {},\n): string {\n const includeSystem = options.includeSystem ?? true;\n const includeTool = options.includeTool ?? true;\n const parts: string[] = [];\n for (const message of messages) {\n const role = getRole(message);\n if (SKIP_ROLES.has(role)) {\n continue;\n }\n if (!includeSystem && SYSTEM_ROLES.has(role)) {\n continue;\n }\n if (!includeTool && TOOL_ROLES.has(role)) {\n continue;\n }\n const text = extractContentText(message.content);\n const stripped = text.trim();\n if (stripped) {\n parts.push(stripped);\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromPrompts(prompts: readonly string[]): string {\n return prompts\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n .join(\"\\n\");\n}\n\nexport function extractTextFromToolInput(inputStr: string): string {\n return inputStr.trim();\n}\n\nexport function extractTextFromLLMResult(response: DucktypedLLMResult): string {\n const parts: string[] = [];\n for (const genList of response.generations ?? []) {\n for (const gen of genList) {\n const text = (gen.text ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromDocuments(documents: ReadonlyArray<DucktypedDocument>): string {\n const parts: string[] = [];\n for (const doc of documents) {\n const text = (doc.pageContent ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n return parts.join(\"\\n\");\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport {\n FIREWALL_HOOK_TO_LABEL,\n FirewallHook,\n HookLabel,\n prependToolName,\n resolveHooks,\n} from \"../hooks.js\";\nimport type { LangChainAdapterOptions } from \"../types.js\";\nimport {\n extractTextFromDocuments,\n extractTextFromLLMResult,\n extractTextFromPrompts,\n extractTextFromToolInput,\n} from \"../utils/extract.js\";\n\nconst USER_ROLES: ReadonlySet<string> = new Set([\"human\", \"user\"]);\n\ninterface LangChainDucktypedMessage {\n role?: string;\n type?: string;\n content?: string | ReadonlyArray<string | { type?: string; text?: string }>;\n}\n\nfunction getMessageRole(message: LangChainDucktypedMessage): string {\n if (typeof message.role === \"string\") {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nfunction extractMessageText(content: LangChainDucktypedMessage[\"content\"]): string {\n if (content === undefined) {\n return \"\";\n }\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<LangChainDucktypedMessage>,\n): LangChainDucktypedMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (USER_ROLES.has(getMessageRole(msg))) {\n return msg;\n }\n }\n return undefined;\n}\n\nexport async function createLangChainHandler(\n firewall: Firewall,\n options: LangChainAdapterOptions = {},\n): Promise<unknown> {\n const { BaseCallbackHandler } = await import(\"@langchain/core/callbacks/base\");\n\n const enabledHooks = resolveHooks(options.hooks);\n // includeSystem / includeTool are retained in LangChainAdapterOptions for\n // backwards compat but are inert under the per-message hook-routing algorithm:\n // handleChatModelStart only scans the last user-role message; system and tool\n // messages are either trusted or already classified by their own hooks.\n const failOpen = options.failOpen ?? true;\n const logger =\n options.logger ??\n ((message: string, error: unknown): void => {\n // eslint-disable-next-line no-console\n console.warn(`silmaril.firewall: ${message}`, error);\n });\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classify = async (\n text: string,\n hookLabel: HookLabel,\n runId: string,\n toolName?: string,\n ): Promise<void> => {\n try {\n const result = await firewall.classify(text, {\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n });\n const threshold = effectiveThreshold(hookLabel);\n if (result.score >= threshold) {\n throw new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n runId,\n });\n }\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n throw err;\n }\n if (!failOpen) {\n throw err;\n }\n logger(\"classification failed, allowing prompt through\", err);\n }\n };\n\n class SilmarilFirewallHandler extends BaseCallbackHandler {\n override name = \"silmaril_firewall_handler\";\n override raiseError = true;\n override awaitHandlers = true;\n\n override async handleChatModelStart(\n _llm: unknown,\n messages: unknown,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {\n return;\n }\n const batches = messages as ReadonlyArray<ReadonlyArray<LangChainDucktypedMessage>>;\n const flat: LangChainDucktypedMessage[] = [];\n for (const batch of batches) {\n for (const m of batch) {\n flat.push(m);\n }\n }\n const lastUser = findLastUserMessage(flat);\n if (!lastUser) {\n return;\n }\n const text = extractMessageText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);\n }\n\n override async handleLLMStart(\n _llm: unknown,\n prompts: string[],\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_START)) {\n return;\n }\n const text = extractTextFromPrompts(prompts);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);\n }\n\n override async handleToolStart(\n tool: { name?: string } | undefined,\n inputStr: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_START)) {\n return;\n }\n const text = extractTextFromToolInput(inputStr);\n if (!text) {\n return;\n }\n const toolName = tool?.name;\n const prefixed = prependToolName(text, toolName);\n await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);\n }\n\n override async handleRetrieverStart(\n _retriever: unknown,\n query: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {\n return;\n }\n const text = query.trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);\n }\n\n override async handleLLMEnd(output: unknown, runId: string): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_END)) {\n return;\n }\n const text = extractTextFromLLMResult(output as { generations?: ReadonlyArray<ReadonlyArray<{ text?: string }>> });\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);\n }\n\n override async handleToolEnd(\n output: unknown,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _kwargs?: { name?: string },\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_END)) {\n return;\n }\n const text = String(output).trim();\n if (!text) {\n return;\n }\n const toolName = _kwargs?.name;\n const prefixed = prependToolName(text, toolName);\n await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);\n }\n\n override async handleRetrieverEnd(\n documents: ReadonlyArray<{ pageContent?: string }>,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {\n return;\n }\n const text = extractTextFromDocuments(documents);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);\n }\n }\n\n return new SilmarilFirewallHandler();\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport {\n Firewall,\n DEFAULT_THRESHOLD,\n DEFAULT_TIMEOUT_MS,\n} from \"./firewall.js\";\n\nexport { PromptBlockedException, SilmarilApiError } from \"./exceptions.js\";\n\nexport type { FirewallMiddleware } from \"./adapters/vercel.js\";\n\nexport {\n chunkText,\n MAX_INPUT_TOKENS,\n CHUNK_WINDOW,\n CHUNK_OVERLAP,\n MAX_INPUT_CHARS,\n CHUNK_WINDOW_CHARS,\n CHUNK_OVERLAP_CHARS,\n} from \"./chunking.js\";\n\nexport {\n HookLabel,\n FirewallHook,\n DEFAULT_HOOKS,\n INPUT_HOOKS,\n OUTPUT_HOOKS,\n ALL_HOOKS,\n DEFAULT_HOOK_THRESHOLDS,\n FIREWALL_HOOK_TO_LABEL,\n resolveHooks,\n prependHook,\n prependToolName,\n} from \"./hooks.js\";\n\nexport type {\n BlockResult,\n ClassifyEvent,\n Prediction,\n FirewallOptions,\n ClassifyOptions,\n ClassifyBatchOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n} from \"./types.js\";\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport { HookLabel } from \"../hooks.js\";\nimport type { BlockResult, MiddlewareOptions } from \"../types.js\";\n\ninterface VercelContentPart {\n type?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n}\n\ninterface VercelPromptMessage {\n role: string;\n content: string | ReadonlyArray<VercelContentPart | string>;\n}\n\ninterface VercelToolCall {\n toolCallType?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n}\n\ninterface VercelGenerateResult {\n text?: string;\n toolCalls?: ReadonlyArray<VercelToolCall>;\n}\n\ninterface WrapGenerateArgs<TResult extends VercelGenerateResult & Record<string, unknown>> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doGenerate: () => PromiseLike<TResult>;\n}\n\ninterface WrapStreamArgs<\n TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>,\n> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doStream: () => PromiseLike<TResult>;\n}\n\ninterface StreamPart {\n type?: string;\n textDelta?: string;\n delta?: string;\n}\n\nfunction stringifyToolValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n\nfunction extractContentText(content: VercelPromptMessage[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction iterateToolResultParts(\n message: VercelPromptMessage,\n): Array<{ text: string; toolName: string | undefined }> {\n if (typeof message.content === \"string\") {\n return [];\n }\n const out: Array<{ text: string; toolName: string | undefined }> = [];\n for (const part of message.content) {\n if (typeof part === \"string\") {\n continue;\n }\n if (part.type === \"tool-result\") {\n const text = stringifyToolValue(part.result);\n if (text.trim()) {\n out.push({ text, toolName: part.toolName });\n }\n }\n }\n return out;\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<VercelPromptMessage>,\n): VercelPromptMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role.toLowerCase() === \"user\") {\n return msg;\n }\n }\n return undefined;\n}\n\nexport interface FirewallMiddleware {\n wrapGenerate: <TResult extends VercelGenerateResult & Record<string, unknown>>(\n args: WrapGenerateArgs<TResult>,\n ) => Promise<TResult>;\n wrapStream: <TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>(\n args: WrapStreamArgs<TResult>,\n ) => Promise<TResult>;\n}\n\nexport function createMiddleware(\n firewall: Firewall,\n options: MiddlewareOptions = {},\n): FirewallMiddleware {\n const scanInput = options.scanInput ?? true;\n const scanOutput = options.scanOutput ?? false;\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classifyOrBlock = async (\n text: string,\n hook: HookLabel,\n toolName?: string,\n ): Promise<void> => {\n if (!text.trim()) {\n return;\n }\n const result: BlockResult = await firewall.classify(\n text,\n toolName !== undefined ? { hook, toolName } : { hook },\n );\n const threshold = effectiveThreshold(hook);\n const blocked = result.score >= threshold;\n options.onClassify?.({\n hook,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n });\n if (blocked) {\n const err = new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n });\n options.onBlocked?.(err);\n throw err;\n }\n };\n\n const scanPrompt = async (prompt: ReadonlyArray<VercelPromptMessage>): Promise<void> => {\n if (prompt.length === 0) {\n return;\n }\n const last = prompt[prompt.length - 1]!;\n const role = last.role.toLowerCase();\n\n // If the newest message is a tool result (typical mid-flow in a\n // tool-calling loop), classify each tool-result part individually with\n // tool_response + toolName. No manual wiring from the caller — the\n // toolName is read directly from the Vercel content part.\n if (role === \"tool\") {\n for (const { text, toolName } of iterateToolResultParts(last)) {\n await classifyOrBlock(text, HookLabel.TOOL_RESPONSE, toolName);\n }\n return;\n }\n\n // Otherwise, fall back to scanning the most recent user message.\n // Covers: [user], [system, user], [user, assistant(text), user], etc.\n const lastUser = findLastUserMessage(prompt);\n if (!lastUser) {\n return;\n }\n const text = extractContentText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classifyOrBlock(text, HookLabel.USER_INPUT);\n };\n\n const scanGenerateResult = async (result: VercelGenerateResult): Promise<void> => {\n if (scanOutput && typeof result.text === \"string\" && result.text.length > 0) {\n await classifyOrBlock(result.text, HookLabel.LLM_OUTPUT);\n }\n if (options.scanToolCalls && Array.isArray(result.toolCalls)) {\n for (const call of result.toolCalls) {\n if (!call || typeof call !== \"object\") {\n continue;\n }\n const args =\n typeof call.args === \"string\" ? call.args : stringifyToolValue(call.args);\n const toolName = typeof call.toolName === \"string\" ? call.toolName : undefined;\n if (args.trim()) {\n await classifyOrBlock(args, HookLabel.TOOL_CALL, toolName);\n }\n }\n }\n };\n\n return {\n async wrapGenerate<TResult extends VercelGenerateResult & Record<string, unknown>>({\n params,\n doGenerate,\n }: WrapGenerateArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const result = await doGenerate();\n await scanGenerateResult(result);\n return result;\n },\n\n async wrapStream<TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>({\n params,\n doStream,\n }: WrapStreamArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const { stream, ...rest } = await doStream();\n if (!scanOutput) {\n return { stream, ...rest } as unknown as TResult;\n }\n\n let buffered = \"\";\n const transformed = stream.pipeThrough(\n new TransformStream<unknown, unknown>({\n transform(chunk, controller): void {\n const part = chunk as StreamPart;\n if (part && part.type === \"text-delta\") {\n const delta = part.textDelta ?? part.delta ?? \"\";\n buffered += delta;\n }\n controller.enqueue(chunk);\n },\n async flush(controller): Promise<void> {\n if (!buffered.trim()) {\n return;\n }\n try {\n await classifyOrBlock(buffered, HookLabel.LLM_OUTPUT);\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n controller.enqueue({ type: \"error\", error: err });\n return;\n }\n throw err;\n }\n },\n }),\n );\n return { stream: transformed, ...rest } as unknown as TResult;\n },\n };\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst CHARS_PER_TOKEN = 4;\n\nexport const MAX_INPUT_TOKENS = 10_240;\nexport const CHUNK_WINDOW = 400;\nexport const CHUNK_OVERLAP = 64;\n\nexport const MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;\nexport const CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;\nexport const CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;\nconst CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;\n\nexport function chunkText(text: string): string[] {\n const n = text.length;\n if (n > MAX_INPUT_CHARS) {\n throw new Error(\n `Input has ~${Math.floor(n / CHARS_PER_TOKEN)} tokens (${n} chars); ` +\n `max is ${MAX_INPUT_TOKENS} tokens (${MAX_INPUT_CHARS} chars)`,\n );\n }\n if (n <= CHUNK_WINDOW_CHARS) {\n return [text];\n }\n const chunks: string[] = [];\n for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {\n const end = start + CHUNK_WINDOW_CHARS;\n chunks.push(text.slice(start, end));\n if (end >= n) {\n break;\n }\n }\n return chunks;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport { createMiddleware, type FirewallMiddleware } from \"./adapters/vercel.js\";\nimport { chunkText } from \"./chunking.js\";\nimport { SilmarilApiError } from \"./exceptions.js\";\nimport type { HookLabel } from \"./hooks.js\";\nimport type {\n BlockResult,\n ClassifyBatchOptions,\n ClassifyOptions,\n FirewallOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n Prediction,\n} from \"./types.js\";\n\nexport const DEFAULT_THRESHOLD = 0.5;\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RETRIES = 5;\nconst MAX_BACKOFF_SECONDS = 30;\n\ninterface SingleClassifyPayload {\n text: string;\n hook?: string;\n tool_name?: string;\n}\n\ninterface BatchClassifyPayload {\n texts: readonly string[];\n hooks?: readonly string[];\n tool_names?: readonly (string | null)[];\n}\n\ninterface SingleClassifyResponse {\n prediction: Prediction;\n score: number;\n}\n\ninterface BatchClassifyResponse {\n predictions: readonly SingleClassifyResponse[];\n}\n\nexport class Firewall {\n readonly apiKey: string;\n readonly apiUrl: string;\n readonly threshold: number;\n readonly timeoutMs: number;\n readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;\n\n private readonly headers: Readonly<Record<string, string>>;\n\n constructor(options: FirewallOptions) {\n if (!options.apiKey) {\n throw new Error(\"Firewall: apiKey is required\");\n }\n if (!options.apiUrl) {\n throw new Error(\"Firewall: apiUrl is required\");\n }\n this.apiKey = options.apiKey;\n this.apiUrl = options.apiUrl;\n this.threshold = options.threshold ?? DEFAULT_THRESHOLD;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.hookThresholds = options.hookThresholds ?? {};\n this.headers = Object.freeze({\n \"x-api-key\": this.apiKey,\n \"content-type\": \"application/json\",\n });\n }\n\n async classify(text: string, options: ClassifyOptions = {}): Promise<BlockResult> {\n const chunks = chunkText(text);\n if (chunks.length === 1) {\n const payload: SingleClassifyPayload = { text: chunks[0]! };\n if (options.hook !== undefined) {\n payload.hook = options.hook;\n }\n if (options.toolName !== undefined) {\n payload.tool_name = options.toolName;\n }\n const data = await this.postWithRetry<SingleClassifyResponse>(payload);\n return Object.freeze({ prediction: data.prediction, score: Number(data.score) });\n }\n const batchOpts: ClassifyBatchOptions = {};\n if (options.hook !== undefined) {\n batchOpts.hooks = chunks.map(() => options.hook!);\n }\n if (options.toolName !== undefined) {\n batchOpts.toolNames = chunks.map(() => options.toolName);\n }\n const results = await this.classifyBatch(chunks, batchOpts);\n return results.reduce((best, r) => (r.score > best.score ? r : best));\n }\n\n async classifyBatch(\n texts: readonly string[],\n options: ClassifyBatchOptions = {},\n ): Promise<BlockResult[]> {\n const payload: BatchClassifyPayload = { texts };\n if (options.hooks && options.hooks.length > 0) {\n payload.hooks = options.hooks.map((h) => String(h));\n }\n if (options.toolNames && options.toolNames.length > 0) {\n payload.tool_names = options.toolNames.map((t) => (t === undefined ? null : t));\n }\n const data = await this.postWithRetry<BatchClassifyResponse>(payload);\n return data.predictions.map((p) =>\n Object.freeze({ prediction: p.prediction, score: Number(p.score) }),\n );\n }\n\n asLangChainHandler(options: LangChainAdapterOptions = {}): Promise<unknown> {\n return import(\"./adapters/langchain.js\").then((m) => m.createLangChainHandler(this, options));\n }\n\n asMiddleware(options: MiddlewareOptions = {}): FirewallMiddleware {\n return createMiddleware(this, options);\n }\n\n effectiveThreshold(hook: HookLabel | undefined): number {\n if (hook === undefined) {\n return this.threshold;\n }\n return this.hookThresholds[hook] ?? this.threshold;\n }\n\n private async postWithRetry<T>(\n payload: SingleClassifyPayload | BatchClassifyPayload,\n maxRetries: number = DEFAULT_MAX_RETRIES,\n ): Promise<T> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const response = await fetch(this.apiUrl, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (response.status !== 429 || attempt === maxRetries) {\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new SilmarilApiError({\n status: response.status,\n statusText: response.statusText,\n body,\n });\n }\n return (await response.json()) as T;\n }\n const waitSeconds = Math.min(2 ** attempt, MAX_BACKOFF_SECONDS);\n await new Promise<void>((resolve) => setTimeout(resolve, waitSeconds * 1000));\n }\n throw new Error(\"Firewall: exhausted retries (unreachable)\");\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAGM,wBAEO,wBAsCA;AA3Cb;AAAA;AAAA;AAGA,IAAM,yBAAyB;AAExB,IAAM,yBAAN,MAAM,gCAA+B,MAAM;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAET,YAAY,QAKT;AACD,cAAM,wBAAuB,cAAc,MAAM,CAAC;AAClD,aAAK,OAAO;AACZ,aAAK,QAAQ,OAAO;AACpB,aAAK,YAAY,OAAO;AACxB,aAAK,aAAa,OAAO;AACzB,aAAK,QAAQ,OAAO;AACpB,eAAO,eAAe,MAAM,wBAAuB,SAAS;AAAA,MAC9D;AAAA,MAEA,OAAe,cAAc,QAIlB;AACT,cAAM,YACJ,OAAO,WAAW,SAAS,yBACvB,GAAG,OAAO,WAAW,MAAM,GAAG,sBAAsB,CAAC,QACrD,OAAO;AACb,eACE,8CACU,OAAO,MAAM,QAAQ,CAAC,CAAC,eAAe,OAAO,UAAU,QAAQ,CAAC,CAAC,OACvE,SAAS;AAAA,MAEjB;AAAA,IACF;AAEO,IAAM,mBAAN,MAAM,0BAAyB,MAAM;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MAET,YAAY,QAA8D;AACxE,cAAM,sBAAsB,OAAO,MAAM,IAAI,OAAO,UAAU,KAAK,OAAO,IAAI,EAAE;AAChF,aAAK,OAAO;AACZ,aAAK,SAAS,OAAO;AACrB,aAAK,aAAa,OAAO;AACzB,aAAK,OAAO,OAAO;AACnB,eAAO,eAAe,MAAM,kBAAiB,SAAS;AAAA,MACxD;AAAA,IACF;AAAA;AAAA;;;ACcO,SAAS,aACd,OAC2B;AAC3B,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,QAAM,WAAW,oBAAI,IAAkB;AACvC,aAAW,KAAK,OAAO;AACrB,QAAI,CAAC,qBAAqB,IAAI,CAAC,GAAG;AAChC,YAAM,IAAI,MAAM,+BAA+B,OAAO,CAAC,CAAC,EAAE;AAAA,IAC5D;AACA,aAAS,IAAI,CAAiB;AAAA,EAChC;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAAc,MAAqD;AAC7F,MAAI,SAAS,UAAa,SAAS,MAAM;AACvC,WAAO;AAAA,EACT;AACA,MAAI,SAAS,UAAU,SAAS;AAC9B,WAAO;AAAA,EACT;AACA,SAAO,SAAS,IAAI,KAAK,IAAI;AAC/B;AAEO,SAAS,gBAAgB,MAAc,UAA6C;AACzF,MAAI,aAAa,UAAa,aAAa,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,SAAO,SAAS,QAAQ,KAAK,IAAI;AACnC;AArGA,IAGa,WAWA,cAYA,eAKA,aAOA,cAMA,WAKA,wBAUA,yBASP;AApEN;AAAA;AAAA;AAGO,IAAM,YAAY;AAAA,MACvB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,SAAS;AAAA,IACX;AAIO,IAAM,eAAe;AAAA,MAC1B,WAAW;AAAA,MACX,kBAAkB;AAAA,MAClB,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,IACjB;AAIO,IAAM,gBAA2C,oBAAI,IAAkB;AAAA,MAC5E,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAEM,IAAM,cAAyC,oBAAI,IAAkB;AAAA,MAC1E,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAEM,IAAM,eAA0C,oBAAI,IAAkB;AAAA,MAC3E,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAEM,IAAM,YAAuC,oBAAI,IAAkB;AAAA,MACxE,GAAG;AAAA,MACH,GAAG;AAAA,IACL,CAAC;AAEM,IAAM,yBAAoE;AAAA,MAC/E,CAAC,aAAa,gBAAgB,GAAG,UAAU;AAAA,MAC3C,CAAC,aAAa,SAAS,GAAG,UAAU;AAAA,MACpC,CAAC,aAAa,UAAU,GAAG,UAAU;AAAA,MACrC,CAAC,aAAa,QAAQ,GAAG,UAAU;AAAA,MACnC,CAAC,aAAa,eAAe,GAAG,UAAU;AAAA,MAC1C,CAAC,aAAa,aAAa,GAAG,UAAU;AAAA,MACxC,CAAC,aAAa,OAAO,GAAG,UAAU;AAAA,IACpC;AAEO,IAAM,0BAA+D;AAAA,MAC1E,CAAC,UAAU,UAAU,GAAG;AAAA,MACxB,CAAC,UAAU,aAAa,GAAG;AAAA,MAC3B,CAAC,UAAU,SAAS,GAAG;AAAA,MACvB,CAAC,UAAU,aAAa,GAAG;AAAA,MAC3B,CAAC,UAAU,UAAU,GAAG;AAAA,MACxB,CAAC,UAAU,OAAO,GAAG;AAAA,IACvB;AAEA,IAAM,uBAA4C,IAAI,IAAI,OAAO,OAAO,YAAY,CAAC;AAAA;AAAA;;;ACc9E,SAAS,uBAAuB,SAAoC;AACzE,SAAO,QACJ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,KAAK,IAAI;AACd;AAEO,SAAS,yBAAyB,UAA0B;AACjE,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,yBAAyB,UAAsC;AAC7E,QAAM,QAAkB,CAAC;AACzB,aAAW,WAAW,SAAS,eAAe,CAAC,GAAG;AAChD,eAAW,OAAO,SAAS;AACzB,YAAM,QAAQ,IAAI,QAAQ,IAAI,KAAK;AACnC,UAAI,MAAM;AACR,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,yBAAyB,WAAqD;AAC5F,QAAM,QAAkB,CAAC;AACzB,aAAW,OAAO,WAAW;AAC3B,UAAM,QAAQ,IAAI,eAAe,IAAI,KAAK;AAC1C,QAAI,MAAM;AACR,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAnHA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AA4BA,SAAS,eAAe,SAA4C;AAClE,MAAI,OAAO,QAAQ,SAAS,UAAU;AACpC,WAAO,QAAQ,KAAK,YAAY;AAAA,EAClC;AACA,MAAI,OAAO,QAAQ,SAAS,UAAU;AACpC,WAAO,QAAQ,KAAK,YAAY;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,SAAuD;AACjF,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,KAAK,KAAK;AAAA,IAClB,WAAW,SAAS,MAAM,SAAS,QAAQ;AACzC,YAAM,KAAK,MAAM,QAAQ,EAAE;AAAA,IAC7B;AAAA,EACF;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAASA,qBACP,UACuC;AACvC,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,WAAW,IAAI,eAAe,GAAG,CAAC,GAAG;AACvC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,uBACpB,UACA,UAAmC,CAAC,GAClB;AAClB,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,gCAAgC;AAE7E,QAAM,eAAe,aAAa,QAAQ,KAAK;AAK/C,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,SACJ,QAAQ,WACP,CAAC,SAAiB,UAAyB;AAE1C,YAAQ,KAAK,sBAAsB,OAAO,IAAI,KAAK;AAAA,EACrD;AACF,QAAM,oBAAoB,QAAQ;AAClC,QAAM,yBAAyB,QAAQ,kBAAkB,CAAC;AAE1D,QAAM,qBAAqB,CAAC,UAA6B;AACvD,QAAI,uBAAuB,KAAK,MAAM,QAAW;AAC/C,aAAO,uBAAuB,KAAK;AAAA,IACrC;AACA,QAAI,SAAS,eAAe,KAAK,MAAM,QAAW;AAChD,aAAO,SAAS,eAAe,KAAK;AAAA,IACtC;AACA,WAAO,qBAAqB,SAAS;AAAA,EACvC;AAEA,QAAM,WAAW,OACf,MACA,WACA,OACA,aACkB;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,SAAS,SAAS,MAAM;AAAA,QAC3C,MAAM;AAAA,QACN,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC/C,CAAC;AACD,YAAM,YAAY,mBAAmB,SAAS;AAC9C,UAAI,OAAO,SAAS,WAAW;AAC7B,cAAM,IAAI,uBAAuB;AAAA,UAC/B,OAAO,OAAO;AAAA,UACd;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,wBAAwB;AACzC,cAAM;AAAA,MACR;AACA,UAAI,CAAC,UAAU;AACb,cAAM;AAAA,MACR;AACA,aAAO,kDAAkD,GAAG;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAM,gCAAgC,oBAAoB;AAAA,IAC/C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAEzB,MAAe,qBACb,MACA,UACA,OACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,gBAAgB,GAAG;AACpD;AAAA,MACF;AACA,YAAM,UAAU;AAChB,YAAM,OAAoC,CAAC;AAC3C,iBAAW,SAAS,SAAS;AAC3B,mBAAW,KAAK,OAAO;AACrB,eAAK,KAAK,CAAC;AAAA,QACb;AAAA,MACF;AACA,YAAM,WAAWA,qBAAoB,IAAI;AACzC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AACA,YAAM,OAAO,mBAAmB,SAAS,OAAO,EAAE,KAAK;AACvD,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,SAAS,MAAM,uBAAuB,aAAa,gBAAgB,GAAG,KAAK;AAAA,IACnF;AAAA,IAEA,MAAe,eACb,MACA,SACA,OACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,SAAS,GAAG;AAC7C;AAAA,MACF;AACA,YAAM,OAAO,uBAAuB,OAAO;AAC3C,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,SAAS,MAAM,uBAAuB,aAAa,SAAS,GAAG,KAAK;AAAA,IAC5E;AAAA,IAEA,MAAe,gBACb,MACA,UACA,OACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,UAAU,GAAG;AAC9C;AAAA,MACF;AACA,YAAM,OAAO,yBAAyB,QAAQ;AAC9C,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,WAAW,MAAM;AACvB,YAAM,WAAW,gBAAgB,MAAM,QAAQ;AAC/C,YAAM,SAAS,UAAU,uBAAuB,aAAa,UAAU,GAAG,OAAO,QAAQ;AAAA,IAC3F;AAAA,IAEA,MAAe,qBACb,YACA,OACA,OACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,eAAe,GAAG;AACnD;AAAA,MACF;AACA,YAAM,OAAO,MAAM,KAAK;AACxB,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,SAAS,MAAM,uBAAuB,aAAa,eAAe,GAAG,KAAK;AAAA,IAClF;AAAA,IAEA,MAAe,aAAa,QAAiB,OAA8B;AACzE,UAAI,CAAC,aAAa,IAAI,aAAa,OAAO,GAAG;AAC3C;AAAA,MACF;AACA,YAAM,OAAO,yBAAyB,MAA2E;AACjH,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,SAAS,MAAM,uBAAuB,aAAa,OAAO,GAAG,KAAK;AAAA,IAC1E;AAAA,IAEA,MAAe,cACb,QACA,OACA,cACA,OACA,SACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,QAAQ,GAAG;AAC5C;AAAA,MACF;AACA,YAAM,OAAO,OAAO,MAAM,EAAE,KAAK;AACjC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,WAAW,SAAS;AAC1B,YAAM,WAAW,gBAAgB,MAAM,QAAQ;AAC/C,YAAM,SAAS,UAAU,uBAAuB,aAAa,QAAQ,GAAG,OAAO,QAAQ;AAAA,IACzF;AAAA,IAEA,MAAe,mBACb,WACA,OACe;AACf,UAAI,CAAC,aAAa,IAAI,aAAa,aAAa,GAAG;AACjD;AAAA,MACF;AACA,YAAM,OAAO,yBAAyB,SAAS;AAC/C,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AACA,YAAM,SAAS,MAAM,uBAAuB,aAAa,aAAa,GAAG,KAAK;AAAA,IAChF;AAAA,EACF;AAEA,SAAO,IAAI,wBAAwB;AACrC;AA9PA,IAoBM;AApBN;AAAA;AAAA;AAIA;AACA;AAQA;AAOA,IAAM,aAAkC,oBAAI,IAAI,CAAC,SAAS,MAAM,CAAC;AAAA;AAAA;;;ACpBjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIA;AACA;AA+CA,SAAS,mBAAmB,OAAwB;AAClD,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAGA,SAAS,mBAAmB,SAAiD;AAC3E,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,KAAK,KAAK;AAAA,IAClB,WAAW,SAAS,MAAM,SAAS,QAAQ;AACzC,YAAM,KAAK,MAAM,QAAQ,EAAE;AAAA,IAC7B;AAAA,EACF;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,uBACP,SACuD;AACvD,MAAI,OAAO,QAAQ,YAAY,UAAU;AACvC,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAA6D,CAAC;AACpE,aAAW,QAAQ,QAAQ,SAAS;AAClC,QAAI,OAAO,SAAS,UAAU;AAC5B;AAAA,IACF;AACA,QAAI,KAAK,SAAS,eAAe;AAC/B,YAAM,OAAO,mBAAmB,KAAK,MAAM;AAC3C,UAAI,KAAK,KAAK,GAAG;AACf,YAAI,KAAK,EAAE,MAAM,UAAU,KAAK,SAAS,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,oBACP,UACiC;AACjC,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,IAAI,KAAK,YAAY,MAAM,QAAQ;AACrC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAWO,SAAS,iBACd,UACA,UAA6B,CAAC,GACV;AACpB,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,oBAAoB,QAAQ;AAClC,QAAM,yBAAyB,QAAQ,kBAAkB,CAAC;AAE1D,QAAM,qBAAqB,CAAC,UAA6B;AACvD,QAAI,uBAAuB,KAAK,MAAM,QAAW;AAC/C,aAAO,uBAAuB,KAAK;AAAA,IACrC;AACA,QAAI,SAAS,eAAe,KAAK,MAAM,QAAW;AAChD,aAAO,SAAS,eAAe,KAAK;AAAA,IACtC;AACA,WAAO,qBAAqB,SAAS;AAAA,EACvC;AAEA,QAAM,kBAAkB,OACtB,MACA,MACA,aACkB;AAClB,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB;AAAA,IACF;AACA,UAAM,SAAsB,MAAM,SAAS;AAAA,MACzC;AAAA,MACA,aAAa,SAAY,EAAE,MAAM,SAAS,IAAI,EAAE,KAAK;AAAA,IACvD;AACA,UAAM,YAAY,mBAAmB,IAAI;AACzC,UAAM,UAAU,OAAO,SAAS;AAChC,YAAQ,aAAa;AAAA,MACnB;AAAA,MACA,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC7C;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,SAAS;AACX,YAAM,MAAM,IAAI,uBAAuB;AAAA,QACrC,OAAO,OAAO;AAAA,QACd;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AACD,cAAQ,YAAY,GAAG;AACvB,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,aAAa,OAAO,WAA8D;AACtF,QAAI,OAAO,WAAW,GAAG;AACvB;AAAA,IACF;AACA,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,UAAM,OAAO,KAAK,KAAK,YAAY;AAMnC,QAAI,SAAS,QAAQ;AACnB,iBAAW,EAAE,MAAAC,OAAM,SAAS,KAAK,uBAAuB,IAAI,GAAG;AAC7D,cAAM,gBAAgBA,OAAM,UAAU,eAAe,QAAQ;AAAA,MAC/D;AACA;AAAA,IACF;AAIA,UAAM,WAAW,oBAAoB,MAAM;AAC3C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AACA,UAAM,OAAO,mBAAmB,SAAS,OAAO,EAAE,KAAK;AACvD,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AACA,UAAM,gBAAgB,MAAM,UAAU,UAAU;AAAA,EAClD;AAEA,QAAM,qBAAqB,OAAO,WAAgD;AAChF,QAAI,cAAc,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,GAAG;AAC3E,YAAM,gBAAgB,OAAO,MAAM,UAAU,UAAU;AAAA,IACzD;AACA,QAAI,QAAQ,iBAAiB,MAAM,QAAQ,OAAO,SAAS,GAAG;AAC5D,iBAAW,QAAQ,OAAO,WAAW;AACnC,YAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC;AAAA,QACF;AACA,cAAM,OACJ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,mBAAmB,KAAK,IAAI;AAC1E,cAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACrE,YAAI,KAAK,KAAK,GAAG;AACf,gBAAM,gBAAgB,MAAM,UAAU,WAAW,QAAQ;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,aAA6E;AAAA,MACjF;AAAA,MACA;AAAA,IACF,GAAgD;AAC9C,UAAI,WAAW;AACb,cAAM,WAAW,OAAO,MAAM;AAAA,MAChC;AACA,YAAM,SAAS,MAAM,WAAW;AAChC,YAAM,mBAAmB,MAAM;AAC/B,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAA0F;AAAA,MAC9F;AAAA,MACA;AAAA,IACF,GAA8C;AAC5C,UAAI,WAAW;AACb,cAAM,WAAW,OAAO,MAAM;AAAA,MAChC;AACA,YAAM,EAAE,QAAQ,GAAG,KAAK,IAAI,MAAM,SAAS;AAC3C,UAAI,CAAC,YAAY;AACf,eAAO,EAAE,QAAQ,GAAG,KAAK;AAAA,MAC3B;AAEA,UAAI,WAAW;AACf,YAAM,cAAc,OAAO;AAAA,QACzB,IAAI,gBAAkC;AAAA,UACpC,UAAU,OAAO,YAAkB;AACjC,kBAAM,OAAO;AACb,gBAAI,QAAQ,KAAK,SAAS,cAAc;AACtC,oBAAM,QAAQ,KAAK,aAAa,KAAK,SAAS;AAC9C,0BAAY;AAAA,YACd;AACA,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,UACA,MAAM,MAAM,YAA2B;AACrC,gBAAI,CAAC,SAAS,KAAK,GAAG;AACpB;AAAA,YACF;AACA,gBAAI;AACF,oBAAM,gBAAgB,UAAU,UAAU,UAAU;AAAA,YACtD,SAAS,KAAK;AACZ,kBAAI,eAAe,wBAAwB;AACzC,2BAAW,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,CAAC;AAChD;AAAA,cACF;AACA,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO,EAAE,QAAQ,aAAa,GAAG,KAAK;AAAA,IACxC;AAAA,EACF;AACF;;;ACrRA,IAAM,kBAAkB;AAEjB,IAAM,mBAAmB;AACzB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAEtB,IAAM,kBAAkB,mBAAmB;AAC3C,IAAM,qBAAqB,eAAe;AAC1C,IAAM,sBAAsB,gBAAgB;AACnD,IAAM,qBAAqB,qBAAqB;AAEzC,SAAS,UAAU,MAAwB;AAChD,QAAM,IAAI,KAAK;AACf,MAAI,IAAI,iBAAiB;AACvB,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,MAAM,IAAI,eAAe,CAAC,YAAY,CAAC,mBAC9C,gBAAgB,YAAY,eAAe;AAAA,IACzD;AAAA,EACF;AACA,MAAI,KAAK,oBAAoB;AAC3B,WAAO,CAAC,IAAI;AAAA,EACd;AACA,QAAM,SAAmB,CAAC;AAC1B,WAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS,oBAAoB;AAC1D,UAAM,MAAM,QAAQ;AACpB,WAAO,KAAK,KAAK,MAAM,OAAO,GAAG,CAAC;AAClC,QAAI,OAAO,GAAG;AACZ;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC7BA;AAYO,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAClC,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAuBrB,IAAM,WAAN,MAAe;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ;AAAA,EAEjB,YAAY,SAA0B;AACpC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,iBAAiB,QAAQ,kBAAkB,CAAC;AACjD,SAAK,UAAU,OAAO,OAAO;AAAA,MAC3B,aAAa,KAAK;AAAA,MAClB,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,MAAc,UAA2B,CAAC,GAAyB;AAChF,UAAM,SAAS,UAAU,IAAI;AAC7B,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,UAAiC,EAAE,MAAM,OAAO,CAAC,EAAG;AAC1D,UAAI,QAAQ,SAAS,QAAW;AAC9B,gBAAQ,OAAO,QAAQ;AAAA,MACzB;AACA,UAAI,QAAQ,aAAa,QAAW;AAClC,gBAAQ,YAAY,QAAQ;AAAA,MAC9B;AACA,YAAM,OAAO,MAAM,KAAK,cAAsC,OAAO;AACrE,aAAO,OAAO,OAAO,EAAE,YAAY,KAAK,YAAY,OAAO,OAAO,KAAK,KAAK,EAAE,CAAC;AAAA,IACjF;AACA,UAAM,YAAkC,CAAC;AACzC,QAAI,QAAQ,SAAS,QAAW;AAC9B,gBAAU,QAAQ,OAAO,IAAI,MAAM,QAAQ,IAAK;AAAA,IAClD;AACA,QAAI,QAAQ,aAAa,QAAW;AAClC,gBAAU,YAAY,OAAO,IAAI,MAAM,QAAQ,QAAQ;AAAA,IACzD;AACA,UAAM,UAAU,MAAM,KAAK,cAAc,QAAQ,SAAS;AAC1D,WAAO,QAAQ,OAAO,CAAC,MAAM,MAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,IAAK;AAAA,EACtE;AAAA,EAEA,MAAM,cACJ,OACA,UAAgC,CAAC,GACT;AACxB,UAAM,UAAgC,EAAE,MAAM;AAC9C,QAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC7C,cAAQ,QAAQ,QAAQ,MAAM,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC;AAAA,IACpD;AACA,QAAI,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;AACrD,cAAQ,aAAa,QAAQ,UAAU,IAAI,CAAC,MAAO,MAAM,SAAY,OAAO,CAAE;AAAA,IAChF;AACA,UAAM,OAAO,MAAM,KAAK,cAAqC,OAAO;AACpE,WAAO,KAAK,YAAY;AAAA,MAAI,CAAC,MAC3B,OAAO,OAAO,EAAE,YAAY,EAAE,YAAY,OAAO,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IACpE;AAAA,EACF;AAAA,EAEA,mBAAmB,UAAmC,CAAC,GAAqB;AAC1E,WAAO,oEAAkC,KAAK,CAAC,MAAM,EAAE,uBAAuB,MAAM,OAAO,CAAC;AAAA,EAC9F;AAAA,EAEA,aAAa,UAA6B,CAAC,GAAuB;AAChE,WAAO,iBAAiB,MAAM,OAAO;AAAA,EACvC;AAAA,EAEA,mBAAmB,MAAqC;AACtD,QAAI,SAAS,QAAW;AACtB,aAAO,KAAK;AAAA,IACd;AACA,WAAO,KAAK,eAAe,IAAI,KAAK,KAAK;AAAA,EAC3C;AAAA,EAEA,MAAc,cACZ,SACA,aAAqB,qBACT;AACZ,aAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,YAAM,WAAW,MAAM,MAAM,KAAK,QAAQ;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,YAAY,QAAQ,KAAK,SAAS;AAAA,MAC5C,CAAC;AACD,UAAI,SAAS,WAAW,OAAO,YAAY,YAAY;AACrD,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,gBAAM,IAAI,iBAAiB;AAAA,YACzB,QAAQ,SAAS;AAAA,YACjB,YAAY,SAAS;AAAA,YACrB;AAAA,UACF,CAAC;AAAA,QACH;AACA,eAAQ,MAAM,SAAS,KAAK;AAAA,MAC9B;AACA,YAAM,cAAc,KAAK,IAAI,KAAK,SAAS,mBAAmB;AAC9D,YAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,cAAc,GAAI,CAAC;AAAA,IAC9E;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;;;AHhJA;AAcA;","names":["findLastUserMessage","text"]}
|
|
1
|
+
{"version":3,"sources":["../src/exceptions.ts","../src/hooks.ts","../src/utils/extract.ts","../src/adapters/langchain.ts","../src/index.ts","../src/adapters/vercel.ts","../src/chunking.ts","../src/firewall.ts"],"sourcesContent":["// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst MAX_PROMPT_DISPLAY_LEN = 100;\n\nexport class PromptBlockedException extends Error {\n readonly score: number;\n readonly threshold: number;\n readonly promptText: string;\n readonly runId: string | undefined;\n\n constructor(params: {\n score: number;\n threshold: number;\n promptText: string;\n runId?: string;\n }) {\n super(PromptBlockedException.formatMessage(params));\n this.name = \"PromptBlockedException\";\n this.score = params.score;\n this.threshold = params.threshold;\n this.promptText = params.promptText;\n this.runId = params.runId;\n Object.setPrototypeOf(this, PromptBlockedException.prototype);\n }\n\n private static formatMessage(params: {\n score: number;\n threshold: number;\n promptText: string;\n }): string {\n const truncated =\n params.promptText.length > MAX_PROMPT_DISPLAY_LEN\n ? `${params.promptText.slice(0, MAX_PROMPT_DISPLAY_LEN)}...`\n : params.promptText;\n return (\n `Prompt blocked by Silmaril Firewall ` +\n `(score=${params.score.toFixed(4)}, threshold=${params.threshold.toFixed(4)}): ` +\n `'${truncated}'`\n );\n }\n}\n\nexport class SilmarilApiError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly body: string;\n\n constructor(params: { status: number; statusText: string; body: string }) {\n super(`Silmaril API error ${params.status} ${params.statusText}: ${params.body}`);\n this.name = \"SilmarilApiError\";\n this.status = params.status;\n this.statusText = params.statusText;\n this.body = params.body;\n Object.setPrototypeOf(this, SilmarilApiError.prototype);\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport const HookLabel = {\n USER_INPUT: \"user_input\",\n SYSTEM_PROMPT: \"system_prompt\",\n TOOL_CALL: \"tool_call\",\n TOOL_RESPONSE: \"tool_response\",\n LLM_OUTPUT: \"llm_output\",\n UNKNOWN: \"unknown\",\n} as const;\n\nexport type HookLabel = (typeof HookLabel)[keyof typeof HookLabel];\n\nexport const FirewallHook = {\n LLM_START: \"on_llm_start\",\n CHAT_MODEL_START: \"on_chat_model_start\",\n TOOL_START: \"on_tool_start\",\n RETRIEVER_START: \"on_retriever_start\",\n LLM_END: \"on_llm_end\",\n TOOL_END: \"on_tool_end\",\n RETRIEVER_END: \"on_retriever_end\",\n} as const;\n\nexport type FirewallHook = (typeof FirewallHook)[keyof typeof FirewallHook];\n\nexport const DEFAULT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n]);\n\nexport const INPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.RETRIEVER_START,\n]);\n\nexport const OUTPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_END,\n FirewallHook.TOOL_END,\n FirewallHook.RETRIEVER_END,\n]);\n\nexport const ALL_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n ...INPUT_HOOKS,\n ...OUTPUT_HOOKS,\n]);\n\nexport const FIREWALL_HOOK_TO_LABEL: Readonly<Record<FirewallHook, HookLabel>> = {\n [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,\n [FirewallHook.LLM_START]: HookLabel.USER_INPUT,\n [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,\n [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,\n [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT,\n};\n\nexport const DEFAULT_HOOK_THRESHOLDS: Readonly<Record<HookLabel, number>> = {\n [HookLabel.USER_INPUT]: 0.5,\n [HookLabel.SYSTEM_PROMPT]: 0.5,\n [HookLabel.TOOL_CALL]: 0.5,\n [HookLabel.TOOL_RESPONSE]: 0.5,\n [HookLabel.LLM_OUTPUT]: 0.5,\n [HookLabel.UNKNOWN]: 0.5,\n};\n\nconst FIREWALL_HOOK_VALUES: ReadonlySet<string> = new Set(Object.values(FirewallHook));\n\nexport function resolveHooks(\n hooks: Iterable<FirewallHook | string> | undefined,\n): ReadonlySet<FirewallHook> {\n if (hooks === undefined) {\n return DEFAULT_HOOKS;\n }\n const resolved = new Set<FirewallHook>();\n for (const h of hooks) {\n if (!FIREWALL_HOOK_VALUES.has(h)) {\n throw new Error(`Invalid FirewallHook value: ${String(h)}`);\n }\n resolved.add(h as FirewallHook);\n }\n return resolved;\n}\n\nexport function prependHook(text: string, hook: HookLabel | string | undefined | null): string {\n if (hook === undefined || hook === null) {\n return text;\n }\n if (hook === HookLabel.UNKNOWN) {\n return text;\n }\n return `[HOOK:${hook}] ${text}`;\n}\n\nexport function prependToolName(text: string, toolName: string | undefined | null): string {\n if (toolName === undefined || toolName === null) {\n return text;\n }\n return `[TOOL:${toolName}] ${text}`;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst SKIP_ROLES: ReadonlySet<string> = new Set([\"ai\", \"assistant\"]);\nconst SYSTEM_ROLES: ReadonlySet<string> = new Set([\"system\"]);\nconst TOOL_ROLES: ReadonlySet<string> = new Set([\"tool\", \"function\"]);\n\ntype MessageContent = string | ReadonlyArray<string | { type?: string; text?: string }>;\n\ninterface DucktypedMessage {\n role?: string;\n type?: string;\n content?: MessageContent;\n}\n\ninterface DucktypedGeneration {\n text?: string;\n}\n\ninterface DucktypedLLMResult {\n generations?: ReadonlyArray<ReadonlyArray<DucktypedGeneration>>;\n}\n\ninterface DucktypedDocument {\n pageContent?: string;\n}\n\nfunction extractContentText(content: MessageContent | undefined): string {\n if (typeof content === \"string\") {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && typeof block === \"object\" && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n }\n return \"\";\n}\n\nfunction getRole(message: DucktypedMessage): string {\n if (message.role !== undefined) {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nexport function extractTextFromMessages(\n messages: ReadonlyArray<DucktypedMessage>,\n options: { includeSystem?: boolean; includeTool?: boolean } = {},\n): string {\n const includeSystem = options.includeSystem ?? true;\n const includeTool = options.includeTool ?? true;\n const parts: string[] = [];\n for (const message of messages) {\n const role = getRole(message);\n if (SKIP_ROLES.has(role)) {\n continue;\n }\n if (!includeSystem && SYSTEM_ROLES.has(role)) {\n continue;\n }\n if (!includeTool && TOOL_ROLES.has(role)) {\n continue;\n }\n const text = extractContentText(message.content);\n const stripped = text.trim();\n if (stripped) {\n parts.push(stripped);\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromPrompts(prompts: readonly string[]): string {\n return prompts\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n .join(\"\\n\");\n}\n\nexport function extractTextFromToolInput(inputStr: string): string {\n return inputStr.trim();\n}\n\nexport function extractTextFromLLMResult(response: DucktypedLLMResult): string {\n const parts: string[] = [];\n for (const genList of response.generations ?? []) {\n for (const gen of genList) {\n const text = (gen.text ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromDocuments(documents: ReadonlyArray<DucktypedDocument>): string {\n const parts: string[] = [];\n for (const doc of documents) {\n const text = (doc.pageContent ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n return parts.join(\"\\n\");\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport {\n FIREWALL_HOOK_TO_LABEL,\n FirewallHook,\n HookLabel,\n resolveHooks,\n} from \"../hooks.js\";\nimport type { BlockResult, LangChainAdapterOptions } from \"../types.js\";\nimport {\n extractTextFromDocuments,\n extractTextFromLLMResult,\n extractTextFromPrompts,\n extractTextFromToolInput,\n} from \"../utils/extract.js\";\n\nconst USER_ROLES: ReadonlySet<string> = new Set([\"human\", \"user\"]);\n\ninterface LangChainDucktypedMessage {\n role?: string;\n type?: string;\n content?: string | ReadonlyArray<string | { type?: string; text?: string }>;\n}\n\nfunction getMessageRole(message: LangChainDucktypedMessage): string {\n if (typeof message.role === \"string\") {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nfunction extractMessageText(content: LangChainDucktypedMessage[\"content\"]): string {\n if (content === undefined) {\n return \"\";\n }\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<LangChainDucktypedMessage>,\n): LangChainDucktypedMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (USER_ROLES.has(getMessageRole(msg))) {\n return msg;\n }\n }\n return undefined;\n}\n\nexport async function createLangChainHandler(\n firewall: Firewall,\n options: LangChainAdapterOptions = {},\n): Promise<BaseCallbackHandler> {\n const { BaseCallbackHandler } = await import(\"@langchain/core/callbacks/base\");\n\n const enabledHooks = resolveHooks(options.hooks);\n // includeSystem / includeTool are retained in LangChainAdapterOptions for\n // backwards compat but are inert under the per-message hook-routing algorithm:\n // handleChatModelStart only scans the last user-role message; system and tool\n // messages are either trusted or already classified by their own hooks.\n const failOpen = options.failOpen ?? true;\n const logger =\n options.logger ??\n ((message: string, error: unknown): void => {\n // eslint-disable-next-line no-console\n console.warn(`silmaril.firewall: ${message}`, error);\n });\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n const shadowMode = options.shadowMode ?? firewall.shadowMode;\n const onClassify = options.onClassify;\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const fireOnClassify = (\n hookLabel: HookLabel,\n text: string,\n result: BlockResult,\n blocked: boolean,\n toolName: string | undefined,\n ): void => {\n if (!onClassify) {\n return;\n }\n try {\n onClassify({\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n shadowMode,\n });\n } catch (err) {\n logger(\"onClassify callback threw\", err);\n }\n };\n\n const classify = async (\n text: string,\n hookLabel: HookLabel,\n runId: string,\n toolName?: string,\n ): Promise<void> => {\n let result: BlockResult;\n try {\n result = await firewall.classify(text, {\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n });\n } catch (err) {\n if (!failOpen) {\n throw err;\n }\n logger(\"classification failed, allowing prompt through\", err);\n return;\n }\n const threshold = effectiveThreshold(hookLabel);\n const blocked = result.score >= threshold;\n fireOnClassify(hookLabel, text, result, blocked, toolName);\n if (blocked && !shadowMode) {\n throw new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n runId,\n });\n }\n };\n\n class SilmarilFirewallHandler extends BaseCallbackHandler {\n override name = \"silmaril_firewall_handler\";\n override raiseError = true;\n override awaitHandlers = true;\n\n override async handleChatModelStart(\n _llm: unknown,\n messages: unknown,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {\n return;\n }\n const batches = messages as ReadonlyArray<ReadonlyArray<LangChainDucktypedMessage>>;\n const flat: LangChainDucktypedMessage[] = [];\n for (const batch of batches) {\n for (const m of batch) {\n flat.push(m);\n }\n }\n const lastUser = findLastUserMessage(flat);\n if (!lastUser) {\n return;\n }\n const text = extractMessageText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);\n }\n\n override async handleLLMStart(\n _llm: unknown,\n prompts: string[],\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_START)) {\n return;\n }\n const text = extractTextFromPrompts(prompts);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);\n }\n\n override async handleToolStart(\n tool: { name?: string } | undefined,\n inputStr: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_START)) {\n return;\n }\n const text = extractTextFromToolInput(inputStr);\n if (!text) {\n return;\n }\n const toolName = tool?.name;\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);\n }\n\n override async handleRetrieverStart(\n _retriever: unknown,\n query: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {\n return;\n }\n const text = query.trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);\n }\n\n override async handleLLMEnd(output: unknown, runId: string): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_END)) {\n return;\n }\n const text = extractTextFromLLMResult(output as { generations?: ReadonlyArray<ReadonlyArray<{ text?: string }>> });\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);\n }\n\n override async handleToolEnd(\n output: unknown,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _kwargs?: { name?: string },\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_END)) {\n return;\n }\n const text = String(output).trim();\n if (!text) {\n return;\n }\n const toolName = _kwargs?.name;\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);\n }\n\n override async handleRetrieverEnd(\n documents: ReadonlyArray<{ pageContent?: string }>,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {\n return;\n }\n const text = extractTextFromDocuments(documents);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);\n }\n }\n\n return new SilmarilFirewallHandler();\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport {\n Firewall,\n DEFAULT_THRESHOLD,\n DEFAULT_TIMEOUT_MS,\n} from \"./firewall.js\";\n\nexport { PromptBlockedException, SilmarilApiError } from \"./exceptions.js\";\n\nexport type { FirewallMiddleware } from \"./adapters/vercel.js\";\n\nexport {\n chunkText,\n MAX_INPUT_TOKENS,\n CHUNK_WINDOW,\n CHUNK_OVERLAP,\n MAX_INPUT_CHARS,\n CHUNK_WINDOW_CHARS,\n CHUNK_OVERLAP_CHARS,\n} from \"./chunking.js\";\n\nexport {\n HookLabel,\n FirewallHook,\n DEFAULT_HOOKS,\n INPUT_HOOKS,\n OUTPUT_HOOKS,\n ALL_HOOKS,\n DEFAULT_HOOK_THRESHOLDS,\n FIREWALL_HOOK_TO_LABEL,\n resolveHooks,\n prependHook,\n prependToolName,\n} from \"./hooks.js\";\n\nexport type {\n BlockResult,\n ClassifyEvent,\n Prediction,\n ClassificationMetadata,\n FirewallOptions,\n ClassifyOptions,\n ClassifyBatchOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n} from \"./types.js\";\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport { HookLabel } from \"../hooks.js\";\nimport type { BlockResult, MiddlewareOptions } from \"../types.js\";\n\ninterface VercelContentPart {\n type?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n}\n\ninterface VercelPromptMessage {\n role: string;\n content: string | ReadonlyArray<VercelContentPart | string>;\n}\n\ninterface VercelToolCall {\n toolCallType?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n}\n\ninterface VercelGenerateResult {\n text?: string;\n toolCalls?: ReadonlyArray<VercelToolCall>;\n}\n\ninterface WrapGenerateArgs<TResult extends VercelGenerateResult & Record<string, unknown>> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doGenerate: () => PromiseLike<TResult>;\n}\n\ninterface WrapStreamArgs<\n TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>,\n> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doStream: () => PromiseLike<TResult>;\n}\n\ninterface StreamPart {\n type?: string;\n textDelta?: string;\n delta?: string;\n}\n\nfunction stringifyToolValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n\nfunction extractContentText(content: VercelPromptMessage[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction iterateToolResultParts(\n message: VercelPromptMessage,\n): Array<{ text: string; toolName: string | undefined }> {\n if (typeof message.content === \"string\") {\n return [];\n }\n const out: Array<{ text: string; toolName: string | undefined }> = [];\n for (const part of message.content) {\n if (typeof part === \"string\") {\n continue;\n }\n if (part.type === \"tool-result\") {\n const text = stringifyToolValue(part.result);\n if (text.trim()) {\n out.push({ text, toolName: part.toolName });\n }\n }\n }\n return out;\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<VercelPromptMessage>,\n): VercelPromptMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role.toLowerCase() === \"user\") {\n return msg;\n }\n }\n return undefined;\n}\n\nexport interface FirewallMiddleware {\n wrapGenerate: <TResult extends VercelGenerateResult & Record<string, unknown>>(\n args: WrapGenerateArgs<TResult>,\n ) => Promise<TResult>;\n wrapStream: <TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>(\n args: WrapStreamArgs<TResult>,\n ) => Promise<TResult>;\n}\n\nexport function createMiddleware(\n firewall: Firewall,\n options: MiddlewareOptions = {},\n): FirewallMiddleware {\n const scanInput = options.scanInput ?? true;\n const scanOutput = options.scanOutput ?? false;\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n const shadowMode = options.shadowMode ?? firewall.shadowMode;\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classifyOrBlock = async (\n text: string,\n hook: HookLabel,\n toolName?: string,\n ): Promise<void> => {\n if (!text.trim()) {\n return;\n }\n const result: BlockResult = await firewall.classify(\n text,\n toolName !== undefined ? { hook, toolName } : { hook },\n );\n const threshold = effectiveThreshold(hook);\n const blocked = result.score >= threshold;\n options.onClassify?.({\n hook,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n shadowMode,\n });\n if (blocked && !shadowMode) {\n const err = new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n });\n options.onBlocked?.(err);\n throw err;\n }\n };\n\n const scanPrompt = async (prompt: ReadonlyArray<VercelPromptMessage>): Promise<void> => {\n if (prompt.length === 0) {\n return;\n }\n const last = prompt[prompt.length - 1]!;\n const role = last.role.toLowerCase();\n\n // If the newest message is a tool result (typical mid-flow in a\n // tool-calling loop), classify each tool-result part individually with\n // tool_response + toolName. No manual wiring from the caller — the\n // toolName is read directly from the Vercel content part.\n if (role === \"tool\") {\n for (const { text, toolName } of iterateToolResultParts(last)) {\n await classifyOrBlock(text, HookLabel.TOOL_RESPONSE, toolName);\n }\n return;\n }\n\n // Otherwise, fall back to scanning the most recent user message.\n // Covers: [user], [system, user], [user, assistant(text), user], etc.\n const lastUser = findLastUserMessage(prompt);\n if (!lastUser) {\n return;\n }\n const text = extractContentText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classifyOrBlock(text, HookLabel.USER_INPUT);\n };\n\n const scanGenerateResult = async (result: VercelGenerateResult): Promise<void> => {\n if (scanOutput && typeof result.text === \"string\" && result.text.length > 0) {\n await classifyOrBlock(result.text, HookLabel.LLM_OUTPUT);\n }\n if (options.scanToolCalls && Array.isArray(result.toolCalls)) {\n for (const call of result.toolCalls) {\n if (!call || typeof call !== \"object\") {\n continue;\n }\n const args =\n typeof call.args === \"string\" ? call.args : stringifyToolValue(call.args);\n const toolName = typeof call.toolName === \"string\" ? call.toolName : undefined;\n if (args.trim()) {\n await classifyOrBlock(args, HookLabel.TOOL_CALL, toolName);\n }\n }\n }\n };\n\n return {\n async wrapGenerate<TResult extends VercelGenerateResult & Record<string, unknown>>({\n params,\n doGenerate,\n }: WrapGenerateArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const result = await doGenerate();\n await scanGenerateResult(result);\n return result;\n },\n\n async wrapStream<TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>({\n params,\n doStream,\n }: WrapStreamArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const { stream, ...rest } = await doStream();\n if (!scanOutput) {\n return { stream, ...rest } as unknown as TResult;\n }\n\n let buffered = \"\";\n const transformed = stream.pipeThrough(\n new TransformStream<unknown, unknown>({\n transform(chunk, controller): void {\n const part = chunk as StreamPart;\n if (part && part.type === \"text-delta\") {\n const delta = part.textDelta ?? part.delta ?? \"\";\n buffered += delta;\n }\n controller.enqueue(chunk);\n },\n async flush(controller): Promise<void> {\n if (!buffered.trim()) {\n return;\n }\n try {\n await classifyOrBlock(buffered, HookLabel.LLM_OUTPUT);\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n controller.enqueue({ type: \"error\", error: err });\n return;\n }\n throw err;\n }\n },\n }),\n );\n return { stream: transformed, ...rest } as unknown as TResult;\n },\n };\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst CHARS_PER_TOKEN = 4;\n\nexport const MAX_INPUT_TOKENS = 10_240;\nexport const CHUNK_WINDOW = 400;\nexport const CHUNK_OVERLAP = 64;\n\nexport const MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;\nexport const CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;\nexport const CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;\nconst CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;\n\nexport function chunkText(text: string): string[] {\n const n = text.length;\n if (n > MAX_INPUT_CHARS) {\n throw new Error(\n `Input has ~${Math.floor(n / CHARS_PER_TOKEN)} tokens (${n} chars); ` +\n `max is ${MAX_INPUT_TOKENS} tokens (${MAX_INPUT_CHARS} chars)`,\n );\n }\n if (n <= CHUNK_WINDOW_CHARS) {\n return [text];\n }\n const chunks: string[] = [];\n for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {\n const end = start + CHUNK_WINDOW_CHARS;\n chunks.push(text.slice(start, end));\n if (end >= n) {\n break;\n }\n }\n return chunks;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\n\nimport { createMiddleware, type FirewallMiddleware } from \"./adapters/vercel.js\";\nimport { chunkText } from \"./chunking.js\";\nimport { SilmarilApiError } from \"./exceptions.js\";\nimport type { HookLabel } from \"./hooks.js\";\nimport type {\n BlockResult,\n ClassifyBatchOptions,\n ClassifyOptions,\n ClassificationMetadata,\n FirewallOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n Prediction,\n} from \"./types.js\";\n\nexport const DEFAULT_THRESHOLD = 0.5;\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RETRIES = 5;\nconst MAX_BACKOFF_SECONDS = 30;\n\ninterface SingleClassifyPayload {\n text: string;\n hook?: string;\n tool_name?: string;\n metadata?: ClassificationMetadata;\n}\n\ninterface BatchClassifyPayload {\n texts: readonly string[];\n hooks?: readonly string[];\n tool_names?: readonly (string | null)[];\n metadata?: readonly (ClassificationMetadata | null)[];\n}\n\ninterface SingleClassifyResponse {\n prediction: Prediction;\n score: number;\n primary_outcome?: string;\n outcome_scores?: Record<string, number>;\n detector_scores?: Record<string, number>;\n detector_counts?: Record<string, number>;\n}\n\ninterface BatchClassifyResponse {\n predictions: readonly SingleClassifyResponse[];\n}\n\nfunction blockResultFromResponse(data: SingleClassifyResponse): BlockResult {\n const result: {\n prediction: Prediction;\n score: number;\n primaryOutcome?: string;\n outcomeScores?: Readonly<Record<string, number>>;\n detectorScores?: Readonly<Record<string, number>>;\n detectorCounts?: Readonly<Record<string, number>>;\n } = {\n prediction: data.prediction,\n score: Number(data.score),\n };\n if (data.primary_outcome !== undefined) {\n result.primaryOutcome = data.primary_outcome;\n }\n if (data.outcome_scores !== undefined) {\n result.outcomeScores = Object.freeze(\n Object.fromEntries(Object.entries(data.outcome_scores).map(([k, v]) => [k, Number(v)])),\n );\n }\n if (data.detector_scores !== undefined) {\n result.detectorScores = Object.freeze(\n Object.fromEntries(Object.entries(data.detector_scores).map(([k, v]) => [k, Number(v)])),\n );\n }\n if (data.detector_counts !== undefined) {\n result.detectorCounts = Object.freeze(\n Object.fromEntries(Object.entries(data.detector_counts).map(([k, v]) => [k, Number(v)])),\n );\n }\n return Object.freeze(result);\n}\n\nexport class Firewall {\n readonly apiKey: string;\n readonly apiUrl: string;\n readonly threshold: number;\n readonly timeoutMs: number;\n readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;\n readonly shadowMode: boolean;\n\n private readonly headers: Readonly<Record<string, string>>;\n\n constructor(options: FirewallOptions) {\n if (!options.apiKey) {\n throw new Error(\"Firewall: apiKey is required\");\n }\n if (!options.apiUrl) {\n throw new Error(\"Firewall: apiUrl is required\");\n }\n this.apiKey = options.apiKey;\n this.apiUrl = options.apiUrl;\n this.threshold = options.threshold ?? DEFAULT_THRESHOLD;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.hookThresholds = Object.freeze({ ...(options.hookThresholds ?? {}) });\n this.shadowMode = options.shadowMode ?? false;\n this.headers = Object.freeze({\n \"x-api-key\": this.apiKey,\n \"content-type\": \"application/json\",\n });\n }\n\n async classify(text: string, options: ClassifyOptions = {}): Promise<BlockResult> {\n const chunks = chunkText(text);\n if (chunks.length === 1) {\n const payload: SingleClassifyPayload = { text: chunks[0]! };\n if (options.hook !== undefined) {\n payload.hook = options.hook;\n }\n if (options.toolName !== undefined) {\n payload.tool_name = options.toolName;\n }\n if (options.metadata !== undefined) {\n payload.metadata = options.metadata;\n }\n const data = await this.postWithRetry<SingleClassifyResponse>(payload);\n return blockResultFromResponse(data);\n }\n const batchOpts: ClassifyBatchOptions = {};\n if (options.hook !== undefined) {\n batchOpts.hooks = chunks.map(() => options.hook!);\n }\n if (options.toolName !== undefined) {\n batchOpts.toolNames = chunks.map(() => options.toolName);\n }\n if (options.metadata !== undefined) {\n batchOpts.metadata = chunks.map(() => options.metadata);\n }\n const results = await this.classifyBatch(chunks, batchOpts);\n return results.reduce((best, r) => (r.score > best.score ? r : best));\n }\n\n async classifyBatch(\n texts: readonly string[],\n options: ClassifyBatchOptions = {},\n ): Promise<BlockResult[]> {\n if (options.hooks !== undefined && options.hooks.length !== texts.length) {\n throw new Error(\n `Firewall: hooks length ${options.hooks.length} does not match texts length ${texts.length}`,\n );\n }\n if (options.toolNames !== undefined && options.toolNames.length !== texts.length) {\n throw new Error(\n `Firewall: toolNames length ${options.toolNames.length} does not match texts length ${texts.length}`,\n );\n }\n if (options.metadata !== undefined && options.metadata.length !== texts.length) {\n throw new Error(\n `Firewall: metadata length ${options.metadata.length} does not match texts length ${texts.length}`,\n );\n }\n\n const payload: BatchClassifyPayload = { texts };\n if (options.hooks && options.hooks.length > 0) {\n payload.hooks = options.hooks.map((h) => String(h));\n }\n if (options.toolNames && options.toolNames.length > 0) {\n payload.tool_names = options.toolNames.map((t) => (t === undefined ? null : t));\n }\n if (options.metadata && options.metadata.length > 0) {\n payload.metadata = options.metadata.map((m) => (m === undefined ? null : m));\n }\n const data = await this.postWithRetry<BatchClassifyResponse>(payload);\n return data.predictions.map((p) => blockResultFromResponse(p));\n }\n\n asLangChainHandler(options: LangChainAdapterOptions = {}): Promise<BaseCallbackHandler> {\n return import(\"./adapters/langchain.js\").then((m) => m.createLangChainHandler(this, options));\n }\n\n asMiddleware(options: MiddlewareOptions = {}): FirewallMiddleware {\n return createMiddleware(this, options);\n }\n\n effectiveThreshold(hook: HookLabel | undefined): number {\n if (hook === undefined) {\n return this.threshold;\n }\n return this.hookThresholds[hook] ?? this.threshold;\n }\n\n private async postWithRetry<T>(\n payload: SingleClassifyPayload | BatchClassifyPayload,\n maxRetries: number = DEFAULT_MAX_RETRIES,\n ): Promise<T> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const response = await fetch(this.apiUrl, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (response.status !== 429 || attempt === maxRetries) {\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new SilmarilApiError({\n status: response.status,\n statusText: response.statusText,\n body,\n });\n }\n return (await response.json()) as T;\n }\n const waitSeconds = Math.min(2 ** attempt, MAX_BACKOFF_SECONDS);\n await new Promise<void>((resolve) => setTimeout(resolve, waitSeconds * 1000));\n }\n throw new Error(\"Firewall: exhausted retries (unreachable)\");\n }\n}\n"],"mappings":"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","names":["findLastUserMessage","text"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
2
|
+
|
|
1
3
|
declare const HookLabel: {
|
|
2
4
|
readonly USER_INPUT: "user_input";
|
|
3
5
|
readonly SYSTEM_PROMPT: "system_prompt";
|
|
@@ -31,6 +33,10 @@ type Prediction = "BENIGN" | "MALICIOUS";
|
|
|
31
33
|
interface BlockResult {
|
|
32
34
|
readonly prediction: Prediction;
|
|
33
35
|
readonly score: number;
|
|
36
|
+
readonly primaryOutcome?: string;
|
|
37
|
+
readonly outcomeScores?: Readonly<Record<string, number>>;
|
|
38
|
+
readonly detectorScores?: Readonly<Record<string, number>>;
|
|
39
|
+
readonly detectorCounts?: Readonly<Record<string, number>>;
|
|
34
40
|
}
|
|
35
41
|
interface FirewallOptions {
|
|
36
42
|
apiKey: string;
|
|
@@ -38,14 +44,18 @@ interface FirewallOptions {
|
|
|
38
44
|
threshold?: number;
|
|
39
45
|
timeoutMs?: number;
|
|
40
46
|
hookThresholds?: Partial<Record<HookLabel, number>>;
|
|
47
|
+
shadowMode?: boolean;
|
|
41
48
|
}
|
|
49
|
+
type ClassificationMetadata = Readonly<Record<string, unknown>>;
|
|
42
50
|
interface ClassifyOptions {
|
|
43
51
|
hook?: HookLabel;
|
|
44
52
|
toolName?: string;
|
|
53
|
+
metadata?: ClassificationMetadata;
|
|
45
54
|
}
|
|
46
55
|
interface ClassifyBatchOptions {
|
|
47
56
|
hooks?: readonly HookLabel[];
|
|
48
57
|
toolNames?: readonly (string | undefined)[];
|
|
58
|
+
metadata?: readonly (ClassificationMetadata | undefined)[];
|
|
49
59
|
}
|
|
50
60
|
interface LangChainAdapterOptions {
|
|
51
61
|
threshold?: number;
|
|
@@ -55,6 +65,8 @@ interface LangChainAdapterOptions {
|
|
|
55
65
|
includeTool?: boolean;
|
|
56
66
|
failOpen?: boolean;
|
|
57
67
|
logger?: (message: string, error: unknown) => void;
|
|
68
|
+
shadowMode?: boolean;
|
|
69
|
+
onClassify?: (event: ClassifyEvent) => void;
|
|
58
70
|
}
|
|
59
71
|
interface ClassifyEvent {
|
|
60
72
|
readonly hook: HookLabel;
|
|
@@ -62,6 +74,7 @@ interface ClassifyEvent {
|
|
|
62
74
|
readonly text: string;
|
|
63
75
|
readonly result: BlockResult;
|
|
64
76
|
readonly blocked: boolean;
|
|
77
|
+
readonly shadowMode: boolean;
|
|
65
78
|
}
|
|
66
79
|
interface MiddlewareOptions {
|
|
67
80
|
scanInput?: boolean;
|
|
@@ -69,6 +82,7 @@ interface MiddlewareOptions {
|
|
|
69
82
|
scanToolCalls?: boolean;
|
|
70
83
|
threshold?: number;
|
|
71
84
|
hookThresholds?: Partial<Record<HookLabel, number>>;
|
|
85
|
+
shadowMode?: boolean;
|
|
72
86
|
onBlocked?: (err: Error) => void;
|
|
73
87
|
onClassify?: (event: ClassifyEvent) => void;
|
|
74
88
|
}
|
|
@@ -124,11 +138,12 @@ declare class Firewall {
|
|
|
124
138
|
readonly threshold: number;
|
|
125
139
|
readonly timeoutMs: number;
|
|
126
140
|
readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;
|
|
141
|
+
readonly shadowMode: boolean;
|
|
127
142
|
private readonly headers;
|
|
128
143
|
constructor(options: FirewallOptions);
|
|
129
144
|
classify(text: string, options?: ClassifyOptions): Promise<BlockResult>;
|
|
130
145
|
classifyBatch(texts: readonly string[], options?: ClassifyBatchOptions): Promise<BlockResult[]>;
|
|
131
|
-
asLangChainHandler(options?: LangChainAdapterOptions): Promise<
|
|
146
|
+
asLangChainHandler(options?: LangChainAdapterOptions): Promise<BaseCallbackHandler>;
|
|
132
147
|
asMiddleware(options?: MiddlewareOptions): FirewallMiddleware;
|
|
133
148
|
effectiveThreshold(hook: HookLabel | undefined): number;
|
|
134
149
|
private postWithRetry;
|
|
@@ -166,4 +181,4 @@ declare const CHUNK_WINDOW_CHARS: number;
|
|
|
166
181
|
declare const CHUNK_OVERLAP_CHARS: number;
|
|
167
182
|
declare function chunkText(text: string): string[];
|
|
168
183
|
|
|
169
|
-
export { ALL_HOOKS, type BlockResult, CHUNK_OVERLAP, CHUNK_OVERLAP_CHARS, CHUNK_WINDOW, CHUNK_WINDOW_CHARS, type ClassifyBatchOptions, type ClassifyEvent, type ClassifyOptions, DEFAULT_HOOKS, DEFAULT_HOOK_THRESHOLDS, DEFAULT_THRESHOLD, DEFAULT_TIMEOUT_MS, FIREWALL_HOOK_TO_LABEL, Firewall, FirewallHook, type FirewallMiddleware, type FirewallOptions, HookLabel, INPUT_HOOKS, type LangChainAdapterOptions, MAX_INPUT_CHARS, MAX_INPUT_TOKENS, type MiddlewareOptions, OUTPUT_HOOKS, type Prediction, PromptBlockedException, SilmarilApiError, chunkText, prependHook, prependToolName, resolveHooks };
|
|
184
|
+
export { ALL_HOOKS, type BlockResult, CHUNK_OVERLAP, CHUNK_OVERLAP_CHARS, CHUNK_WINDOW, CHUNK_WINDOW_CHARS, type ClassificationMetadata, type ClassifyBatchOptions, type ClassifyEvent, type ClassifyOptions, DEFAULT_HOOKS, DEFAULT_HOOK_THRESHOLDS, DEFAULT_THRESHOLD, DEFAULT_TIMEOUT_MS, FIREWALL_HOOK_TO_LABEL, Firewall, FirewallHook, type FirewallMiddleware, type FirewallOptions, HookLabel, INPUT_HOOKS, type LangChainAdapterOptions, MAX_INPUT_CHARS, MAX_INPUT_TOKENS, type MiddlewareOptions, OUTPUT_HOOKS, type Prediction, PromptBlockedException, SilmarilApiError, chunkText, prependHook, prependToolName, resolveHooks };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
2
|
+
|
|
1
3
|
declare const HookLabel: {
|
|
2
4
|
readonly USER_INPUT: "user_input";
|
|
3
5
|
readonly SYSTEM_PROMPT: "system_prompt";
|
|
@@ -31,6 +33,10 @@ type Prediction = "BENIGN" | "MALICIOUS";
|
|
|
31
33
|
interface BlockResult {
|
|
32
34
|
readonly prediction: Prediction;
|
|
33
35
|
readonly score: number;
|
|
36
|
+
readonly primaryOutcome?: string;
|
|
37
|
+
readonly outcomeScores?: Readonly<Record<string, number>>;
|
|
38
|
+
readonly detectorScores?: Readonly<Record<string, number>>;
|
|
39
|
+
readonly detectorCounts?: Readonly<Record<string, number>>;
|
|
34
40
|
}
|
|
35
41
|
interface FirewallOptions {
|
|
36
42
|
apiKey: string;
|
|
@@ -38,14 +44,18 @@ interface FirewallOptions {
|
|
|
38
44
|
threshold?: number;
|
|
39
45
|
timeoutMs?: number;
|
|
40
46
|
hookThresholds?: Partial<Record<HookLabel, number>>;
|
|
47
|
+
shadowMode?: boolean;
|
|
41
48
|
}
|
|
49
|
+
type ClassificationMetadata = Readonly<Record<string, unknown>>;
|
|
42
50
|
interface ClassifyOptions {
|
|
43
51
|
hook?: HookLabel;
|
|
44
52
|
toolName?: string;
|
|
53
|
+
metadata?: ClassificationMetadata;
|
|
45
54
|
}
|
|
46
55
|
interface ClassifyBatchOptions {
|
|
47
56
|
hooks?: readonly HookLabel[];
|
|
48
57
|
toolNames?: readonly (string | undefined)[];
|
|
58
|
+
metadata?: readonly (ClassificationMetadata | undefined)[];
|
|
49
59
|
}
|
|
50
60
|
interface LangChainAdapterOptions {
|
|
51
61
|
threshold?: number;
|
|
@@ -55,6 +65,8 @@ interface LangChainAdapterOptions {
|
|
|
55
65
|
includeTool?: boolean;
|
|
56
66
|
failOpen?: boolean;
|
|
57
67
|
logger?: (message: string, error: unknown) => void;
|
|
68
|
+
shadowMode?: boolean;
|
|
69
|
+
onClassify?: (event: ClassifyEvent) => void;
|
|
58
70
|
}
|
|
59
71
|
interface ClassifyEvent {
|
|
60
72
|
readonly hook: HookLabel;
|
|
@@ -62,6 +74,7 @@ interface ClassifyEvent {
|
|
|
62
74
|
readonly text: string;
|
|
63
75
|
readonly result: BlockResult;
|
|
64
76
|
readonly blocked: boolean;
|
|
77
|
+
readonly shadowMode: boolean;
|
|
65
78
|
}
|
|
66
79
|
interface MiddlewareOptions {
|
|
67
80
|
scanInput?: boolean;
|
|
@@ -69,6 +82,7 @@ interface MiddlewareOptions {
|
|
|
69
82
|
scanToolCalls?: boolean;
|
|
70
83
|
threshold?: number;
|
|
71
84
|
hookThresholds?: Partial<Record<HookLabel, number>>;
|
|
85
|
+
shadowMode?: boolean;
|
|
72
86
|
onBlocked?: (err: Error) => void;
|
|
73
87
|
onClassify?: (event: ClassifyEvent) => void;
|
|
74
88
|
}
|
|
@@ -124,11 +138,12 @@ declare class Firewall {
|
|
|
124
138
|
readonly threshold: number;
|
|
125
139
|
readonly timeoutMs: number;
|
|
126
140
|
readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;
|
|
141
|
+
readonly shadowMode: boolean;
|
|
127
142
|
private readonly headers;
|
|
128
143
|
constructor(options: FirewallOptions);
|
|
129
144
|
classify(text: string, options?: ClassifyOptions): Promise<BlockResult>;
|
|
130
145
|
classifyBatch(texts: readonly string[], options?: ClassifyBatchOptions): Promise<BlockResult[]>;
|
|
131
|
-
asLangChainHandler(options?: LangChainAdapterOptions): Promise<
|
|
146
|
+
asLangChainHandler(options?: LangChainAdapterOptions): Promise<BaseCallbackHandler>;
|
|
132
147
|
asMiddleware(options?: MiddlewareOptions): FirewallMiddleware;
|
|
133
148
|
effectiveThreshold(hook: HookLabel | undefined): number;
|
|
134
149
|
private postWithRetry;
|
|
@@ -166,4 +181,4 @@ declare const CHUNK_WINDOW_CHARS: number;
|
|
|
166
181
|
declare const CHUNK_OVERLAP_CHARS: number;
|
|
167
182
|
declare function chunkText(text: string): string[];
|
|
168
183
|
|
|
169
|
-
export { ALL_HOOKS, type BlockResult, CHUNK_OVERLAP, CHUNK_OVERLAP_CHARS, CHUNK_WINDOW, CHUNK_WINDOW_CHARS, type ClassifyBatchOptions, type ClassifyEvent, type ClassifyOptions, DEFAULT_HOOKS, DEFAULT_HOOK_THRESHOLDS, DEFAULT_THRESHOLD, DEFAULT_TIMEOUT_MS, FIREWALL_HOOK_TO_LABEL, Firewall, FirewallHook, type FirewallMiddleware, type FirewallOptions, HookLabel, INPUT_HOOKS, type LangChainAdapterOptions, MAX_INPUT_CHARS, MAX_INPUT_TOKENS, type MiddlewareOptions, OUTPUT_HOOKS, type Prediction, PromptBlockedException, SilmarilApiError, chunkText, prependHook, prependToolName, resolveHooks };
|
|
184
|
+
export { ALL_HOOKS, type BlockResult, CHUNK_OVERLAP, CHUNK_OVERLAP_CHARS, CHUNK_WINDOW, CHUNK_WINDOW_CHARS, type ClassificationMetadata, type ClassifyBatchOptions, type ClassifyEvent, type ClassifyOptions, DEFAULT_HOOKS, DEFAULT_HOOK_THRESHOLDS, DEFAULT_THRESHOLD, DEFAULT_TIMEOUT_MS, FIREWALL_HOOK_TO_LABEL, Firewall, FirewallHook, type FirewallMiddleware, type FirewallOptions, HookLabel, INPUT_HOOKS, type LangChainAdapterOptions, MAX_INPUT_CHARS, MAX_INPUT_TOKENS, type MiddlewareOptions, OUTPUT_HOOKS, type Prediction, PromptBlockedException, SilmarilApiError, chunkText, prependHook, prependToolName, resolveHooks };
|
package/dist/index.js
CHANGED
|
@@ -129,10 +129,10 @@ var init_hooks = __esm({
|
|
|
129
129
|
};
|
|
130
130
|
DEFAULT_HOOK_THRESHOLDS = {
|
|
131
131
|
[HookLabel.USER_INPUT]: 0.5,
|
|
132
|
-
[HookLabel.SYSTEM_PROMPT]: 0.
|
|
133
|
-
[HookLabel.TOOL_CALL]: 0.
|
|
134
|
-
[HookLabel.TOOL_RESPONSE]: 0.
|
|
135
|
-
[HookLabel.LLM_OUTPUT]: 0.
|
|
132
|
+
[HookLabel.SYSTEM_PROMPT]: 0.5,
|
|
133
|
+
[HookLabel.TOOL_CALL]: 0.5,
|
|
134
|
+
[HookLabel.TOOL_RESPONSE]: 0.5,
|
|
135
|
+
[HookLabel.LLM_OUTPUT]: 0.5,
|
|
136
136
|
[HookLabel.UNKNOWN]: 0.5
|
|
137
137
|
};
|
|
138
138
|
FIREWALL_HOOK_VALUES = new Set(Object.values(FirewallHook));
|
|
@@ -223,6 +223,8 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
223
223
|
});
|
|
224
224
|
const overrideThreshold = options.threshold;
|
|
225
225
|
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
226
|
+
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
227
|
+
const onClassify = options.onClassify;
|
|
226
228
|
const effectiveThreshold = (label) => {
|
|
227
229
|
if (overrideHookThresholds[label] !== void 0) {
|
|
228
230
|
return overrideHookThresholds[label];
|
|
@@ -232,29 +234,47 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
232
234
|
}
|
|
233
235
|
return overrideThreshold ?? firewall.threshold;
|
|
234
236
|
};
|
|
237
|
+
const fireOnClassify = (hookLabel, text, result, blocked, toolName) => {
|
|
238
|
+
if (!onClassify) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
onClassify({
|
|
243
|
+
hook: hookLabel,
|
|
244
|
+
...toolName !== void 0 ? { toolName } : {},
|
|
245
|
+
text,
|
|
246
|
+
result,
|
|
247
|
+
blocked,
|
|
248
|
+
shadowMode
|
|
249
|
+
});
|
|
250
|
+
} catch (err) {
|
|
251
|
+
logger("onClassify callback threw", err);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
235
254
|
const classify = async (text, hookLabel, runId, toolName) => {
|
|
255
|
+
let result;
|
|
236
256
|
try {
|
|
237
|
-
|
|
257
|
+
result = await firewall.classify(text, {
|
|
238
258
|
hook: hookLabel,
|
|
239
259
|
...toolName !== void 0 ? { toolName } : {}
|
|
240
260
|
});
|
|
241
|
-
const threshold = effectiveThreshold(hookLabel);
|
|
242
|
-
if (result.score >= threshold) {
|
|
243
|
-
throw new PromptBlockedException({
|
|
244
|
-
score: result.score,
|
|
245
|
-
threshold,
|
|
246
|
-
promptText: text,
|
|
247
|
-
runId
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
261
|
} catch (err) {
|
|
251
|
-
if (err instanceof PromptBlockedException) {
|
|
252
|
-
throw err;
|
|
253
|
-
}
|
|
254
262
|
if (!failOpen) {
|
|
255
263
|
throw err;
|
|
256
264
|
}
|
|
257
265
|
logger("classification failed, allowing prompt through", err);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const threshold = effectiveThreshold(hookLabel);
|
|
269
|
+
const blocked = result.score >= threshold;
|
|
270
|
+
fireOnClassify(hookLabel, text, result, blocked, toolName);
|
|
271
|
+
if (blocked && !shadowMode) {
|
|
272
|
+
throw new PromptBlockedException({
|
|
273
|
+
score: result.score,
|
|
274
|
+
threshold,
|
|
275
|
+
promptText: text,
|
|
276
|
+
runId
|
|
277
|
+
});
|
|
258
278
|
}
|
|
259
279
|
};
|
|
260
280
|
class SilmarilFirewallHandler extends BaseCallbackHandler {
|
|
@@ -301,8 +321,7 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
301
321
|
return;
|
|
302
322
|
}
|
|
303
323
|
const toolName = tool?.name;
|
|
304
|
-
|
|
305
|
-
await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);
|
|
324
|
+
await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);
|
|
306
325
|
}
|
|
307
326
|
async handleRetrieverStart(_retriever, query, runId) {
|
|
308
327
|
if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {
|
|
@@ -333,8 +352,7 @@ async function createLangChainHandler(firewall, options = {}) {
|
|
|
333
352
|
return;
|
|
334
353
|
}
|
|
335
354
|
const toolName = _kwargs?.name;
|
|
336
|
-
|
|
337
|
-
await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);
|
|
355
|
+
await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);
|
|
338
356
|
}
|
|
339
357
|
async handleRetrieverEnd(documents, runId) {
|
|
340
358
|
if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {
|
|
@@ -422,6 +440,7 @@ function createMiddleware(firewall, options = {}) {
|
|
|
422
440
|
const scanOutput = options.scanOutput ?? false;
|
|
423
441
|
const overrideThreshold = options.threshold;
|
|
424
442
|
const overrideHookThresholds = options.hookThresholds ?? {};
|
|
443
|
+
const shadowMode = options.shadowMode ?? firewall.shadowMode;
|
|
425
444
|
const effectiveThreshold = (label) => {
|
|
426
445
|
if (overrideHookThresholds[label] !== void 0) {
|
|
427
446
|
return overrideHookThresholds[label];
|
|
@@ -446,9 +465,10 @@ function createMiddleware(firewall, options = {}) {
|
|
|
446
465
|
...toolName !== void 0 ? { toolName } : {},
|
|
447
466
|
text,
|
|
448
467
|
result,
|
|
449
|
-
blocked
|
|
468
|
+
blocked,
|
|
469
|
+
shadowMode
|
|
450
470
|
});
|
|
451
|
-
if (blocked) {
|
|
471
|
+
if (blocked && !shadowMode) {
|
|
452
472
|
const err = new PromptBlockedException({
|
|
453
473
|
score: result.score,
|
|
454
474
|
threshold,
|
|
@@ -588,12 +608,38 @@ var DEFAULT_THRESHOLD = 0.5;
|
|
|
588
608
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
589
609
|
var DEFAULT_MAX_RETRIES = 5;
|
|
590
610
|
var MAX_BACKOFF_SECONDS = 30;
|
|
611
|
+
function blockResultFromResponse(data) {
|
|
612
|
+
const result = {
|
|
613
|
+
prediction: data.prediction,
|
|
614
|
+
score: Number(data.score)
|
|
615
|
+
};
|
|
616
|
+
if (data.primary_outcome !== void 0) {
|
|
617
|
+
result.primaryOutcome = data.primary_outcome;
|
|
618
|
+
}
|
|
619
|
+
if (data.outcome_scores !== void 0) {
|
|
620
|
+
result.outcomeScores = Object.freeze(
|
|
621
|
+
Object.fromEntries(Object.entries(data.outcome_scores).map(([k, v]) => [k, Number(v)]))
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (data.detector_scores !== void 0) {
|
|
625
|
+
result.detectorScores = Object.freeze(
|
|
626
|
+
Object.fromEntries(Object.entries(data.detector_scores).map(([k, v]) => [k, Number(v)]))
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
if (data.detector_counts !== void 0) {
|
|
630
|
+
result.detectorCounts = Object.freeze(
|
|
631
|
+
Object.fromEntries(Object.entries(data.detector_counts).map(([k, v]) => [k, Number(v)]))
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
return Object.freeze(result);
|
|
635
|
+
}
|
|
591
636
|
var Firewall = class {
|
|
592
637
|
apiKey;
|
|
593
638
|
apiUrl;
|
|
594
639
|
threshold;
|
|
595
640
|
timeoutMs;
|
|
596
641
|
hookThresholds;
|
|
642
|
+
shadowMode;
|
|
597
643
|
headers;
|
|
598
644
|
constructor(options) {
|
|
599
645
|
if (!options.apiKey) {
|
|
@@ -606,7 +652,8 @@ var Firewall = class {
|
|
|
606
652
|
this.apiUrl = options.apiUrl;
|
|
607
653
|
this.threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
608
654
|
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
609
|
-
this.hookThresholds = options.hookThresholds ?? {};
|
|
655
|
+
this.hookThresholds = Object.freeze({ ...options.hookThresholds ?? {} });
|
|
656
|
+
this.shadowMode = options.shadowMode ?? false;
|
|
610
657
|
this.headers = Object.freeze({
|
|
611
658
|
"x-api-key": this.apiKey,
|
|
612
659
|
"content-type": "application/json"
|
|
@@ -622,8 +669,11 @@ var Firewall = class {
|
|
|
622
669
|
if (options.toolName !== void 0) {
|
|
623
670
|
payload.tool_name = options.toolName;
|
|
624
671
|
}
|
|
672
|
+
if (options.metadata !== void 0) {
|
|
673
|
+
payload.metadata = options.metadata;
|
|
674
|
+
}
|
|
625
675
|
const data = await this.postWithRetry(payload);
|
|
626
|
-
return
|
|
676
|
+
return blockResultFromResponse(data);
|
|
627
677
|
}
|
|
628
678
|
const batchOpts = {};
|
|
629
679
|
if (options.hook !== void 0) {
|
|
@@ -632,10 +682,28 @@ var Firewall = class {
|
|
|
632
682
|
if (options.toolName !== void 0) {
|
|
633
683
|
batchOpts.toolNames = chunks.map(() => options.toolName);
|
|
634
684
|
}
|
|
685
|
+
if (options.metadata !== void 0) {
|
|
686
|
+
batchOpts.metadata = chunks.map(() => options.metadata);
|
|
687
|
+
}
|
|
635
688
|
const results = await this.classifyBatch(chunks, batchOpts);
|
|
636
689
|
return results.reduce((best, r) => r.score > best.score ? r : best);
|
|
637
690
|
}
|
|
638
691
|
async classifyBatch(texts, options = {}) {
|
|
692
|
+
if (options.hooks !== void 0 && options.hooks.length !== texts.length) {
|
|
693
|
+
throw new Error(
|
|
694
|
+
`Firewall: hooks length ${options.hooks.length} does not match texts length ${texts.length}`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
if (options.toolNames !== void 0 && options.toolNames.length !== texts.length) {
|
|
698
|
+
throw new Error(
|
|
699
|
+
`Firewall: toolNames length ${options.toolNames.length} does not match texts length ${texts.length}`
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
if (options.metadata !== void 0 && options.metadata.length !== texts.length) {
|
|
703
|
+
throw new Error(
|
|
704
|
+
`Firewall: metadata length ${options.metadata.length} does not match texts length ${texts.length}`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
639
707
|
const payload = { texts };
|
|
640
708
|
if (options.hooks && options.hooks.length > 0) {
|
|
641
709
|
payload.hooks = options.hooks.map((h) => String(h));
|
|
@@ -643,10 +711,11 @@ var Firewall = class {
|
|
|
643
711
|
if (options.toolNames && options.toolNames.length > 0) {
|
|
644
712
|
payload.tool_names = options.toolNames.map((t) => t === void 0 ? null : t);
|
|
645
713
|
}
|
|
714
|
+
if (options.metadata && options.metadata.length > 0) {
|
|
715
|
+
payload.metadata = options.metadata.map((m) => m === void 0 ? null : m);
|
|
716
|
+
}
|
|
646
717
|
const data = await this.postWithRetry(payload);
|
|
647
|
-
return data.predictions.map(
|
|
648
|
-
(p) => Object.freeze({ prediction: p.prediction, score: Number(p.score) })
|
|
649
|
-
);
|
|
718
|
+
return data.predictions.map((p) => blockResultFromResponse(p));
|
|
650
719
|
}
|
|
651
720
|
asLangChainHandler(options = {}) {
|
|
652
721
|
return Promise.resolve().then(() => (init_langchain(), langchain_exports)).then((m) => m.createLangChainHandler(this, options));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/exceptions.ts","../src/hooks.ts","../src/utils/extract.ts","../src/adapters/langchain.ts","../src/adapters/vercel.ts","../src/chunking.ts","../src/firewall.ts","../src/index.ts"],"sourcesContent":["// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst MAX_PROMPT_DISPLAY_LEN = 100;\n\nexport class PromptBlockedException extends Error {\n readonly score: number;\n readonly threshold: number;\n readonly promptText: string;\n readonly runId: string | undefined;\n\n constructor(params: {\n score: number;\n threshold: number;\n promptText: string;\n runId?: string;\n }) {\n super(PromptBlockedException.formatMessage(params));\n this.name = \"PromptBlockedException\";\n this.score = params.score;\n this.threshold = params.threshold;\n this.promptText = params.promptText;\n this.runId = params.runId;\n Object.setPrototypeOf(this, PromptBlockedException.prototype);\n }\n\n private static formatMessage(params: {\n score: number;\n threshold: number;\n promptText: string;\n }): string {\n const truncated =\n params.promptText.length > MAX_PROMPT_DISPLAY_LEN\n ? `${params.promptText.slice(0, MAX_PROMPT_DISPLAY_LEN)}...`\n : params.promptText;\n return (\n `Prompt blocked by Silmaril Firewall ` +\n `(score=${params.score.toFixed(4)}, threshold=${params.threshold.toFixed(4)}): ` +\n `'${truncated}'`\n );\n }\n}\n\nexport class SilmarilApiError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly body: string;\n\n constructor(params: { status: number; statusText: string; body: string }) {\n super(`Silmaril API error ${params.status} ${params.statusText}: ${params.body}`);\n this.name = \"SilmarilApiError\";\n this.status = params.status;\n this.statusText = params.statusText;\n this.body = params.body;\n Object.setPrototypeOf(this, SilmarilApiError.prototype);\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport const HookLabel = {\n USER_INPUT: \"user_input\",\n SYSTEM_PROMPT: \"system_prompt\",\n TOOL_CALL: \"tool_call\",\n TOOL_RESPONSE: \"tool_response\",\n LLM_OUTPUT: \"llm_output\",\n UNKNOWN: \"unknown\",\n} as const;\n\nexport type HookLabel = (typeof HookLabel)[keyof typeof HookLabel];\n\nexport const FirewallHook = {\n LLM_START: \"on_llm_start\",\n CHAT_MODEL_START: \"on_chat_model_start\",\n TOOL_START: \"on_tool_start\",\n RETRIEVER_START: \"on_retriever_start\",\n LLM_END: \"on_llm_end\",\n TOOL_END: \"on_tool_end\",\n RETRIEVER_END: \"on_retriever_end\",\n} as const;\n\nexport type FirewallHook = (typeof FirewallHook)[keyof typeof FirewallHook];\n\nexport const DEFAULT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n]);\n\nexport const INPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.RETRIEVER_START,\n]);\n\nexport const OUTPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_END,\n FirewallHook.TOOL_END,\n FirewallHook.RETRIEVER_END,\n]);\n\nexport const ALL_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n ...INPUT_HOOKS,\n ...OUTPUT_HOOKS,\n]);\n\nexport const FIREWALL_HOOK_TO_LABEL: Readonly<Record<FirewallHook, HookLabel>> = {\n [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,\n [FirewallHook.LLM_START]: HookLabel.USER_INPUT,\n [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,\n [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,\n [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT,\n};\n\nexport const DEFAULT_HOOK_THRESHOLDS: Readonly<Record<HookLabel, number>> = {\n [HookLabel.USER_INPUT]: 0.5,\n [HookLabel.SYSTEM_PROMPT]: 0.3,\n [HookLabel.TOOL_CALL]: 0.4,\n [HookLabel.TOOL_RESPONSE]: 0.3,\n [HookLabel.LLM_OUTPUT]: 0.4,\n [HookLabel.UNKNOWN]: 0.5,\n};\n\nconst FIREWALL_HOOK_VALUES: ReadonlySet<string> = new Set(Object.values(FirewallHook));\n\nexport function resolveHooks(\n hooks: Iterable<FirewallHook | string> | undefined,\n): ReadonlySet<FirewallHook> {\n if (hooks === undefined) {\n return DEFAULT_HOOKS;\n }\n const resolved = new Set<FirewallHook>();\n for (const h of hooks) {\n if (!FIREWALL_HOOK_VALUES.has(h)) {\n throw new Error(`Invalid FirewallHook value: ${String(h)}`);\n }\n resolved.add(h as FirewallHook);\n }\n return resolved;\n}\n\nexport function prependHook(text: string, hook: HookLabel | string | undefined | null): string {\n if (hook === undefined || hook === null) {\n return text;\n }\n if (hook === HookLabel.UNKNOWN) {\n return text;\n }\n return `[HOOK:${hook}] ${text}`;\n}\n\nexport function prependToolName(text: string, toolName: string | undefined | null): string {\n if (toolName === undefined || toolName === null) {\n return text;\n }\n return `[TOOL:${toolName}] ${text}`;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst SKIP_ROLES: ReadonlySet<string> = new Set([\"ai\", \"assistant\"]);\nconst SYSTEM_ROLES: ReadonlySet<string> = new Set([\"system\"]);\nconst TOOL_ROLES: ReadonlySet<string> = new Set([\"tool\", \"function\"]);\n\ntype MessageContent = string | ReadonlyArray<string | { type?: string; text?: string }>;\n\ninterface DucktypedMessage {\n role?: string;\n type?: string;\n content?: MessageContent;\n}\n\ninterface DucktypedGeneration {\n text?: string;\n}\n\ninterface DucktypedLLMResult {\n generations?: ReadonlyArray<ReadonlyArray<DucktypedGeneration>>;\n}\n\ninterface DucktypedDocument {\n pageContent?: string;\n}\n\nfunction extractContentText(content: MessageContent | undefined): string {\n if (typeof content === \"string\") {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && typeof block === \"object\" && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n }\n return \"\";\n}\n\nfunction getRole(message: DucktypedMessage): string {\n if (message.role !== undefined) {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nexport function extractTextFromMessages(\n messages: ReadonlyArray<DucktypedMessage>,\n options: { includeSystem?: boolean; includeTool?: boolean } = {},\n): string {\n const includeSystem = options.includeSystem ?? true;\n const includeTool = options.includeTool ?? true;\n const parts: string[] = [];\n for (const message of messages) {\n const role = getRole(message);\n if (SKIP_ROLES.has(role)) {\n continue;\n }\n if (!includeSystem && SYSTEM_ROLES.has(role)) {\n continue;\n }\n if (!includeTool && TOOL_ROLES.has(role)) {\n continue;\n }\n const text = extractContentText(message.content);\n const stripped = text.trim();\n if (stripped) {\n parts.push(stripped);\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromPrompts(prompts: readonly string[]): string {\n return prompts\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n .join(\"\\n\");\n}\n\nexport function extractTextFromToolInput(inputStr: string): string {\n return inputStr.trim();\n}\n\nexport function extractTextFromLLMResult(response: DucktypedLLMResult): string {\n const parts: string[] = [];\n for (const genList of response.generations ?? []) {\n for (const gen of genList) {\n const text = (gen.text ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromDocuments(documents: ReadonlyArray<DucktypedDocument>): string {\n const parts: string[] = [];\n for (const doc of documents) {\n const text = (doc.pageContent ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n return parts.join(\"\\n\");\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport {\n FIREWALL_HOOK_TO_LABEL,\n FirewallHook,\n HookLabel,\n prependToolName,\n resolveHooks,\n} from \"../hooks.js\";\nimport type { LangChainAdapterOptions } from \"../types.js\";\nimport {\n extractTextFromDocuments,\n extractTextFromLLMResult,\n extractTextFromPrompts,\n extractTextFromToolInput,\n} from \"../utils/extract.js\";\n\nconst USER_ROLES: ReadonlySet<string> = new Set([\"human\", \"user\"]);\n\ninterface LangChainDucktypedMessage {\n role?: string;\n type?: string;\n content?: string | ReadonlyArray<string | { type?: string; text?: string }>;\n}\n\nfunction getMessageRole(message: LangChainDucktypedMessage): string {\n if (typeof message.role === \"string\") {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nfunction extractMessageText(content: LangChainDucktypedMessage[\"content\"]): string {\n if (content === undefined) {\n return \"\";\n }\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<LangChainDucktypedMessage>,\n): LangChainDucktypedMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (USER_ROLES.has(getMessageRole(msg))) {\n return msg;\n }\n }\n return undefined;\n}\n\nexport async function createLangChainHandler(\n firewall: Firewall,\n options: LangChainAdapterOptions = {},\n): Promise<unknown> {\n const { BaseCallbackHandler } = await import(\"@langchain/core/callbacks/base\");\n\n const enabledHooks = resolveHooks(options.hooks);\n // includeSystem / includeTool are retained in LangChainAdapterOptions for\n // backwards compat but are inert under the per-message hook-routing algorithm:\n // handleChatModelStart only scans the last user-role message; system and tool\n // messages are either trusted or already classified by their own hooks.\n const failOpen = options.failOpen ?? true;\n const logger =\n options.logger ??\n ((message: string, error: unknown): void => {\n // eslint-disable-next-line no-console\n console.warn(`silmaril.firewall: ${message}`, error);\n });\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classify = async (\n text: string,\n hookLabel: HookLabel,\n runId: string,\n toolName?: string,\n ): Promise<void> => {\n try {\n const result = await firewall.classify(text, {\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n });\n const threshold = effectiveThreshold(hookLabel);\n if (result.score >= threshold) {\n throw new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n runId,\n });\n }\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n throw err;\n }\n if (!failOpen) {\n throw err;\n }\n logger(\"classification failed, allowing prompt through\", err);\n }\n };\n\n class SilmarilFirewallHandler extends BaseCallbackHandler {\n override name = \"silmaril_firewall_handler\";\n override raiseError = true;\n override awaitHandlers = true;\n\n override async handleChatModelStart(\n _llm: unknown,\n messages: unknown,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {\n return;\n }\n const batches = messages as ReadonlyArray<ReadonlyArray<LangChainDucktypedMessage>>;\n const flat: LangChainDucktypedMessage[] = [];\n for (const batch of batches) {\n for (const m of batch) {\n flat.push(m);\n }\n }\n const lastUser = findLastUserMessage(flat);\n if (!lastUser) {\n return;\n }\n const text = extractMessageText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);\n }\n\n override async handleLLMStart(\n _llm: unknown,\n prompts: string[],\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_START)) {\n return;\n }\n const text = extractTextFromPrompts(prompts);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);\n }\n\n override async handleToolStart(\n tool: { name?: string } | undefined,\n inputStr: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_START)) {\n return;\n }\n const text = extractTextFromToolInput(inputStr);\n if (!text) {\n return;\n }\n const toolName = tool?.name;\n const prefixed = prependToolName(text, toolName);\n await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);\n }\n\n override async handleRetrieverStart(\n _retriever: unknown,\n query: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {\n return;\n }\n const text = query.trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);\n }\n\n override async handleLLMEnd(output: unknown, runId: string): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_END)) {\n return;\n }\n const text = extractTextFromLLMResult(output as { generations?: ReadonlyArray<ReadonlyArray<{ text?: string }>> });\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);\n }\n\n override async handleToolEnd(\n output: unknown,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _kwargs?: { name?: string },\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_END)) {\n return;\n }\n const text = String(output).trim();\n if (!text) {\n return;\n }\n const toolName = _kwargs?.name;\n const prefixed = prependToolName(text, toolName);\n await classify(prefixed, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);\n }\n\n override async handleRetrieverEnd(\n documents: ReadonlyArray<{ pageContent?: string }>,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {\n return;\n }\n const text = extractTextFromDocuments(documents);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);\n }\n }\n\n return new SilmarilFirewallHandler();\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport { HookLabel } from \"../hooks.js\";\nimport type { BlockResult, MiddlewareOptions } from \"../types.js\";\n\ninterface VercelContentPart {\n type?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n}\n\ninterface VercelPromptMessage {\n role: string;\n content: string | ReadonlyArray<VercelContentPart | string>;\n}\n\ninterface VercelToolCall {\n toolCallType?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n}\n\ninterface VercelGenerateResult {\n text?: string;\n toolCalls?: ReadonlyArray<VercelToolCall>;\n}\n\ninterface WrapGenerateArgs<TResult extends VercelGenerateResult & Record<string, unknown>> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doGenerate: () => PromiseLike<TResult>;\n}\n\ninterface WrapStreamArgs<\n TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>,\n> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doStream: () => PromiseLike<TResult>;\n}\n\ninterface StreamPart {\n type?: string;\n textDelta?: string;\n delta?: string;\n}\n\nfunction stringifyToolValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n\nfunction extractContentText(content: VercelPromptMessage[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction iterateToolResultParts(\n message: VercelPromptMessage,\n): Array<{ text: string; toolName: string | undefined }> {\n if (typeof message.content === \"string\") {\n return [];\n }\n const out: Array<{ text: string; toolName: string | undefined }> = [];\n for (const part of message.content) {\n if (typeof part === \"string\") {\n continue;\n }\n if (part.type === \"tool-result\") {\n const text = stringifyToolValue(part.result);\n if (text.trim()) {\n out.push({ text, toolName: part.toolName });\n }\n }\n }\n return out;\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<VercelPromptMessage>,\n): VercelPromptMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role.toLowerCase() === \"user\") {\n return msg;\n }\n }\n return undefined;\n}\n\nexport interface FirewallMiddleware {\n wrapGenerate: <TResult extends VercelGenerateResult & Record<string, unknown>>(\n args: WrapGenerateArgs<TResult>,\n ) => Promise<TResult>;\n wrapStream: <TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>(\n args: WrapStreamArgs<TResult>,\n ) => Promise<TResult>;\n}\n\nexport function createMiddleware(\n firewall: Firewall,\n options: MiddlewareOptions = {},\n): FirewallMiddleware {\n const scanInput = options.scanInput ?? true;\n const scanOutput = options.scanOutput ?? false;\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classifyOrBlock = async (\n text: string,\n hook: HookLabel,\n toolName?: string,\n ): Promise<void> => {\n if (!text.trim()) {\n return;\n }\n const result: BlockResult = await firewall.classify(\n text,\n toolName !== undefined ? { hook, toolName } : { hook },\n );\n const threshold = effectiveThreshold(hook);\n const blocked = result.score >= threshold;\n options.onClassify?.({\n hook,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n });\n if (blocked) {\n const err = new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n });\n options.onBlocked?.(err);\n throw err;\n }\n };\n\n const scanPrompt = async (prompt: ReadonlyArray<VercelPromptMessage>): Promise<void> => {\n if (prompt.length === 0) {\n return;\n }\n const last = prompt[prompt.length - 1]!;\n const role = last.role.toLowerCase();\n\n // If the newest message is a tool result (typical mid-flow in a\n // tool-calling loop), classify each tool-result part individually with\n // tool_response + toolName. No manual wiring from the caller — the\n // toolName is read directly from the Vercel content part.\n if (role === \"tool\") {\n for (const { text, toolName } of iterateToolResultParts(last)) {\n await classifyOrBlock(text, HookLabel.TOOL_RESPONSE, toolName);\n }\n return;\n }\n\n // Otherwise, fall back to scanning the most recent user message.\n // Covers: [user], [system, user], [user, assistant(text), user], etc.\n const lastUser = findLastUserMessage(prompt);\n if (!lastUser) {\n return;\n }\n const text = extractContentText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classifyOrBlock(text, HookLabel.USER_INPUT);\n };\n\n const scanGenerateResult = async (result: VercelGenerateResult): Promise<void> => {\n if (scanOutput && typeof result.text === \"string\" && result.text.length > 0) {\n await classifyOrBlock(result.text, HookLabel.LLM_OUTPUT);\n }\n if (options.scanToolCalls && Array.isArray(result.toolCalls)) {\n for (const call of result.toolCalls) {\n if (!call || typeof call !== \"object\") {\n continue;\n }\n const args =\n typeof call.args === \"string\" ? call.args : stringifyToolValue(call.args);\n const toolName = typeof call.toolName === \"string\" ? call.toolName : undefined;\n if (args.trim()) {\n await classifyOrBlock(args, HookLabel.TOOL_CALL, toolName);\n }\n }\n }\n };\n\n return {\n async wrapGenerate<TResult extends VercelGenerateResult & Record<string, unknown>>({\n params,\n doGenerate,\n }: WrapGenerateArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const result = await doGenerate();\n await scanGenerateResult(result);\n return result;\n },\n\n async wrapStream<TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>({\n params,\n doStream,\n }: WrapStreamArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const { stream, ...rest } = await doStream();\n if (!scanOutput) {\n return { stream, ...rest } as unknown as TResult;\n }\n\n let buffered = \"\";\n const transformed = stream.pipeThrough(\n new TransformStream<unknown, unknown>({\n transform(chunk, controller): void {\n const part = chunk as StreamPart;\n if (part && part.type === \"text-delta\") {\n const delta = part.textDelta ?? part.delta ?? \"\";\n buffered += delta;\n }\n controller.enqueue(chunk);\n },\n async flush(controller): Promise<void> {\n if (!buffered.trim()) {\n return;\n }\n try {\n await classifyOrBlock(buffered, HookLabel.LLM_OUTPUT);\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n controller.enqueue({ type: \"error\", error: err });\n return;\n }\n throw err;\n }\n },\n }),\n );\n return { stream: transformed, ...rest } as unknown as TResult;\n },\n };\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst CHARS_PER_TOKEN = 4;\n\nexport const MAX_INPUT_TOKENS = 10_240;\nexport const CHUNK_WINDOW = 400;\nexport const CHUNK_OVERLAP = 64;\n\nexport const MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;\nexport const CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;\nexport const CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;\nconst CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;\n\nexport function chunkText(text: string): string[] {\n const n = text.length;\n if (n > MAX_INPUT_CHARS) {\n throw new Error(\n `Input has ~${Math.floor(n / CHARS_PER_TOKEN)} tokens (${n} chars); ` +\n `max is ${MAX_INPUT_TOKENS} tokens (${MAX_INPUT_CHARS} chars)`,\n );\n }\n if (n <= CHUNK_WINDOW_CHARS) {\n return [text];\n }\n const chunks: string[] = [];\n for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {\n const end = start + CHUNK_WINDOW_CHARS;\n chunks.push(text.slice(start, end));\n if (end >= n) {\n break;\n }\n }\n return chunks;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport { createMiddleware, type FirewallMiddleware } from \"./adapters/vercel.js\";\nimport { chunkText } from \"./chunking.js\";\nimport { SilmarilApiError } from \"./exceptions.js\";\nimport type { HookLabel } from \"./hooks.js\";\nimport type {\n BlockResult,\n ClassifyBatchOptions,\n ClassifyOptions,\n FirewallOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n Prediction,\n} from \"./types.js\";\n\nexport const DEFAULT_THRESHOLD = 0.5;\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RETRIES = 5;\nconst MAX_BACKOFF_SECONDS = 30;\n\ninterface SingleClassifyPayload {\n text: string;\n hook?: string;\n tool_name?: string;\n}\n\ninterface BatchClassifyPayload {\n texts: readonly string[];\n hooks?: readonly string[];\n tool_names?: readonly (string | null)[];\n}\n\ninterface SingleClassifyResponse {\n prediction: Prediction;\n score: number;\n}\n\ninterface BatchClassifyResponse {\n predictions: readonly SingleClassifyResponse[];\n}\n\nexport class Firewall {\n readonly apiKey: string;\n readonly apiUrl: string;\n readonly threshold: number;\n readonly timeoutMs: number;\n readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;\n\n private readonly headers: Readonly<Record<string, string>>;\n\n constructor(options: FirewallOptions) {\n if (!options.apiKey) {\n throw new Error(\"Firewall: apiKey is required\");\n }\n if (!options.apiUrl) {\n throw new Error(\"Firewall: apiUrl is required\");\n }\n this.apiKey = options.apiKey;\n this.apiUrl = options.apiUrl;\n this.threshold = options.threshold ?? DEFAULT_THRESHOLD;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.hookThresholds = options.hookThresholds ?? {};\n this.headers = Object.freeze({\n \"x-api-key\": this.apiKey,\n \"content-type\": \"application/json\",\n });\n }\n\n async classify(text: string, options: ClassifyOptions = {}): Promise<BlockResult> {\n const chunks = chunkText(text);\n if (chunks.length === 1) {\n const payload: SingleClassifyPayload = { text: chunks[0]! };\n if (options.hook !== undefined) {\n payload.hook = options.hook;\n }\n if (options.toolName !== undefined) {\n payload.tool_name = options.toolName;\n }\n const data = await this.postWithRetry<SingleClassifyResponse>(payload);\n return Object.freeze({ prediction: data.prediction, score: Number(data.score) });\n }\n const batchOpts: ClassifyBatchOptions = {};\n if (options.hook !== undefined) {\n batchOpts.hooks = chunks.map(() => options.hook!);\n }\n if (options.toolName !== undefined) {\n batchOpts.toolNames = chunks.map(() => options.toolName);\n }\n const results = await this.classifyBatch(chunks, batchOpts);\n return results.reduce((best, r) => (r.score > best.score ? r : best));\n }\n\n async classifyBatch(\n texts: readonly string[],\n options: ClassifyBatchOptions = {},\n ): Promise<BlockResult[]> {\n const payload: BatchClassifyPayload = { texts };\n if (options.hooks && options.hooks.length > 0) {\n payload.hooks = options.hooks.map((h) => String(h));\n }\n if (options.toolNames && options.toolNames.length > 0) {\n payload.tool_names = options.toolNames.map((t) => (t === undefined ? null : t));\n }\n const data = await this.postWithRetry<BatchClassifyResponse>(payload);\n return data.predictions.map((p) =>\n Object.freeze({ prediction: p.prediction, score: Number(p.score) }),\n );\n }\n\n asLangChainHandler(options: LangChainAdapterOptions = {}): Promise<unknown> {\n return import(\"./adapters/langchain.js\").then((m) => m.createLangChainHandler(this, options));\n }\n\n asMiddleware(options: MiddlewareOptions = {}): FirewallMiddleware {\n return createMiddleware(this, options);\n }\n\n effectiveThreshold(hook: HookLabel | undefined): number {\n if (hook === undefined) {\n return this.threshold;\n }\n return this.hookThresholds[hook] ?? this.threshold;\n }\n\n private async postWithRetry<T>(\n payload: SingleClassifyPayload | BatchClassifyPayload,\n maxRetries: number = DEFAULT_MAX_RETRIES,\n ): Promise<T> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const response = await fetch(this.apiUrl, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (response.status !== 429 || attempt === maxRetries) {\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new SilmarilApiError({\n status: response.status,\n statusText: response.statusText,\n body,\n });\n }\n return (await response.json()) as T;\n }\n const waitSeconds = Math.min(2 ** attempt, MAX_BACKOFF_SECONDS);\n await new Promise<void>((resolve) => setTimeout(resolve, waitSeconds * 1000));\n }\n throw new Error(\"Firewall: exhausted retries (unreachable)\");\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport {\n Firewall,\n DEFAULT_THRESHOLD,\n DEFAULT_TIMEOUT_MS,\n} from \"./firewall.js\";\n\nexport { PromptBlockedException, SilmarilApiError } from \"./exceptions.js\";\n\nexport type { FirewallMiddleware } from \"./adapters/vercel.js\";\n\nexport {\n chunkText,\n MAX_INPUT_TOKENS,\n CHUNK_WINDOW,\n CHUNK_OVERLAP,\n MAX_INPUT_CHARS,\n CHUNK_WINDOW_CHARS,\n CHUNK_OVERLAP_CHARS,\n} from \"./chunking.js\";\n\nexport {\n HookLabel,\n FirewallHook,\n DEFAULT_HOOKS,\n INPUT_HOOKS,\n OUTPUT_HOOKS,\n ALL_HOOKS,\n DEFAULT_HOOK_THRESHOLDS,\n FIREWALL_HOOK_TO_LABEL,\n resolveHooks,\n prependHook,\n prependToolName,\n} from \"./hooks.js\";\n\nexport type {\n BlockResult,\n ClassifyEvent,\n Prediction,\n FirewallOptions,\n ClassifyOptions,\n ClassifyBatchOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n} from \"./types.js\";\n"],"mappings":"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","names":["findLastUserMessage","text"]}
|
|
1
|
+
{"version":3,"sources":["../src/exceptions.ts","../src/hooks.ts","../src/utils/extract.ts","../src/adapters/langchain.ts","../src/adapters/vercel.ts","../src/chunking.ts","../src/firewall.ts","../src/index.ts"],"sourcesContent":["// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst MAX_PROMPT_DISPLAY_LEN = 100;\n\nexport class PromptBlockedException extends Error {\n readonly score: number;\n readonly threshold: number;\n readonly promptText: string;\n readonly runId: string | undefined;\n\n constructor(params: {\n score: number;\n threshold: number;\n promptText: string;\n runId?: string;\n }) {\n super(PromptBlockedException.formatMessage(params));\n this.name = \"PromptBlockedException\";\n this.score = params.score;\n this.threshold = params.threshold;\n this.promptText = params.promptText;\n this.runId = params.runId;\n Object.setPrototypeOf(this, PromptBlockedException.prototype);\n }\n\n private static formatMessage(params: {\n score: number;\n threshold: number;\n promptText: string;\n }): string {\n const truncated =\n params.promptText.length > MAX_PROMPT_DISPLAY_LEN\n ? `${params.promptText.slice(0, MAX_PROMPT_DISPLAY_LEN)}...`\n : params.promptText;\n return (\n `Prompt blocked by Silmaril Firewall ` +\n `(score=${params.score.toFixed(4)}, threshold=${params.threshold.toFixed(4)}): ` +\n `'${truncated}'`\n );\n }\n}\n\nexport class SilmarilApiError extends Error {\n readonly status: number;\n readonly statusText: string;\n readonly body: string;\n\n constructor(params: { status: number; statusText: string; body: string }) {\n super(`Silmaril API error ${params.status} ${params.statusText}: ${params.body}`);\n this.name = \"SilmarilApiError\";\n this.status = params.status;\n this.statusText = params.statusText;\n this.body = params.body;\n Object.setPrototypeOf(this, SilmarilApiError.prototype);\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport const HookLabel = {\n USER_INPUT: \"user_input\",\n SYSTEM_PROMPT: \"system_prompt\",\n TOOL_CALL: \"tool_call\",\n TOOL_RESPONSE: \"tool_response\",\n LLM_OUTPUT: \"llm_output\",\n UNKNOWN: \"unknown\",\n} as const;\n\nexport type HookLabel = (typeof HookLabel)[keyof typeof HookLabel];\n\nexport const FirewallHook = {\n LLM_START: \"on_llm_start\",\n CHAT_MODEL_START: \"on_chat_model_start\",\n TOOL_START: \"on_tool_start\",\n RETRIEVER_START: \"on_retriever_start\",\n LLM_END: \"on_llm_end\",\n TOOL_END: \"on_tool_end\",\n RETRIEVER_END: \"on_retriever_end\",\n} as const;\n\nexport type FirewallHook = (typeof FirewallHook)[keyof typeof FirewallHook];\n\nexport const DEFAULT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n]);\n\nexport const INPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_START,\n FirewallHook.CHAT_MODEL_START,\n FirewallHook.TOOL_START,\n FirewallHook.RETRIEVER_START,\n]);\n\nexport const OUTPUT_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n FirewallHook.LLM_END,\n FirewallHook.TOOL_END,\n FirewallHook.RETRIEVER_END,\n]);\n\nexport const ALL_HOOKS: ReadonlySet<FirewallHook> = new Set<FirewallHook>([\n ...INPUT_HOOKS,\n ...OUTPUT_HOOKS,\n]);\n\nexport const FIREWALL_HOOK_TO_LABEL: Readonly<Record<FirewallHook, HookLabel>> = {\n [FirewallHook.CHAT_MODEL_START]: HookLabel.USER_INPUT,\n [FirewallHook.LLM_START]: HookLabel.USER_INPUT,\n [FirewallHook.TOOL_START]: HookLabel.TOOL_CALL,\n [FirewallHook.TOOL_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.RETRIEVER_START]: HookLabel.TOOL_CALL,\n [FirewallHook.RETRIEVER_END]: HookLabel.TOOL_RESPONSE,\n [FirewallHook.LLM_END]: HookLabel.LLM_OUTPUT,\n};\n\nexport const DEFAULT_HOOK_THRESHOLDS: Readonly<Record<HookLabel, number>> = {\n [HookLabel.USER_INPUT]: 0.5,\n [HookLabel.SYSTEM_PROMPT]: 0.5,\n [HookLabel.TOOL_CALL]: 0.5,\n [HookLabel.TOOL_RESPONSE]: 0.5,\n [HookLabel.LLM_OUTPUT]: 0.5,\n [HookLabel.UNKNOWN]: 0.5,\n};\n\nconst FIREWALL_HOOK_VALUES: ReadonlySet<string> = new Set(Object.values(FirewallHook));\n\nexport function resolveHooks(\n hooks: Iterable<FirewallHook | string> | undefined,\n): ReadonlySet<FirewallHook> {\n if (hooks === undefined) {\n return DEFAULT_HOOKS;\n }\n const resolved = new Set<FirewallHook>();\n for (const h of hooks) {\n if (!FIREWALL_HOOK_VALUES.has(h)) {\n throw new Error(`Invalid FirewallHook value: ${String(h)}`);\n }\n resolved.add(h as FirewallHook);\n }\n return resolved;\n}\n\nexport function prependHook(text: string, hook: HookLabel | string | undefined | null): string {\n if (hook === undefined || hook === null) {\n return text;\n }\n if (hook === HookLabel.UNKNOWN) {\n return text;\n }\n return `[HOOK:${hook}] ${text}`;\n}\n\nexport function prependToolName(text: string, toolName: string | undefined | null): string {\n if (toolName === undefined || toolName === null) {\n return text;\n }\n return `[TOOL:${toolName}] ${text}`;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst SKIP_ROLES: ReadonlySet<string> = new Set([\"ai\", \"assistant\"]);\nconst SYSTEM_ROLES: ReadonlySet<string> = new Set([\"system\"]);\nconst TOOL_ROLES: ReadonlySet<string> = new Set([\"tool\", \"function\"]);\n\ntype MessageContent = string | ReadonlyArray<string | { type?: string; text?: string }>;\n\ninterface DucktypedMessage {\n role?: string;\n type?: string;\n content?: MessageContent;\n}\n\ninterface DucktypedGeneration {\n text?: string;\n}\n\ninterface DucktypedLLMResult {\n generations?: ReadonlyArray<ReadonlyArray<DucktypedGeneration>>;\n}\n\ninterface DucktypedDocument {\n pageContent?: string;\n}\n\nfunction extractContentText(content: MessageContent | undefined): string {\n if (typeof content === \"string\") {\n return content;\n }\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && typeof block === \"object\" && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n }\n return \"\";\n}\n\nfunction getRole(message: DucktypedMessage): string {\n if (message.role !== undefined) {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nexport function extractTextFromMessages(\n messages: ReadonlyArray<DucktypedMessage>,\n options: { includeSystem?: boolean; includeTool?: boolean } = {},\n): string {\n const includeSystem = options.includeSystem ?? true;\n const includeTool = options.includeTool ?? true;\n const parts: string[] = [];\n for (const message of messages) {\n const role = getRole(message);\n if (SKIP_ROLES.has(role)) {\n continue;\n }\n if (!includeSystem && SYSTEM_ROLES.has(role)) {\n continue;\n }\n if (!includeTool && TOOL_ROLES.has(role)) {\n continue;\n }\n const text = extractContentText(message.content);\n const stripped = text.trim();\n if (stripped) {\n parts.push(stripped);\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromPrompts(prompts: readonly string[]): string {\n return prompts\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n .join(\"\\n\");\n}\n\nexport function extractTextFromToolInput(inputStr: string): string {\n return inputStr.trim();\n}\n\nexport function extractTextFromLLMResult(response: DucktypedLLMResult): string {\n const parts: string[] = [];\n for (const genList of response.generations ?? []) {\n for (const gen of genList) {\n const text = (gen.text ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n }\n return parts.join(\"\\n\");\n}\n\nexport function extractTextFromDocuments(documents: ReadonlyArray<DucktypedDocument>): string {\n const parts: string[] = [];\n for (const doc of documents) {\n const text = (doc.pageContent ?? \"\").trim();\n if (text) {\n parts.push(text);\n }\n }\n return parts.join(\"\\n\");\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport {\n FIREWALL_HOOK_TO_LABEL,\n FirewallHook,\n HookLabel,\n resolveHooks,\n} from \"../hooks.js\";\nimport type { BlockResult, LangChainAdapterOptions } from \"../types.js\";\nimport {\n extractTextFromDocuments,\n extractTextFromLLMResult,\n extractTextFromPrompts,\n extractTextFromToolInput,\n} from \"../utils/extract.js\";\n\nconst USER_ROLES: ReadonlySet<string> = new Set([\"human\", \"user\"]);\n\ninterface LangChainDucktypedMessage {\n role?: string;\n type?: string;\n content?: string | ReadonlyArray<string | { type?: string; text?: string }>;\n}\n\nfunction getMessageRole(message: LangChainDucktypedMessage): string {\n if (typeof message.role === \"string\") {\n return message.role.toLowerCase();\n }\n if (typeof message.type === \"string\") {\n return message.type.toLowerCase();\n }\n return \"\";\n}\n\nfunction extractMessageText(content: LangChainDucktypedMessage[\"content\"]): string {\n if (content === undefined) {\n return \"\";\n }\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<LangChainDucktypedMessage>,\n): LangChainDucktypedMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (USER_ROLES.has(getMessageRole(msg))) {\n return msg;\n }\n }\n return undefined;\n}\n\nexport async function createLangChainHandler(\n firewall: Firewall,\n options: LangChainAdapterOptions = {},\n): Promise<BaseCallbackHandler> {\n const { BaseCallbackHandler } = await import(\"@langchain/core/callbacks/base\");\n\n const enabledHooks = resolveHooks(options.hooks);\n // includeSystem / includeTool are retained in LangChainAdapterOptions for\n // backwards compat but are inert under the per-message hook-routing algorithm:\n // handleChatModelStart only scans the last user-role message; system and tool\n // messages are either trusted or already classified by their own hooks.\n const failOpen = options.failOpen ?? true;\n const logger =\n options.logger ??\n ((message: string, error: unknown): void => {\n // eslint-disable-next-line no-console\n console.warn(`silmaril.firewall: ${message}`, error);\n });\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n const shadowMode = options.shadowMode ?? firewall.shadowMode;\n const onClassify = options.onClassify;\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const fireOnClassify = (\n hookLabel: HookLabel,\n text: string,\n result: BlockResult,\n blocked: boolean,\n toolName: string | undefined,\n ): void => {\n if (!onClassify) {\n return;\n }\n try {\n onClassify({\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n shadowMode,\n });\n } catch (err) {\n logger(\"onClassify callback threw\", err);\n }\n };\n\n const classify = async (\n text: string,\n hookLabel: HookLabel,\n runId: string,\n toolName?: string,\n ): Promise<void> => {\n let result: BlockResult;\n try {\n result = await firewall.classify(text, {\n hook: hookLabel,\n ...(toolName !== undefined ? { toolName } : {}),\n });\n } catch (err) {\n if (!failOpen) {\n throw err;\n }\n logger(\"classification failed, allowing prompt through\", err);\n return;\n }\n const threshold = effectiveThreshold(hookLabel);\n const blocked = result.score >= threshold;\n fireOnClassify(hookLabel, text, result, blocked, toolName);\n if (blocked && !shadowMode) {\n throw new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n runId,\n });\n }\n };\n\n class SilmarilFirewallHandler extends BaseCallbackHandler {\n override name = \"silmaril_firewall_handler\";\n override raiseError = true;\n override awaitHandlers = true;\n\n override async handleChatModelStart(\n _llm: unknown,\n messages: unknown,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {\n return;\n }\n const batches = messages as ReadonlyArray<ReadonlyArray<LangChainDucktypedMessage>>;\n const flat: LangChainDucktypedMessage[] = [];\n for (const batch of batches) {\n for (const m of batch) {\n flat.push(m);\n }\n }\n const lastUser = findLastUserMessage(flat);\n if (!lastUser) {\n return;\n }\n const text = extractMessageText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);\n }\n\n override async handleLLMStart(\n _llm: unknown,\n prompts: string[],\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_START)) {\n return;\n }\n const text = extractTextFromPrompts(prompts);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);\n }\n\n override async handleToolStart(\n tool: { name?: string } | undefined,\n inputStr: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_START)) {\n return;\n }\n const text = extractTextFromToolInput(inputStr);\n if (!text) {\n return;\n }\n const toolName = tool?.name;\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);\n }\n\n override async handleRetrieverStart(\n _retriever: unknown,\n query: string,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {\n return;\n }\n const text = query.trim();\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);\n }\n\n override async handleLLMEnd(output: unknown, runId: string): Promise<void> {\n if (!enabledHooks.has(FirewallHook.LLM_END)) {\n return;\n }\n const text = extractTextFromLLMResult(output as { generations?: ReadonlyArray<ReadonlyArray<{ text?: string }>> });\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);\n }\n\n override async handleToolEnd(\n output: unknown,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _kwargs?: { name?: string },\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.TOOL_END)) {\n return;\n }\n const text = String(output).trim();\n if (!text) {\n return;\n }\n const toolName = _kwargs?.name;\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);\n }\n\n override async handleRetrieverEnd(\n documents: ReadonlyArray<{ pageContent?: string }>,\n runId: string,\n ): Promise<void> {\n if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {\n return;\n }\n const text = extractTextFromDocuments(documents);\n if (!text) {\n return;\n }\n await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);\n }\n }\n\n return new SilmarilFirewallHandler();\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { Firewall } from \"../firewall.js\";\nimport { PromptBlockedException } from \"../exceptions.js\";\nimport { HookLabel } from \"../hooks.js\";\nimport type { BlockResult, MiddlewareOptions } from \"../types.js\";\n\ninterface VercelContentPart {\n type?: string;\n text?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n}\n\ninterface VercelPromptMessage {\n role: string;\n content: string | ReadonlyArray<VercelContentPart | string>;\n}\n\ninterface VercelToolCall {\n toolCallType?: string;\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n}\n\ninterface VercelGenerateResult {\n text?: string;\n toolCalls?: ReadonlyArray<VercelToolCall>;\n}\n\ninterface WrapGenerateArgs<TResult extends VercelGenerateResult & Record<string, unknown>> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doGenerate: () => PromiseLike<TResult>;\n}\n\ninterface WrapStreamArgs<\n TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>,\n> {\n params: { prompt: ReadonlyArray<VercelPromptMessage> } & Record<string, unknown>;\n doStream: () => PromiseLike<TResult>;\n}\n\ninterface StreamPart {\n type?: string;\n textDelta?: string;\n delta?: string;\n}\n\nfunction stringifyToolValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n\nfunction extractContentText(content: VercelPromptMessage[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n const parts: string[] = [];\n for (const block of content) {\n if (typeof block === \"string\") {\n parts.push(block);\n } else if (block && block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n return parts.join(\" \");\n}\n\nfunction iterateToolResultParts(\n message: VercelPromptMessage,\n): Array<{ text: string; toolName: string | undefined }> {\n if (typeof message.content === \"string\") {\n return [];\n }\n const out: Array<{ text: string; toolName: string | undefined }> = [];\n for (const part of message.content) {\n if (typeof part === \"string\") {\n continue;\n }\n if (part.type === \"tool-result\") {\n const text = stringifyToolValue(part.result);\n if (text.trim()) {\n out.push({ text, toolName: part.toolName });\n }\n }\n }\n return out;\n}\n\nfunction findLastUserMessage(\n messages: ReadonlyArray<VercelPromptMessage>,\n): VercelPromptMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role.toLowerCase() === \"user\") {\n return msg;\n }\n }\n return undefined;\n}\n\nexport interface FirewallMiddleware {\n wrapGenerate: <TResult extends VercelGenerateResult & Record<string, unknown>>(\n args: WrapGenerateArgs<TResult>,\n ) => Promise<TResult>;\n wrapStream: <TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>(\n args: WrapStreamArgs<TResult>,\n ) => Promise<TResult>;\n}\n\nexport function createMiddleware(\n firewall: Firewall,\n options: MiddlewareOptions = {},\n): FirewallMiddleware {\n const scanInput = options.scanInput ?? true;\n const scanOutput = options.scanOutput ?? false;\n const overrideThreshold = options.threshold;\n const overrideHookThresholds = options.hookThresholds ?? {};\n const shadowMode = options.shadowMode ?? firewall.shadowMode;\n\n const effectiveThreshold = (label: HookLabel): number => {\n if (overrideHookThresholds[label] !== undefined) {\n return overrideHookThresholds[label] as number;\n }\n if (firewall.hookThresholds[label] !== undefined) {\n return firewall.hookThresholds[label] as number;\n }\n return overrideThreshold ?? firewall.threshold;\n };\n\n const classifyOrBlock = async (\n text: string,\n hook: HookLabel,\n toolName?: string,\n ): Promise<void> => {\n if (!text.trim()) {\n return;\n }\n const result: BlockResult = await firewall.classify(\n text,\n toolName !== undefined ? { hook, toolName } : { hook },\n );\n const threshold = effectiveThreshold(hook);\n const blocked = result.score >= threshold;\n options.onClassify?.({\n hook,\n ...(toolName !== undefined ? { toolName } : {}),\n text,\n result,\n blocked,\n shadowMode,\n });\n if (blocked && !shadowMode) {\n const err = new PromptBlockedException({\n score: result.score,\n threshold,\n promptText: text,\n });\n options.onBlocked?.(err);\n throw err;\n }\n };\n\n const scanPrompt = async (prompt: ReadonlyArray<VercelPromptMessage>): Promise<void> => {\n if (prompt.length === 0) {\n return;\n }\n const last = prompt[prompt.length - 1]!;\n const role = last.role.toLowerCase();\n\n // If the newest message is a tool result (typical mid-flow in a\n // tool-calling loop), classify each tool-result part individually with\n // tool_response + toolName. No manual wiring from the caller — the\n // toolName is read directly from the Vercel content part.\n if (role === \"tool\") {\n for (const { text, toolName } of iterateToolResultParts(last)) {\n await classifyOrBlock(text, HookLabel.TOOL_RESPONSE, toolName);\n }\n return;\n }\n\n // Otherwise, fall back to scanning the most recent user message.\n // Covers: [user], [system, user], [user, assistant(text), user], etc.\n const lastUser = findLastUserMessage(prompt);\n if (!lastUser) {\n return;\n }\n const text = extractContentText(lastUser.content).trim();\n if (!text) {\n return;\n }\n await classifyOrBlock(text, HookLabel.USER_INPUT);\n };\n\n const scanGenerateResult = async (result: VercelGenerateResult): Promise<void> => {\n if (scanOutput && typeof result.text === \"string\" && result.text.length > 0) {\n await classifyOrBlock(result.text, HookLabel.LLM_OUTPUT);\n }\n if (options.scanToolCalls && Array.isArray(result.toolCalls)) {\n for (const call of result.toolCalls) {\n if (!call || typeof call !== \"object\") {\n continue;\n }\n const args =\n typeof call.args === \"string\" ? call.args : stringifyToolValue(call.args);\n const toolName = typeof call.toolName === \"string\" ? call.toolName : undefined;\n if (args.trim()) {\n await classifyOrBlock(args, HookLabel.TOOL_CALL, toolName);\n }\n }\n }\n };\n\n return {\n async wrapGenerate<TResult extends VercelGenerateResult & Record<string, unknown>>({\n params,\n doGenerate,\n }: WrapGenerateArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const result = await doGenerate();\n await scanGenerateResult(result);\n return result;\n },\n\n async wrapStream<TResult extends { stream: ReadableStream<unknown> } & Record<string, unknown>>({\n params,\n doStream,\n }: WrapStreamArgs<TResult>): Promise<TResult> {\n if (scanInput) {\n await scanPrompt(params.prompt);\n }\n const { stream, ...rest } = await doStream();\n if (!scanOutput) {\n return { stream, ...rest } as unknown as TResult;\n }\n\n let buffered = \"\";\n const transformed = stream.pipeThrough(\n new TransformStream<unknown, unknown>({\n transform(chunk, controller): void {\n const part = chunk as StreamPart;\n if (part && part.type === \"text-delta\") {\n const delta = part.textDelta ?? part.delta ?? \"\";\n buffered += delta;\n }\n controller.enqueue(chunk);\n },\n async flush(controller): Promise<void> {\n if (!buffered.trim()) {\n return;\n }\n try {\n await classifyOrBlock(buffered, HookLabel.LLM_OUTPUT);\n } catch (err) {\n if (err instanceof PromptBlockedException) {\n controller.enqueue({ type: \"error\", error: err });\n return;\n }\n throw err;\n }\n },\n }),\n );\n return { stream: transformed, ...rest } as unknown as TResult;\n },\n };\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nconst CHARS_PER_TOKEN = 4;\n\nexport const MAX_INPUT_TOKENS = 10_240;\nexport const CHUNK_WINDOW = 400;\nexport const CHUNK_OVERLAP = 64;\n\nexport const MAX_INPUT_CHARS = MAX_INPUT_TOKENS * CHARS_PER_TOKEN;\nexport const CHUNK_WINDOW_CHARS = CHUNK_WINDOW * CHARS_PER_TOKEN;\nexport const CHUNK_OVERLAP_CHARS = CHUNK_OVERLAP * CHARS_PER_TOKEN;\nconst CHUNK_STRIDE_CHARS = CHUNK_WINDOW_CHARS - CHUNK_OVERLAP_CHARS;\n\nexport function chunkText(text: string): string[] {\n const n = text.length;\n if (n > MAX_INPUT_CHARS) {\n throw new Error(\n `Input has ~${Math.floor(n / CHARS_PER_TOKEN)} tokens (${n} chars); ` +\n `max is ${MAX_INPUT_TOKENS} tokens (${MAX_INPUT_CHARS} chars)`,\n );\n }\n if (n <= CHUNK_WINDOW_CHARS) {\n return [text];\n }\n const chunks: string[] = [];\n for (let start = 0; start < n; start += CHUNK_STRIDE_CHARS) {\n const end = start + CHUNK_WINDOW_CHARS;\n chunks.push(text.slice(start, end));\n if (end >= n) {\n break;\n }\n }\n return chunks;\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nimport type { BaseCallbackHandler } from \"@langchain/core/callbacks/base\";\n\nimport { createMiddleware, type FirewallMiddleware } from \"./adapters/vercel.js\";\nimport { chunkText } from \"./chunking.js\";\nimport { SilmarilApiError } from \"./exceptions.js\";\nimport type { HookLabel } from \"./hooks.js\";\nimport type {\n BlockResult,\n ClassifyBatchOptions,\n ClassifyOptions,\n ClassificationMetadata,\n FirewallOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n Prediction,\n} from \"./types.js\";\n\nexport const DEFAULT_THRESHOLD = 0.5;\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RETRIES = 5;\nconst MAX_BACKOFF_SECONDS = 30;\n\ninterface SingleClassifyPayload {\n text: string;\n hook?: string;\n tool_name?: string;\n metadata?: ClassificationMetadata;\n}\n\ninterface BatchClassifyPayload {\n texts: readonly string[];\n hooks?: readonly string[];\n tool_names?: readonly (string | null)[];\n metadata?: readonly (ClassificationMetadata | null)[];\n}\n\ninterface SingleClassifyResponse {\n prediction: Prediction;\n score: number;\n primary_outcome?: string;\n outcome_scores?: Record<string, number>;\n detector_scores?: Record<string, number>;\n detector_counts?: Record<string, number>;\n}\n\ninterface BatchClassifyResponse {\n predictions: readonly SingleClassifyResponse[];\n}\n\nfunction blockResultFromResponse(data: SingleClassifyResponse): BlockResult {\n const result: {\n prediction: Prediction;\n score: number;\n primaryOutcome?: string;\n outcomeScores?: Readonly<Record<string, number>>;\n detectorScores?: Readonly<Record<string, number>>;\n detectorCounts?: Readonly<Record<string, number>>;\n } = {\n prediction: data.prediction,\n score: Number(data.score),\n };\n if (data.primary_outcome !== undefined) {\n result.primaryOutcome = data.primary_outcome;\n }\n if (data.outcome_scores !== undefined) {\n result.outcomeScores = Object.freeze(\n Object.fromEntries(Object.entries(data.outcome_scores).map(([k, v]) => [k, Number(v)])),\n );\n }\n if (data.detector_scores !== undefined) {\n result.detectorScores = Object.freeze(\n Object.fromEntries(Object.entries(data.detector_scores).map(([k, v]) => [k, Number(v)])),\n );\n }\n if (data.detector_counts !== undefined) {\n result.detectorCounts = Object.freeze(\n Object.fromEntries(Object.entries(data.detector_counts).map(([k, v]) => [k, Number(v)])),\n );\n }\n return Object.freeze(result);\n}\n\nexport class Firewall {\n readonly apiKey: string;\n readonly apiUrl: string;\n readonly threshold: number;\n readonly timeoutMs: number;\n readonly hookThresholds: Readonly<Partial<Record<HookLabel, number>>>;\n readonly shadowMode: boolean;\n\n private readonly headers: Readonly<Record<string, string>>;\n\n constructor(options: FirewallOptions) {\n if (!options.apiKey) {\n throw new Error(\"Firewall: apiKey is required\");\n }\n if (!options.apiUrl) {\n throw new Error(\"Firewall: apiUrl is required\");\n }\n this.apiKey = options.apiKey;\n this.apiUrl = options.apiUrl;\n this.threshold = options.threshold ?? DEFAULT_THRESHOLD;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.hookThresholds = Object.freeze({ ...(options.hookThresholds ?? {}) });\n this.shadowMode = options.shadowMode ?? false;\n this.headers = Object.freeze({\n \"x-api-key\": this.apiKey,\n \"content-type\": \"application/json\",\n });\n }\n\n async classify(text: string, options: ClassifyOptions = {}): Promise<BlockResult> {\n const chunks = chunkText(text);\n if (chunks.length === 1) {\n const payload: SingleClassifyPayload = { text: chunks[0]! };\n if (options.hook !== undefined) {\n payload.hook = options.hook;\n }\n if (options.toolName !== undefined) {\n payload.tool_name = options.toolName;\n }\n if (options.metadata !== undefined) {\n payload.metadata = options.metadata;\n }\n const data = await this.postWithRetry<SingleClassifyResponse>(payload);\n return blockResultFromResponse(data);\n }\n const batchOpts: ClassifyBatchOptions = {};\n if (options.hook !== undefined) {\n batchOpts.hooks = chunks.map(() => options.hook!);\n }\n if (options.toolName !== undefined) {\n batchOpts.toolNames = chunks.map(() => options.toolName);\n }\n if (options.metadata !== undefined) {\n batchOpts.metadata = chunks.map(() => options.metadata);\n }\n const results = await this.classifyBatch(chunks, batchOpts);\n return results.reduce((best, r) => (r.score > best.score ? r : best));\n }\n\n async classifyBatch(\n texts: readonly string[],\n options: ClassifyBatchOptions = {},\n ): Promise<BlockResult[]> {\n if (options.hooks !== undefined && options.hooks.length !== texts.length) {\n throw new Error(\n `Firewall: hooks length ${options.hooks.length} does not match texts length ${texts.length}`,\n );\n }\n if (options.toolNames !== undefined && options.toolNames.length !== texts.length) {\n throw new Error(\n `Firewall: toolNames length ${options.toolNames.length} does not match texts length ${texts.length}`,\n );\n }\n if (options.metadata !== undefined && options.metadata.length !== texts.length) {\n throw new Error(\n `Firewall: metadata length ${options.metadata.length} does not match texts length ${texts.length}`,\n );\n }\n\n const payload: BatchClassifyPayload = { texts };\n if (options.hooks && options.hooks.length > 0) {\n payload.hooks = options.hooks.map((h) => String(h));\n }\n if (options.toolNames && options.toolNames.length > 0) {\n payload.tool_names = options.toolNames.map((t) => (t === undefined ? null : t));\n }\n if (options.metadata && options.metadata.length > 0) {\n payload.metadata = options.metadata.map((m) => (m === undefined ? null : m));\n }\n const data = await this.postWithRetry<BatchClassifyResponse>(payload);\n return data.predictions.map((p) => blockResultFromResponse(p));\n }\n\n asLangChainHandler(options: LangChainAdapterOptions = {}): Promise<BaseCallbackHandler> {\n return import(\"./adapters/langchain.js\").then((m) => m.createLangChainHandler(this, options));\n }\n\n asMiddleware(options: MiddlewareOptions = {}): FirewallMiddleware {\n return createMiddleware(this, options);\n }\n\n effectiveThreshold(hook: HookLabel | undefined): number {\n if (hook === undefined) {\n return this.threshold;\n }\n return this.hookThresholds[hook] ?? this.threshold;\n }\n\n private async postWithRetry<T>(\n payload: SingleClassifyPayload | BatchClassifyPayload,\n maxRetries: number = DEFAULT_MAX_RETRIES,\n ): Promise<T> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const response = await fetch(this.apiUrl, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (response.status !== 429 || attempt === maxRetries) {\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new SilmarilApiError({\n status: response.status,\n statusText: response.statusText,\n body,\n });\n }\n return (await response.json()) as T;\n }\n const waitSeconds = Math.min(2 ** attempt, MAX_BACKOFF_SECONDS);\n await new Promise<void>((resolve) => setTimeout(resolve, waitSeconds * 1000));\n }\n throw new Error(\"Firewall: exhausted retries (unreachable)\");\n }\n}\n","// Copyright (c) 2024-2025 Silmaril Security Inc. All rights reserved.\n// PROPRIETARY AND CONFIDENTIAL\n\nexport {\n Firewall,\n DEFAULT_THRESHOLD,\n DEFAULT_TIMEOUT_MS,\n} from \"./firewall.js\";\n\nexport { PromptBlockedException, SilmarilApiError } from \"./exceptions.js\";\n\nexport type { FirewallMiddleware } from \"./adapters/vercel.js\";\n\nexport {\n chunkText,\n MAX_INPUT_TOKENS,\n CHUNK_WINDOW,\n CHUNK_OVERLAP,\n MAX_INPUT_CHARS,\n CHUNK_WINDOW_CHARS,\n CHUNK_OVERLAP_CHARS,\n} from \"./chunking.js\";\n\nexport {\n HookLabel,\n FirewallHook,\n DEFAULT_HOOKS,\n INPUT_HOOKS,\n OUTPUT_HOOKS,\n ALL_HOOKS,\n DEFAULT_HOOK_THRESHOLDS,\n FIREWALL_HOOK_TO_LABEL,\n resolveHooks,\n prependHook,\n prependToolName,\n} from \"./hooks.js\";\n\nexport type {\n BlockResult,\n ClassifyEvent,\n Prediction,\n ClassificationMetadata,\n FirewallOptions,\n ClassifyOptions,\n ClassifyBatchOptions,\n LangChainAdapterOptions,\n MiddlewareOptions,\n} from \"./types.js\";\n"],"mappings":"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","names":["findLastUserMessage","text"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silmaril-security/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "TypeScript SDK for Silmaril Firewall — prompt injection and jailbreak detection",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Silmaril Security Inc.",
|
|
@@ -42,14 +42,16 @@
|
|
|
42
42
|
"LICENSE"
|
|
43
43
|
],
|
|
44
44
|
"publishConfig": {
|
|
45
|
-
"access": "
|
|
45
|
+
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=18"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "tsup",
|
|
52
|
-
"
|
|
52
|
+
"prepare": "tsup",
|
|
53
|
+
"typecheck": "tsc --noEmit && npm run typecheck:notebooks",
|
|
54
|
+
"typecheck:notebooks": "node scripts/typecheck-notebooks.mjs",
|
|
53
55
|
"lint": "eslint src test",
|
|
54
56
|
"test": "vitest run",
|
|
55
57
|
"test:watch": "vitest"
|
|
@@ -67,7 +69,8 @@
|
|
|
67
69
|
}
|
|
68
70
|
},
|
|
69
71
|
"devDependencies": {
|
|
70
|
-
"@
|
|
72
|
+
"@ai-sdk/provider": "^2.0.0",
|
|
73
|
+
"@langchain/core": "^1.1.39",
|
|
71
74
|
"@types/node": "^20.12.0",
|
|
72
75
|
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
|
73
76
|
"@typescript-eslint/parser": "^7.8.0",
|
|
@@ -76,6 +79,6 @@
|
|
|
76
79
|
"tsup": "^8.0.0",
|
|
77
80
|
"typescript": "^5.4.0",
|
|
78
81
|
"undici": "^6.15.0",
|
|
79
|
-
"vitest": "^1.
|
|
82
|
+
"vitest": "^4.1.4"
|
|
80
83
|
}
|
|
81
84
|
}
|