ai-sdk-guardrails
Version:
Input and output guardrails middleware for Vercel AI SDK.
5,146 lines • 169 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/advanced/index.ts
var advanced_exports = {};
__export(advanced_exports, {
DEFAULT_DETECT_NORMALIZATION: () => DEFAULT_DETECT_NORMALIZATION,
GuardrailViolationAbort: () => GuardrailViolationAbort,
PII_PATTERNS: () => PII_PATTERNS,
ToolParameterValidationError: () => ToolParameterValidationError,
after: () => after,
backoffPresets: () => presets,
clearViolationHistory: () => clearViolationHistory,
compositeBackoff: () => compositeBackoff,
createAdaptivePrepareStep: () => createAdaptivePrepareStep,
createConsoleDebugger: () => createConsoleDebugger,
createContentFilterTransform: () => createContentFilterTransform,
createDebugWrapper: () => createDebugWrapper,
createDefaultBuildRetryParams: () => createDefaultBuildRetryParams,
createFinishReasonEnhancement: () => createFinishReasonEnhancement,
createGuardrailAbortController: () => createGuardrailAbortController,
createGuardrailPrepareStep: () => createGuardrailPrepareStep,
createGuardrailProviderMetadata: () => createGuardrailProviderMetadata,
createGuardrailStreamTransform: () => createGuardrailStreamTransform,
createGuardrailStreamTransformBuffered: () => createGuardrailStreamTransformBuffered,
createGuardrailTransform: () => createGuardrailTransform,
createHealthCheck: () => createHealthCheck,
createMetricsCollector: () => createMetricsCollector,
createPIIRedactionTransform: () => createPIIRedactionTransform,
createPipeline: () => createPipeline,
createTokenAwareGuardrailTransform: () => createTokenAwareGuardrailTransform,
createTokenBudgetTransform: () => createTokenBudgetTransform,
createToolAbortionController: () => createToolAbortionController,
detectSystemPromptLeak: () => detectSystemPromptLeak,
enhancedPromptInjectionDetector: () => enhancedPromptInjectionDetector,
envDebugMode: () => envDebugMode,
estimateTokenCount: () => estimateTokenCount,
exponentialBackoff: () => exponentialBackoff,
fixedBackoff: () => fixedBackoff,
formatTraceAsJSON: () => formatTraceAsJSON,
formatTraceForConsole: () => formatTraceForConsole,
formatTraceSummary: () => formatTraceSummary,
getGuardrailFinishReason: () => getGuardrailFinishReason,
getViolationStats: () => getViolationStats,
guardrailMiddleware: () => guardrailMiddleware,
incrementalPromptInjectionDetector: () => incrementalPromptInjectionDetector,
inputGuardrailsMiddleware: () => inputGuardrailsMiddleware,
inputPipeline: () => inputPipeline,
intentBasedInjectionDetector: () => intentBasedInjectionDetector,
jitteredExponentialBackoff: () => jitteredExponentialBackoff,
lenientEscalation: () => lenientEscalation,
linearBackoff: () => linearBackoff,
logExecutionSummary: () => logExecutionSummary,
noBackoff: () => noBackoff,
noopGuardrailMiddleware: () => noopGuardrailMiddleware,
normalizeForDetection: () => normalizeForDetection,
not: () => not,
outputGuardrailsMiddleware: () => outputGuardrailsMiddleware,
outputPipeline: () => outputPipeline,
parallel: () => parallel,
resolveDetectNormalization: () => resolveDetectNormalization,
resolveRetryConfig: () => resolveRetryConfig,
strictEscalation: () => strictEscalation,
toolCallInjectionDetector: () => toolCallInjectionDetector,
warnOnly: () => warnOnly,
when: () => when,
withFallback: () => withFallback,
withGracePeriod: () => withGracePeriod,
withGradualEnforcement: () => withGradualEnforcement,
withRetry: () => withRetry,
withToolParameterGuardrails: () => withToolParameterGuardrails,
wrapToolWithAbortion: () => wrapToolWithAbortion
});
module.exports = __toCommonJS(advanced_exports);
// src/guardrails.ts
var import_ai = require("ai");
// src/errors.ts
var import_provider = require("@ai-sdk/provider");
var marker = "ai-sdk-guardrails.error";
var symbol = Symbol.for(marker);
var GuardrailsError = class extends import_provider.AISDKError {
[symbol] = true;
timestamp;
metadata;
constructor(name, message, metadata = {}, cause) {
super({ name, message, cause });
this.timestamp = /* @__PURE__ */ new Date();
this.metadata = metadata;
}
/**
* Convert the error to a serializable object for logging/reporting
*/
toJSON() {
return {
name: this.name,
code: this.code,
message: this.message,
timestamp: this.timestamp.toISOString(),
metadata: this.metadata,
stack: this.stack
};
}
/**
* Check if this error is of a specific guardrails error subclass.
*/
is(errorClass) {
return this instanceof errorClass;
}
/**
* Checks whether the given value is a guardrails error, across package
* versions (marker-based, like `AISDKError.isInstance`).
*/
static isInstance(error) {
return super.hasMarker(error, marker);
}
};
var GuardrailTimeoutError = class extends GuardrailsError {
code = "GUARDRAIL_TIMEOUT";
guardrailName;
timeoutMs;
constructor(guardrailName, timeoutMs, metadata = {}) {
const message = `Guardrail "${guardrailName}" timed out after ${timeoutMs}ms`;
super("GuardrailTimeoutError", message, {
...metadata,
guardrailName,
timeoutMs
});
this.guardrailName = guardrailName;
this.timeoutMs = timeoutMs;
}
};
var GuardrailsInputError = class extends GuardrailsError {
code = "INPUT_BLOCKED";
blockedGuardrails;
constructor(blockedGuardrails, metadata = {}) {
const guardrailNames = blockedGuardrails.map((g) => g.name).join(", ");
const message = `Input blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`;
super("GuardrailsInputError", message, { ...metadata, blockedGuardrails });
this.blockedGuardrails = blockedGuardrails;
}
};
var GuardrailsOutputError = class extends GuardrailsError {
code = "OUTPUT_BLOCKED";
blockedGuardrails;
constructor(blockedGuardrails, metadata = {}) {
const guardrailNames = blockedGuardrails.map((g) => g.name).join(", ");
const message = `Output blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`;
super("GuardrailsOutputError", message, { ...metadata, blockedGuardrails });
this.blockedGuardrails = blockedGuardrails;
}
};
// src/spec.ts
var import_zod = require("zod");
var GuardrailSpec = class {
constructor(name, description, mediaType, configSchema, checkFn, ctxRequirements, metadata) {
this.name = name;
this.description = description;
this.mediaType = mediaType;
this.configSchema = configSchema;
this.checkFn = checkFn;
this.ctxRequirements = ctxRequirements;
this.metadata = metadata;
}
name;
description;
mediaType;
configSchema;
checkFn;
ctxRequirements;
metadata;
/**
* Return a JSON schema-like representation for tooling/SDKs.
*/
schema() {
return this.configSchema._def;
}
/**
* Instantiate the guardrail with validated configuration.
*/
instantiate(config) {
const validated = this.configSchema instanceof import_zod.z.ZodType ? this.configSchema.parse(config ?? {}) : config;
return new ConfiguredGuardrail(this, validated);
}
};
var ConfiguredGuardrail = class {
constructor(spec, config) {
this.spec = spec;
this.config = config;
}
spec;
config;
async ensureAsync(fn, ...args) {
const result = fn(...args);
if (result instanceof Promise) {
return await result;
}
return result;
}
async run(context, input) {
const startedAt = Date.now();
try {
const validatedContext = this.spec.ctxRequirements ? this.spec.ctxRequirements.parse(context) : context;
const result = await this.ensureAsync(
this.spec.checkFn,
validatedContext,
input,
this.config
);
const info = result.info || {};
if (!result.info) {
info.guardrailName = this.spec.name;
info.mediaType = this.spec.mediaType;
}
return {
...result,
info: {
...info,
guardrailName: this.spec.name,
mediaType: this.spec.mediaType
},
context: {
guardrailName: this.spec.name,
guardrailVersion: this.spec.metadata?.version,
executedAt: /* @__PURE__ */ new Date(),
executionTimeMs: Date.now() - startedAt,
...result.context
}
};
} catch (error) {
return {
tripwireTriggered: false,
executionFailed: true,
originalException: error instanceof Error ? error : new Error(String(error)),
info: {
guardrailName: this.spec.name,
mediaType: this.spec.mediaType,
error: error instanceof Error ? error.message : String(error)
},
message: error instanceof Error ? error.message : "Guardrail execution failed",
severity: "high",
context: {
guardrailName: this.spec.name,
executedAt: /* @__PURE__ */ new Date(),
executionTimeMs: Date.now() - startedAt
}
};
}
}
};
// src/registry.ts
var import_zod2 = require("zod");
var NO_CONFIG = import_zod2.z.object({});
var NO_CONTEXT = import_zod2.z.object({});
var GuardrailRegistry = class {
specs = /* @__PURE__ */ new Map();
registerSpec(spec) {
this.specs.set(spec.name, spec);
}
register(name, checkFn, description, mediaType = "text/plain", configSchema, ctxRequirements, metadata) {
const spec = new GuardrailSpec(
name,
description,
mediaType,
configSchema || NO_CONFIG,
checkFn,
ctxRequirements || NO_CONTEXT,
metadata
);
this.registerSpec(
spec
);
}
get(name) {
return this.specs.get(name);
}
has(name) {
return this.specs.has(name);
}
remove(name) {
return this.specs.delete(name);
}
size() {
return this.specs.size;
}
all() {
return [...this.specs.values()];
}
list() {
return this.all();
}
metadata() {
return this.all().map((spec) => ({
name: spec.name,
description: spec.description,
mediaType: spec.mediaType,
hasConfig: spec.configSchema !== NO_CONFIG,
hasContext: spec.ctxRequirements !== NO_CONTEXT,
metadata: spec.metadata
}));
}
};
var defaultRegistry = new GuardrailRegistry();
// src/enhanced-runtime.ts
async function runGuardrails(input, bundle, context = {}, options = {}) {
const {
raiseGuardrailErrors = false,
parallelExecution = true,
timeoutMs,
globalTimeoutMs,
signal
} = options;
const startTime = Date.now();
const guardrails = await instantiateGuardrails(bundle);
if (signal?.aborted) {
throw new Error("Guardrails execution aborted");
}
let results;
if (parallelExecution) {
const promises = guardrails.map(
(guardrail) => runSingleGuardrail(guardrail, context, input, timeoutMs, signal)
);
results = globalTimeoutMs ? await Promise.race([
Promise.all(promises),
rejectAfter(
globalTimeoutMs,
`Global timeout of ${globalTimeoutMs}ms exceeded`
)
]) : await Promise.all(promises);
} else {
results = [];
for (const guardrail of guardrails) {
if (globalTimeoutMs) {
const elapsed = Date.now() - startTime;
const remaining = globalTimeoutMs - elapsed;
if (remaining <= 0) {
throw new Error(`Global timeout of ${globalTimeoutMs}ms exceeded`);
}
}
if (signal?.aborted) {
throw new Error("Guardrails execution aborted");
}
const result = await runSingleGuardrail(
guardrail,
context,
input,
timeoutMs,
signal
);
results.push(result);
if (result.tripwireTriggered && result.severity === "critical") {
break;
}
}
}
if (raiseGuardrailErrors) {
const executionFailures = results.filter((r) => r.executionFailed);
if (executionFailures.length > 0) {
const firstFailure = executionFailures[0];
if (firstFailure.originalException) {
throw firstFailure.originalException;
}
throw new Error(firstFailure.message || "Guardrail execution failed");
}
}
const totalExecutionTimeMs = Date.now() - startTime;
const triggeredCount = results.filter((r) => r.tripwireTriggered).length;
const failedCount = results.filter((r) => r.executionFailed).length;
const successCount = results.length - failedCount;
return {
blocked: triggeredCount > 0,
results,
metadata: {
totalExecutionTimeMs,
triggeredCount,
failedCount,
successCount
}
};
}
async function runSingleGuardrail(guardrail, context, input, timeoutMs, signal) {
try {
if (signal?.aborted) {
throw new Error("Execution aborted");
}
if (timeoutMs) {
return await Promise.race([
guardrail.run(context, input),
rejectAfter(timeoutMs, `Guardrail timeout after ${timeoutMs}ms`)
]);
}
return await guardrail.run(context, input);
} catch (error) {
return {
tripwireTriggered: false,
executionFailed: true,
originalException: error instanceof Error ? error : new Error(String(error)),
info: {
guardrailName: guardrail.spec.name,
mediaType: guardrail.spec.mediaType,
error: error instanceof Error ? error.message : String(error)
},
message: `Guardrail execution failed: ${error instanceof Error ? error.message : String(error)}`,
severity: "high",
context: {
guardrailName: guardrail.spec.name,
executedAt: /* @__PURE__ */ new Date()
}
};
}
}
async function instantiateGuardrails(bundle) {
const guardrails = [];
for (const config of bundle.guardrails) {
const spec = defaultRegistry.get(config.name);
if (!spec) {
throw new Error(`Guardrail '${config.name}' not found in registry`);
}
try {
const guardrail = spec.instantiate(
config.config || {}
);
guardrails.push(guardrail);
} catch (error) {
throw new Error(
`Failed to instantiate guardrail '${config.name}': ${error instanceof Error ? error.message : String(error)}`,
{ cause: error }
);
}
}
return guardrails;
}
function rejectAfter(ms, message) {
return new Promise(
(_resolve, reject) => setTimeout(() => reject(new Error(message)), ms)
);
}
// src/adapters/spec-adapter.ts
var import_zod3 = require("zod");
function guardrailToSpec(guardrail, options) {
return new GuardrailSpec(
guardrail.name,
guardrail.description || `Guardrail: ${guardrail.name}`,
"text/plain",
import_zod3.z.unknown(),
// Legacy guardrails don't expose config schemas
async (context, input) => {
const result = await guardrail.execute(input);
return result;
},
void 0,
{
engine: "custom",
version: guardrail.version,
tags: guardrail.tags,
category: "security",
...options?.metadata
}
);
}
function outputGuardrailToSpec(guardrail, options) {
return new GuardrailSpec(
guardrail.name,
guardrail.description || `Output guardrail: ${guardrail.name}`,
"text/plain",
import_zod3.z.unknown(),
// Placeholder schema for legacy guardrails
async (context, input) => {
const accumulatedText = context._accumulatedText;
const result = await guardrail.execute(
input,
accumulatedText
);
return result;
},
void 0,
{
engine: "custom",
version: guardrail.version,
tags: guardrail.tags,
category: "security",
...options?.metadata
}
);
}
// src/adapters/parallel-runtime-adapter.ts
async function executeInputGuardrailsWithEnhancedRuntime(guardrails, context, options = {}) {
const specs = guardrails.filter((g) => g.enabled !== false).map((g) => guardrailToSpec(g));
const bundle = {
version: 1,
stageName: "input",
guardrails: specs.map((spec) => ({
name: spec.name,
config: {}
}))
};
const tempRegistry = /* @__PURE__ */ new Map();
for (const spec of specs) tempRegistry.set(spec.name, spec);
const originalGet = defaultRegistry.get.bind(defaultRegistry);
defaultRegistry.get = (name) => tempRegistry.get(name) || originalGet(name);
try {
const result = await runGuardrails(
context,
bundle,
{},
{
parallelExecution: options.parallel ?? true,
timeoutMs: options.timeout,
raiseGuardrailErrors: !options.continueOnFailure,
signal: options.signal
}
);
defaultRegistry.get = originalGet;
return result.results.map((r, index) => {
const guardrail = guardrails[index];
const guardrailName = guardrail?.name || "unknown";
return {
tripwireTriggered: r.tripwireTriggered,
message: r.message,
severity: r.severity,
suggestion: r.suggestion,
metadata: r.metadata || {},
context: r.context?.guardrailName ? {
guardrailName: r.context.guardrailName,
guardrailVersion: r.context.guardrailVersion,
executedAt: r.context.executedAt || /* @__PURE__ */ new Date(),
executionTimeMs: r.context.executionTimeMs,
environment: r.context.environment
} : {
guardrailName,
executedAt: /* @__PURE__ */ new Date(),
executionTimeMs: r.context?.executionTimeMs,
environment: r.context?.environment
}
};
});
} catch (error) {
defaultRegistry.get = originalGet;
throw error;
}
}
async function executeOutputGuardrailsWithEnhancedRuntime(guardrails, context, options = {}) {
const specs = guardrails.filter((g) => g.enabled !== false).map(
(g) => outputGuardrailToSpec(g)
);
const bundle = {
version: 1,
stageName: "output",
guardrails: specs.map((spec) => ({
name: spec.name,
config: {}
}))
};
const tempRegistry = /* @__PURE__ */ new Map();
for (const spec of specs) tempRegistry.set(spec.name, spec);
const originalGet = defaultRegistry.get.bind(defaultRegistry);
defaultRegistry.get = (name) => tempRegistry.get(name) || originalGet(name);
try {
const runtimeContext = options.accumulatedText ? { _accumulatedText: options.accumulatedText } : {};
const result = await runGuardrails(context, bundle, runtimeContext, {
parallelExecution: options.parallel ?? true,
timeoutMs: options.timeout,
raiseGuardrailErrors: !options.continueOnFailure,
signal: options.signal
});
defaultRegistry.get = originalGet;
return result.results.map((r, index) => {
const guardrail = guardrails[index];
const guardrailName = guardrail?.name || "unknown";
return {
tripwireTriggered: r.tripwireTriggered,
message: r.message,
severity: r.severity,
suggestion: r.suggestion,
metadata: r.metadata || {},
context: r.context?.guardrailName ? {
guardrailName: r.context.guardrailName,
guardrailVersion: r.context.guardrailVersion,
executedAt: r.context.executedAt || /* @__PURE__ */ new Date(),
executionTimeMs: r.context.executionTimeMs,
environment: r.context.environment
} : {
guardrailName,
executedAt: /* @__PURE__ */ new Date(),
executionTimeMs: r.context?.executionTimeMs,
environment: r.context?.environment
}
};
});
} catch (error) {
defaultRegistry.get = originalGet;
throw error;
}
}
var V1_ENHANCED_RUNTIME_ENABLED = process.env.GUARDRAILS_ENHANCED_RUNTIME !== "false" && process.env.NODE_ENV !== "test";
// src/guardrails/internal.ts
var ENABLE_PERFORMANCE_TRACKING = process.env.NODE_ENV === "development" || process.env.GUARDRAILS_PERFORMANCE_TRACKING === "true";
var USE_ENHANCED_RUNTIME = process.env.GUARDRAILS_USE_ENHANCED_RUNTIME !== "false" && process.env.NODE_ENV !== "test";
function extractTextFromContent(content) {
return content.filter((part) => part.type === "text").map((part) => part.text || "").join("");
}
var normalizedContextCache = /* @__PURE__ */ new WeakMap();
var TimeoutControllerPool = class {
static controllers = [];
static MAX_POOL_SIZE = 20;
static acquire() {
const controller = this.controllers.pop();
if (controller && !controller.signal.aborted) {
return controller;
}
return new AbortController();
}
static release(controller) {
if (!controller.signal.aborted && this.controllers.length < this.MAX_POOL_SIZE) {
this.controllers.push(controller);
}
}
static clear() {
this.controllers = [];
}
};
function guardrailErrorResult(guardrailName, error, extra) {
const errMsg = error instanceof Error ? error.message : String(error);
const result = {
tripwireTriggered: true,
message: extra?.message ?? `Guardrail execution failed: ${errMsg}`,
severity: "critical",
metadata: extra?.metadata ?? { error: errMsg },
info: {
guardrailName,
executionFailed: true,
error: extra?.infoError ?? errMsg
}
};
if (extra?.context) {
result.context = extra.context;
}
return result;
}
var PRIORITY_ORDER = { critical: 4, high: 3, medium: 2, low: 1 };
function prepareGuardrails(guardrails) {
return guardrails.filter((g) => g.enabled !== false).toSorted(
(a, b) => (PRIORITY_ORDER[b.priority || "medium"] || 2) - (PRIORITY_ORDER[a.priority || "medium"] || 2)
);
}
function createConditionalContext(guardrailName, guardrailVersion, executionTimeMs, existingContext) {
if (!ENABLE_PERFORMANCE_TRACKING) {
return {
guardrailName,
...existingContext
};
}
return {
guardrailName,
guardrailVersion,
executedAt: /* @__PURE__ */ new Date(),
executionTimeMs,
...existingContext
};
}
function checkStreamStopCondition(stopConfig, violations) {
if (stopConfig === true) {
const criticalViolations = violations.filter(
(v) => v.summary.blockedResults.some(
(r) => (r.severity ?? "medium") === "critical"
)
);
return violations.length >= 2 || criticalViolations.length > 0;
}
if (typeof stopConfig === "number") {
return violations.length >= stopConfig;
}
if (typeof stopConfig === "function") {
return stopConfig(violations);
}
return false;
}
async function executeWithOptimizedTimeout(execution, timeoutMs, errorMessage) {
const controller = TimeoutControllerPool.acquire();
let timeoutId;
try {
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
controller.abort();
reject(new Error(errorMessage));
}, timeoutMs);
});
const result = await Promise.race([
execution(controller.signal),
timeoutPromise
]);
return result;
} finally {
if (timeoutId) {
clearTimeout(timeoutId);
}
TimeoutControllerPool.release(controller);
}
}
function makeInvokeWithTimeout(invoke, timeoutMs) {
return (guardrail) => executeWithOptimizedTimeout(
(signal) => invoke(guardrail, signal),
timeoutMs,
`Guardrail ${guardrail.name} timed out after ${timeoutMs}ms`
).catch((error) => {
if (error.message.includes("timed out")) {
throw new GuardrailTimeoutError(guardrail.name, timeoutMs);
}
throw error;
});
}
async function executeSequential(guardrails, invokeWithTimeout, opts) {
const { continueOnFailure, logLevel, logger, label } = opts;
const results = [];
for (const guardrail of guardrails) {
try {
const result = await invokeWithTimeout(guardrail);
results.push(result);
if (result.tripwireTriggered) {
if (logLevel !== "none") {
logger.warn(
`${label} guardrail "${guardrail.name}" triggered: ${result.message}`
);
}
if (!continueOnFailure) {
break;
}
}
} catch (error) {
if (logLevel !== "none") {
logger.error(
`Error executing ${label.toLowerCase()} guardrail "${guardrail.name}":`,
error
);
}
results.push(guardrailErrorResult(guardrail.name, error));
if (!continueOnFailure) {
break;
}
}
}
return results;
}
async function executeParallelPerGuardrail(guardrails, invokeWithTimeout, label, logLevel, logger) {
return Promise.all(
guardrails.map(async (guardrail) => {
try {
const result = await invokeWithTimeout(guardrail);
if (result.tripwireTriggered && logLevel !== "none") {
logger.warn(
`${label} guardrail "${guardrail.name}" triggered: ${result.message}`
);
}
return result;
} catch (error) {
if (logLevel !== "none") {
logger.error(
`Error executing ${label.toLowerCase()} guardrail "${guardrail.name}":`,
error
);
}
return guardrailErrorResult(guardrail.name, error);
}
})
);
}
async function executeBatchInputGuardrails(guardrails, normalizedContext, timeoutMs, logLevel, logger) {
if (guardrails.length === 0) return [];
return executeWithOptimizedTimeout(
async (signal) => {
const results = await Promise.allSettled(
guardrails.map(async (guardrail) => {
try {
return await guardrail.execute(normalizedContext, { signal });
} catch (error) {
if (logLevel !== "none") {
logger.error(
`Error executing input guardrail "${guardrail.name}":`,
error
);
}
return guardrailErrorResult(guardrail.name, error);
}
})
);
return results.map((result, index) => {
const guardrail = guardrails[index];
if (result.status === "fulfilled") {
const guardResult = result.value;
if (guardResult.tripwireTriggered && logLevel !== "none") {
logger.warn(
`Input guardrail "${guardrail.name}" triggered: ${guardResult.message}`
);
}
return guardResult;
}
if (logLevel !== "none") {
logger.error(
`Input guardrail "${guardrail.name}" failed:`,
result.reason
);
}
return guardrailErrorResult(guardrail.name, result.reason);
});
},
timeoutMs,
`Batch guardrail execution timed out after ${timeoutMs}ms`
).catch((error) => {
if (error.message.includes("timed out")) {
return guardrails.map(
(guardrail) => guardrailErrorResult(guardrail.name, error, {
message: `Guardrail timed out after ${timeoutMs}ms`,
metadata: { timeout: true },
infoError: "Timeout"
})
);
}
throw error;
});
}
function normalizeGuardrailContext(params) {
const cached = normalizedContextCache.get(params);
if (cached) {
return cached;
}
const promptMessages = Array.isArray(params.prompt) ? params.prompt : [];
const systemMessage = promptMessages.find((msg) => msg.role === "system");
const system = systemMessage && Array.isArray(systemMessage.content) ? extractTextFromContent(systemMessage.content) : "";
const messages = promptMessages.filter((msg) => msg.role !== "system").map((msg) => ({
role: msg.role,
content: msg.content && Array.isArray(msg.content) ? extractTextFromContent(msg.content) : ""
}));
const prompt = messages.length === 1 && messages[0]?.role === "user" ? messages[0].content : messages.map((m) => m.content).join(" ");
const normalized = {
prompt,
messages,
system,
maxOutputTokens: params.maxOutputTokens,
temperature: params.temperature,
modelParams: {
topP: params.topP,
topK: params.topK,
frequencyPenalty: params.frequencyPenalty,
presencePenalty: params.presencePenalty,
seed: params.seed,
stopSequences: params.stopSequences
}
};
normalizedContextCache.set(params, normalized);
return normalized;
}
function toNormalizedGuardrailContext(params) {
const candidate = params;
if (typeof candidate.prompt === "string" && Array.isArray(candidate.messages)) {
return params;
}
return normalizeGuardrailContext(params);
}
function createExecutionSummary(results, startTime) {
const endTime = Date.now();
const totalExecutionTime = endTime - startTime;
const blockedResults = results.filter((r) => r.tripwireTriggered);
const execTimes = results.map((r) => r.context?.executionTimeMs).filter((t) => typeof t === "number");
const avgTime = execTimes.length > 0 ? execTimes.reduce((a, b) => a + b, 0) / execTimes.length : 0;
const stats = {
passed: results.filter((r) => !r.tripwireTriggered).length,
blocked: blockedResults.length,
failed: results.filter(
(r) => r.severity === "critical" && r.tripwireTriggered
).length,
averageExecutionTime: avgTime
};
return {
allResults: results,
blockedResults,
totalExecutionTime,
guardrailsExecuted: results.length,
stats
};
}
async function executeInputGuardrails(guardrails, params, options = {}) {
const {
parallel: parallel2 = true,
timeout = 3e4,
// 30 seconds
continueOnFailure = true,
logLevel = "warn",
logger = console
} = options;
const enabledGuardrails = prepareGuardrails(guardrails);
const invokeWithTimeout = makeInvokeWithTimeout(
(guardrail, signal) => Promise.resolve(
guardrail.execute(toNormalizedGuardrailContext(params), { signal })
),
timeout
);
if (!parallel2) {
return executeSequential(enabledGuardrails, invokeWithTimeout, {
continueOnFailure,
logLevel,
logger,
label: "Input"
});
}
const runFallback = () => executeBatchInputGuardrails(
enabledGuardrails,
toNormalizedGuardrailContext(params),
timeout,
logLevel,
logger
);
if (USE_ENHANCED_RUNTIME) {
try {
return await executeInputGuardrailsWithEnhancedRuntime(
enabledGuardrails,
toNormalizedGuardrailContext(params),
{ parallel: true, timeout, continueOnFailure }
);
} catch {
return runFallback();
}
}
return runFallback();
}
async function executeOutputGuardrails(guardrails, params, options = {}) {
const {
parallel: parallel2 = true,
timeout = 3e4,
// 30 seconds
continueOnFailure = true,
logLevel = "warn",
logger = console,
accumulatedText
} = options;
const enabledGuardrails = prepareGuardrails(guardrails);
const invokeWithTimeout = makeInvokeWithTimeout(
(guardrail, signal) => Promise.resolve(guardrail.execute(params, accumulatedText, { signal })),
timeout
);
if (!parallel2) {
return executeSequential(enabledGuardrails, invokeWithTimeout, {
continueOnFailure,
logLevel,
logger,
label: "Output"
});
}
const runFallback = () => executeParallelPerGuardrail(
enabledGuardrails,
invokeWithTimeout,
"Output",
logLevel,
logger
);
if (USE_ENHANCED_RUNTIME) {
try {
return await executeOutputGuardrailsWithEnhancedRuntime(
enabledGuardrails,
params,
{ parallel: true, timeout, continueOnFailure, accumulatedText }
);
} catch {
return runFallback();
}
}
return runFallback();
}
// src/core.ts
function createOutputGuardrail(name, execute) {
return { name, execute };
}
// src/guardrails/output.ts
var EMPTY_CONTENT = {
text: "",
object: null,
usage: void 0,
finishReason: void 0,
generationTimeMs: void 0,
reasoningText: void 0
};
function emptyContent() {
return { ...EMPTY_CONTENT };
}
function mapUsage(usage) {
if (!usage) {
return void 0;
}
const pickNumber = (keys) => {
for (const key of keys) {
const value = usage[key];
if (typeof value === "number") {
return value;
}
if (value && typeof value === "object" && "total" in value && typeof value.total === "number") {
return value.total;
}
}
return void 0;
};
const totalTokens = pickNumber(["totalTokens"]);
const promptTokens = pickNumber(["inputTokens", "promptTokens"]);
const completionTokens = pickNumber(["outputTokens", "completionTokens"]);
const computedTotal = totalTokens ?? (promptTokens !== void 0 && completionTokens !== void 0 ? promptTokens + completionTokens : void 0);
if (promptTokens === void 0 && completionTokens === void 0 && computedTotal === void 0) {
return void 0;
}
return {
promptTokens,
completionTokens,
totalTokens: computedTotal
};
}
function extractGenerationTime(result) {
return result.experimental_providerMetadata?.generationTimeMs ?? result.providerMetadata?.generationTimeMs ?? 0;
}
function extractReasoningText(result) {
return result.reasoningText || result.experimental_providerMetadata?.reasoningText || void 0;
}
function createContent(partial) {
return {
...EMPTY_CONTENT,
...partial
};
}
function extractContent(result) {
const contentArray = result.content;
if ("content" in result && Array.isArray(contentArray) && contentArray.length > 0) {
const typedResult = result;
const textContent = typedResult.content.filter((item) => item.type === "text" && item.text).map((item) => item.text).join("");
const objectValue = typedResult.output ?? typedResult.object ?? null;
return createContent({
text: textContent || "",
object: objectValue,
usage: mapUsage(typedResult.usage),
finishReason: typedResult.finishReason,
generationTimeMs: extractGenerationTime(typedResult),
reasoningText: extractReasoningText(typedResult)
});
}
if ("output" in result && result.output !== null && result.output !== void 0) {
const outputResult = result;
return createContent({
text: outputResult.text || "",
object: outputResult.output,
usage: mapUsage(outputResult.usage),
finishReason: outputResult.finishReason,
generationTimeMs: extractGenerationTime(outputResult),
reasoningText: extractReasoningText(outputResult)
});
}
if ("object" in result && result.object !== null && result.object !== void 0) {
const objectResult = result;
return createContent({
text: objectResult.text || "",
object: objectResult.object,
usage: mapUsage(objectResult.usage),
finishReason: objectResult.finishReason,
generationTimeMs: extractGenerationTime(objectResult),
reasoningText: extractReasoningText(objectResult)
});
}
if ("text" in result && typeof result.text === "string") {
const textResult = result;
return createContent({
text: textResult.text || "",
object: null,
usage: mapUsage(textResult.usage),
finishReason: textResult.finishReason,
generationTimeMs: extractGenerationTime(textResult),
reasoningText: extractReasoningText(textResult)
});
}
if ("textStream" in result || "objectStream" in result || "embeddings" in result || "then" in result) {
return emptyContent();
}
return emptyContent();
}
var secretRedaction = createOutputGuardrail(
"secret-redaction",
(context) => {
const { text, object } = extractContent(context.result);
const content = text || (object ? JSON.stringify(object) : "");
const secretPatterns = [
// API Keys (various formats) - more specific patterns to reduce false positives
{
name: "API Key",
pattern: /(?:api[_-]?key|apikey|access[_-]?key)\s*[:=]\s*['"]?([a-zA-Z0-9]{20,})['"]?/gi
},
// AWS Access Keys
{
name: "AWS Access Key",
pattern: /AKIA[0-9A-Z]{16}/g
},
// AWS Secret Keys
{
name: "AWS Secret Key",
pattern: /[A-Za-z0-9/+=]{40}/g
},
// AWS ARNs
{
name: "AWS ARN",
pattern: /arn:aws:[a-zA-Z0-9-]+:[a-zA-Z0-9-]*:[0-9]*:[a-zA-Z0-9-_/.:*]+/g
},
// JWT Tokens
{
name: "JWT Token",
pattern: /eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g
},
// Bearer Tokens
{
name: "Bearer Token",
pattern: /Bearer\s+[a-zA-Z0-9_.-]+/gi
},
// GitHub Personal Access Tokens
{
name: "GitHub Token",
pattern: /ghp_[a-zA-Z0-9]{36}/g
},
// Google API Keys
{
name: "Google API Key",
pattern: /AIza[0-9A-Za-z_-]{35}/g
},
// PEM Certificate/Key blocks
{
name: "PEM Certificate/Key",
pattern: /-----BEGIN [A-Z ]+-----[\s\S]*?-----END [A-Z ]+-----/g
},
// SSH Private Keys
{
name: "SSH Private Key",
pattern: /-----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----[\s\S]*?-----END (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----/g
},
// Database Connection Strings
{
name: "Database Connection String",
pattern: /(?:mongodb|postgres|mysql|redis):\/\/[^@\s]+:[^@\s]+@[^/\s]+/gi
},
// Environment variables with secrets (more specific to reduce false positives)
{
name: "Environment Secret",
pattern: /(?:token|secret|password|key)\s*[:=]\s*['"]?([a-zA-Z0-9_.-]{16,})['"]?/gi
}
];
const detectedSecrets = [];
for (const { name, pattern } of secretPatterns) {
if (pattern.global) {
pattern.lastIndex = 0;
}
let match;
while ((match = pattern.exec(content)) !== null) {
const maskedSecret = match[0].length > 20 ? match[0].slice(0, 8) + "..." + match[0].slice(-4) : match[0].slice(0, 4) + "...";
detectedSecrets.push({
type: name,
pattern: maskedSecret,
position: match.index
});
if (match[0].length === 0) {
pattern.lastIndex++;
}
}
}
if (detectedSecrets.length > 0) {
return {
tripwireTriggered: true,
message: `Output contains ${detectedSecrets.length} potential secret(s): ${detectedSecrets.map((s) => s.type).join(", ")}`,
severity: "critical",
metadata: {
secretsDetected: detectedSecrets.length,
secretTypes: detectedSecrets.map((s) => s.type),
contentLength: content.length
},
suggestion: "Remove sensitive information before sharing output",
info: {
guardrailName: "secret-redaction",
secretsDetected: detectedSecrets.length,
secretTypes: detectedSecrets.map((s) => s.type),
contentLength: content.length
}
};
}
return {
tripwireTriggered: false,
info: {
guardrailName: "secret-redaction"
}
};
}
);
var unsafeContentDetector = createOutputGuardrail(
"unsafe-content-detector",
(context) => {
const { text, object } = extractContent(context.result);
const content = text || (object ? JSON.stringify(object) : "");
const unsafePatterns = [
{
category: "Violence",
patterns: [
/\b(kill|murder|assassinate|torture|bomb|weapon|gun|knife|explosive)\b/gi,
/\b(harm|hurt|injure|attack|assault|fight)\s+(someone|people|person)/gi,
/\b(violence|violent|aggression|aggressive)\b/gi
]
},
{
category: "Hate Speech",
patterns: [
/\b(hate|racist|sexist|homophobic|transphobic|xenophobic)\b/gi,
/\b(nazi|fascist|supremacist|terrorism|terrorist)\b/gi,
/\b(discrimination|prejudice|bigotry)\b/gi
]
},
{
category: "Self-Harm",
patterns: [
/\b(suicide|self-harm|self-hurt|cut myself|end my life)\b/gi,
/\b(want to die|kill myself|harm myself)\b/gi,
/\b(suicidal|depression|self-destruction)\b/gi
]
},
{
category: "Illegal Activities",
patterns: [
/\b(illegal drugs|drug dealing|money laundering|fraud|scam)\b/gi,
/\b(hack|crack|pirate|steal|burglary|theft)\b/gi,
/\b(counterfeit|forgery|blackmail|extortion)\b/gi
]
},
{
category: "Adult Content",
patterns: [
/\b(pornography|explicit sexual|adult content|nsfw)\b/gi,
/\b(sexual explicit|graphic sexual|sexual imagery)\b/gi
]
},
{
category: "Personal Information",
patterns: [
/\b\d{3}-\d{2}-\d{4}\b/g,
// SSN format
/\b\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b/g,
// Credit card format
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g
// Email
]
}
];
const detectedIssues = [];
for (const { category, patterns } of unsafePatterns) {
let totalMatches = 0;
for (const pattern of patterns) {
const matches = content.match(pattern);
if (matches) {
totalMatches += matches.length;
}
}
if (totalMatches > 0) {
detectedIssues.push({ category, matches: totalMatches });
}
}
if (detectedIssues.length > 0) {
return {
tripwireTriggered: true,
message: `Unsafe content detected: ${detectedIssues.map((i) => `${i.category} (${i.matches})`).join(", ")}`,
severity: "high",
metadata: {
categoriesDetected: detectedIssues.length,
issues: detectedIssues,
contentLength: content.length
},
suggestion: "Review and modify content to remove potentially harmful material",
info: {
guardrailName: "unsafe-content-detector",
categoriesDetected: detectedIssues.length,
issues: detectedIssues,
contentLength: content.length
}
};
}
return {
tripwireTriggered: false,
info: {
guardrailName: "unsafe-content-detector"
}
};
}
);
// src/guardrails/retry-helpers.ts
var SEVERITY_ORDER = {
low: 1,
medium: 2,
high: 3,
critical: 4
};
function findGuardrailForResult(result, guardrails) {
const name = result.context?.guardrailName ?? result.info?.guardrailName;
return guardrails.find((g) => g.name === name);
}
function getInstructionFromResult(result, guardrail, context) {
if (guardrail?.getRetryInstruction) {
const instruction = guardrail.getRetryInstruction({ ...context, result });
if (typeof instruction === "string") {
return { message: instruction };
}
return instruction ?? void 0;
}
if (result.message) {
const suggestionPart = result.suggestion ? `. ${result.suggestion}` : "";
return {
message: `Please try again. The previous response was blocked: ${result.message}${suggestionPart}`
};
}
return void 0;
}
function selectBlockedResults(summary, strategy) {
const { blockedResults } = summary;
if (blockedResults.length === 0) return [];
switch (strategy) {
case "first": {
return [blockedResults[0]];
}
case "all": {
return blockedResults;
}
default: {
const sorted = [...blockedResults].toSorted((a, b) => {
const severityA = SEVERITY_ORDER[a.severity ?? "medium"] ?? 2;
const severityB = SEVERITY_ORDER[b.severity ?? "medium"] ?? 2;
return severityB - severityA;
});
return [sorted[0]];
}
}
}
function combineInstructions(instructions) {
if (instructions.length === 0) {
return { message: "Please try again with a different approach." };
}
if (instructions.length === 1) {
return instructions[0];
}
const combinedMessage = instructions.map((inst, i) => `${i + 1}. ${inst.message}`).join("\n");
const tempAdjustments = instructions.map((i) => i.temperatureAdjustment).filter((t) => t !== void 0);
const avgTempAdjustment = tempAdjustments.length > 0 ? tempAdjustments.reduce((a, b) => a + b, 0) / tempAdjustments.length : void 0;
return {
message: `Please address the following issues:
${combinedMessage}`,
temperatureAdjustment: avgTempAdjustment,
context: { combinedFrom: instructions.length }
};
}
function createDefaultBuildRetryParams(options) {
const {
outputGuardrails,
multipleBlockedStrategy = "highest-severity",
attempt,
maxRetries
} = options;
return ({ summary, originalParams, lastParams }) => {
const selectedResults = selectBlockedResults(
summary,
multipleBlockedStrategy
);
const instructionContext = { attempt, maxRetries };
const instructions = [];
for (const result of selectedResults) {
const guardrail = findGuardrailForResult(result, outputGuardrails);
const instruction = getInstructionFromResult(
result,
guardrail,
instructionContext
);
if (instruction) {
instructions.push(instruction);
}
}
const finalInstruction = combineInstructions(instructions);
let newTemperature = lastParams.temperature ?? 0.7;
if (finalInstruction.temperatureAdjustment !== void 0) {
newTemperature = Math.max(
0,
Math.min(1, newTemperature + finalInstruction.temperatureAdjustment)
);
}
const existingPrompt = Array.isArray(lastParams.prompt) ? lastParams.prompt : Array.isArray(originalParams.prompt) ? originalParams.prompt : [];
return {
...lastParams,
temperature: newTemperature,
prompt: [
...existingPrompt,
{
role: "user",
content: [{ type: "text", text: finalInstruction.message }]
}
]
};
};
}
function resolveRetryConfig(globalRetry, blockedGuardrails) {
let maxRetries = globalRetry?.maxRetries ?? 0;
let backoffMs = globalRetry?.backoffMs ?? 0;
if (maxRetries === 0) {
for (const guardrail of blockedGuardrails) {
if (guardrail.retry?.maxRetries !== void 0) {
maxRetries = Math.max(maxRetries, guardrail.retry.maxRetries);
}
if (guardrail.retry?.backoffMs !== void 0 && backoffMs === 0) {
backoffMs = guardrail.retry.backoffMs;
}
}
}
return { maxRetries, backoffMs };
}
// src/guardrails/generate-result-sync.ts
function joinTextFromContent(content) {
if (!Array.isArray(content)) {
return void 0;
}
const parts = content.filter(
(part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string"
).map((part) => part.text);
return parts.length > 0 ? parts.join("") : void 0;
}
function snapshotGenerateResultText(result) {
const record = result;
return {
text: typeof record.text === "string" ? record.text : void 0,
contentJoined: joinTextFromContent(record.content)
};
}
function syncGenerateResultTextAfterGuardrails(result, before) {
const record = result;
const afterContent = joinTextFromContent(record.content);
const afterText = typeof record.text === "string" ? record.text : void 0;
const textChanged = afterText !== void 0 && afterText !== before.text;
const contentChanged = afterContent !== void 0 && afterContent !== before.contentJoined;
if (textChanged && !contentChanged && afterText !== void 0) {
record.content = [{ type: "text", text: afterText }];
record.text = afterText;
return result;
}
if (contentChanged && afterContent !== void 0) {
record.text = afterContent;
return result;
}
if (afterContent !== void 0) {
record.text = afterContent;
} else if (afterText !== void 0) {
record.content = [{ type: "text", text: afterText }];
}
return result;
}
// src/guardrails/middleware-factories.ts
var emptyV4Usage = {
inputTokens: {
total: 0,
noCache: void 0,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: 0,
text: void 0,
reasoning: void 0
}
};
var finishReasonStop = {
unified: "stop",
raw: void 0
};
var finishReasonOther = {
unified: "other",
raw: void 0
};
function inputGuardrailsMiddleware(config) {
const {
inputGuardrails,
context,
executionOptions = {},
onInputBlocked,
throwOnBlocked = false
} = config;
return {
specificationVersion: "v4",
transformParams: async ({
params
}) => {
const baseContext = normalizeGuardrailContext(params);
const guardrailContext = context ? { ...baseContext, requestContext: context } : baseContext;
const executionStartTime = Date.now();
const inputResults = await executeInputGuardrails(
inputGuardrails,
guardrailContext,
executionOptions
);
const blockedResults = inputResults.filter((r) => r.tripwireTriggered);
if (blockedResults.length > 0) {
if (onInputBlocked) {
const executionSummary = createExecutionSummary(
inputResults,
executionStartTime
);
onInputBlocked(executionSummary, guardrailContext);
}
if (throwOnBlocked) {
const blockedGuardrails = blockedResults.map((r) => ({
name: r.context?.guardrailName || "unknown",
message: r.message || "Blocked",
severity: r.severity || "medium"
}));
throw new GuardrailsInputError(blockedGuardrails);
}
const enhancedParams = params;
enhancedParams.guardrailsBlocked = blockedResults;
return enhancedParams;
}
return params;
},
wrapGenerate: async ({
doGenerate,
params
}) => {
const paramsWithGuardrails = params;
if (paramsWithGuardrails.guardrailsBlocked) {
const blockedResults = paramsWithGuardrails.guardrailsBlocked;
const blockedMessage = blockedResults.map((r) => r.message).join(", ");
const blockedText = `[Input blocked: ${blockedMessage}]`;
return {
text: blockedText,
content: [{ type: "text", text: blockedText }],
finishReason: finishReasonOther,
usage: emptyV4Usage,
warnings: [],
rawCall: { rawPrompt: params.prompt, rawSettings: {} },
response: { headers: {} }
};
}
return doGenerate();
},
wrapStream: async ({
doStream,
params
}) => {
const paramsWithGuardrails = params;
if (paramsWithGuardrails.guardrailsBlocked) {
const blockedResults = paramsWithGuardrails.guardrailsBlocked;
const blockedMessage = blockedResults.map((r) => r.message).join(", ");
const stream = new ReadableStream({
start(controller) {
controller.enqueue({
type: "text-delta",
id: "1",
delta: `[Input blocked: ${blockedMessage}]`
});
controller.enqueue({
type: "finish",
finishReason: finishReasonOther,
usage: emptyV4Usage
});
controller.close();
}
});
return { stream };
}
return doStream();
}
};
}
function outputGuardrailsMiddleware(config) {
const {
outputGuardrails,
context,
executionOptions = {},
onOutputBlocked,
throwOnBlocked = false,
replaceOnBlocked = true,
streamMode = "buffer",
retry,
stopOnGuardrailViolation
} = config;
return {
specificationVersion: "v4",
wrapGenerate: async ({
doGenerate,
params,
model
}) => {
const result = await doGenerate();
const resultTextBeforeGuardrails = snapshotGenerateResultText(result);
const baseContext = normalizeGuardrailContext(params);
const guardrailContext = context ? { ...baseContext, requestContext: context } : baseContext;
const aiResult = result;
const outputContext = {
input: guardrailContext,
result: aiResult
};
const startTime = Date.now();
const outputResults = await executeOutputGuardrails(
outputGuardrails,
outputContext,
executionOptions
);
const executionSummary = createExecutionSummary(
outputResults,
startTime
);
if (executionSummary.blockedResults.length > 0) {
const blockedGuardrailObjects = executionSummary.blockedResults.map(
(r) => outputGuardrails.find(
(g) => g.name === (r.context?.guardrailName ?? r.info?.guardrailName)
)
).filter((g) => g !== void 0);
const effectiveRetry = resolveRetryConfig(
retry,
blockedGuardrailObjects
);
if (effectiveRetry.maxRetries > 0 && (retry?.onlyWhen ? retry.onlyWhen(executionSummary) : true)) {
const maxRetries = effectiveRetry.maxRetries;
let lastParams = params;
let lastResult = result;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const wait = typeof effectiveRetry.backoffMs === "function" ? effectiveRetry.backoffMs(attempt) : effectiveRetry.backoffMs ?? 0;
if (wait && wait > 0) {
await new Promise((r) => setTimeout(r, wait));
}
const buildRetryParams = retry?.buildRetryParams ?? createDefaultBuildRetryParams({
outputGuardrails,
multipleBlockedStrategy: retry?.multipleBlockedStrategy ?? "highest-severity",
attempt,
maxRetries
});
const nextParams = buildRetryParams({
summary: executionSummary,
originalParams: params,
lastParams,
lastResult
});
const retryRaw = await model.doGenerate(nextParams);
const retryTextBeforeGuardrails = snapshotGenerateResultText(retryRaw);
const retryResult = retryRaw;
const retryContext = {
input: normalizeGuardrailContext(nextParams),
result: retryResult
};
const retryStart = Date.now();
const retryResults = await executeOutputGuardrails(
outputGuardrails,
retryContext,
executionOptions
);
const retrySummary = createExecutionSummary(
retryResults,
retryStart
);
if (retrySummary.blockedResults.length === 0) {
return syncGenerateResultTextAfterGuardrails(
retryRaw,
retryTextBeforeGuardrails
);
}
lastParams = nextParams;
lastResult = retryResult;
}
}
if (onOutputBlocked) {
onOutputBlocked(executionSummary, guardrailContext, result);
}
if (throwOnBlocked) {
const blockedGuardrails = executionSummary.blockedResults.map(
(r) => ({
name: r.context?.guardrailName || "unknown",
message: r.message || "Blocked",
severity: r.severity || "medium"
})
);
throw new GuardrailsOutputError(blockedGuardrails);
}
if (replaceOnBlocked) {
const blockedMessage = executionSummary.blockedResults.map((r) => r.message).join(", ");
const blockedText = `[Output blocked: ${blockedMessage}]`;
const replaced = {
...result,
text: blockedText,
content: [{ type: "text", text: blockedText }]
};
return replaced;
}
}
return syncGenerateResultTextAfterGuardrails(
result,
resultTextBeforeGuardrails
);
},
wrapStream: async ({
doStream,
params,
model
}) => {
const streamResult = await doStream();
if (streamMode === "buffer") {
let accumulatedText2 = "";
let streamUsage;
let streamFinishReason;
const blockedChunks = [];
const transformStream2 = new TransformStream({
transform(chunk) {
if (chunk.type === "text-delta") {
const anyChunk = chunk;
accumulatedText2 += anyChunk.delta ?? anyChunk.textDelta ?? "";
} else if (chunk.type === "finish") {
streamUsage = chunk.usage;
streamFinishReason = chunk.finishReason;
}
blockedChunks.push(chunk);
},
async flush(controller) {
const baseContext = normalizeGuardrailContext(params);
const guardrailContext = context ? { ...baseContext, requestContext: context } : baseContext;
const streamedResult = {
text: accumulatedText2,
content: [{ type: "text", text: accumulatedText2 }],
usage: streamUsage,
finishReason: streamFinishReason
};
const streamedTextBeforeGuardrails = snapshotGenerateResultText(streamedResult);
const outputContext = {
input: guardrailContext,
result: streamedResult
};
const startTime = Date.now();
const outputResults = await executeOutputGuardrails(
outputGuardrails,
outputContext,
{
...executionOptions,
accumulatedText: accumulatedText2
}
);
const executionSummary = createExecutionSummary(
outputResults,
startTime
);
if (executionSummary.blockedResults.length > 0) {
const blockedGuardrailObjects = executionSummary.blockedResults.map(
(r) => outputGuardrails.find(
(g) => g.name === (r.context?.guardrailName ?? r.info?.guardrailName)
)
).filter((g) => g !== void 0);
const effectiveRetry = resolveRetryConfig(
retry,
blockedGuardrailObjects
);
if (effectiveRetry.maxRetries > 0 && (retry?.onlyWhen ? retry.onlyWhen(executionSummary) : true)) {
const maxRetries = effectiveRetry.maxRetries;
let lastParams = params;
let lastResult = {
text: accumulatedText2
};
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const wait = typeof effectiveRetry.backoffMs === "function" ? effectiveRetry.backoffMs(attempt) : effectiveRetry.backoffMs ?? 0;
if (wait && wait > 0) {
await new Promise((r) => setTimeout(r, wait));
}
const buildRetryParams = retry?.buildRetryParams ?? createDefaultBuildRetryParams({
outputGuardrails,
multipleBlockedStrategy: retry?.multipleBlockedStrategy ?? "highest-severity",
attempt,
maxRetries
});
const nextParams = buildRetryParams({
summary: executionSummary,
originalParams: params,
lastParams,
lastResult
});
const retryResult = await model.doGenerate(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
nextParams
);
const retryContext = {
input: normalizeGuardrailContext(nextParams),
result: retryResult
};
const retryStart = Date.now();
const retryResults = await executeOutputGuardrails(
outputGuardrails,
retryContext,
executionOptions
);
const retrySummary = createExecutionSummary(
retryResults,
retryStart
);
if (retrySummary.blockedResults.length === 0) {
const { text: repairedText } = extractContent(
retryResult
);
controller.enqueue({
type: "text-delta",
id: "1",
delta: repairedText
});
controller.enqueue({
type: "finish",
finishReason: finishReasonStop,
usage: emptyV4Usage
});
return;
}
lastParams = nextParams;
lastResult = retryResult;
}
}
if (onOutputBlocked) {
onOutputBlocked(
executionSummary,
guardrailContext,
streamedResult
);
}
if (throwOnBlocked) {
controller.error(
new Error(
`Output guardrails blocked response: ${executionSummary.blockedResults.map((r) => r.message).join(", ")}`
)
);
return;
}
if (replaceOnBlocked) {
const blockedMessage = executionSummary.blockedResults.map((r) => r.message).join(", ");
controller.enqueue({
type: "text-delta",
id: "1",
delta: `[Output blocked: ${blockedMessage}]`
});
controller.enqueue({
type: "finish",
finishReason: finishReasonOther,
usage: emptyV4Usage
});
} else {
for (const chunk of blockedChunks) {
controller.enqueue(chunk);
}
}
} else {
syncGenerateResultTextAfterGuardrails(
streamedResult,
streamedTextBeforeGuardrails
);
const finalText = typeof streamedResult.text === "string" ? streamedResult.text : accumulatedText2;
if (finalText === accumulatedText2) {
for (const chunk of blockedChunks) {
controller.enqueue(chunk);
}
} else {
controller.enqueue({
type: "text-delta",
id: "1",
delta: finalText
});
controller.enqueue({
type: "finish",
finishReason: streamFinishReason ?? finishReasonStop,
usage: streamUsage ?? emptyV4Usage
});
}
}
}
});
return {
stream: streamResult.stream.pipeThrough(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
transformStream2
)
};
}
let accumulatedText = "";
let blocked = false;
let chunkIndex = 0;
const streamViolationHistory = [];
const transformStream = new TransformStream({
async transform(chunk, controller) {
if (blocked) {
return;
}
if (chunk.type === "text-delta") {
const anyChunk = chunk;
accumulatedText += anyChunk.delta ?? anyChunk.textDelta ?? "";
chunkIndex++;
const baseContext = normalizeGuardrailContext(params);
const guardrailContext = context ? { ...baseContext, requestContext: context } : baseContext;
const outputContext = {
input: guardrailContext,
result: { text: accumulatedText }
};
const startTime = Date.now();
const outputResults = await executeOutputGuardrails(
outputGuardrails,
outputContext,
{
...executionOptions,
accumulatedText
}
);
const executionSummary = createExecutionSummary(
outputResults,
startTime
);
if (executionSummary.blockedResults.length > 0) {
streamViolationHistory.push({
chunkIndex,
summary: executionSummary
});
const shouldStopEarly = stopOnGuardrailViolation && checkStreamStopCondition(
stopOnGuardrailViolation,
streamViolationHistory
);
if (shouldStopEarly) {
blocked = true;
}
if (blocked) {
if (onOutputBlocked) {
onOutputBlocked(executionSummary, guardrailContext, {
text: accumulatedText
});
}
if (throwOnBlocked) {
controller.error(
new Error(
`Output guardrails blocked response: ${executionSummary.blockedResults.map((r) => r.message).join(", ")}`
)
);
return;
}
if (replaceOnBlocked) {
const blockedMessage = executionSummary.blockedResults.map((r) => r.message).join(", ");
controller.enqueue({
type: "text-delta",
id: "1",
delta: `[Output blocked: ${blockedMessage}]`
});
controller.enqueue({
type: "finish",
finishReason: finishReasonOther,
usage: emptyV4Usage
});
return;
}
}
}
controller.enqueue(chunk);
} else {
controller.enqueue(chunk);
}
}
});
return {
stream: streamResult.stream.pipeThrough(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
transformStream
)
};
}
};
}
// src/guardrails.ts
async function runWithEnvelope(name, version, run) {
const startTime = ENABLE_PERFORMANCE_TRACKING ? Date.now() : 0;
try {
const result = await run();
const executionTime = ENABLE_PERFORMANCE_TRACKING ? Date.now() - startTime : void 0;
return {
...result,
context: createConditionalContext(
name,
version,
executionTime,
result.context
)
};
} catch (error) {
const executionTime = ENABLE_PERFORMANCE_TRACKING ? Date.now() - startTime : void 0;
return guardrailErrorResult(name, error, {
context: createConditionalContext(name, version, executionTime)
});
}
}
function defineInputGuardrail(guardrail) {
const originalExecute = guardrail.execute;
return {
enabled: true,
priority: "medium",
version: "1.0.0",
tags: [],
...guardrail,
execute: (params, options) => runWithEnvelope(
guardrail.name,
guardrail.version,
() => options === void 0 ? originalExecute(params) : originalExecute(params, options)
)
};
}
// src/guardrails/normalization.ts
var RE_WHITESPACE = /\s+/g;
var RE_WHITESPACE_SPLIT = /\s+/;
var RE_WORD_CHAR = /\w/;
var INVISIBLE_CHARS = /[\u00AD\u180E\u200B-\u200F\u2028-\u202F\u2060\uFEFF]/;
var RE_INVISIBLE = new RegExp(INVISIBLE_CHARS, "g");
var RE_REGEX_META = /[.*+?^${}()|[\]\\]/g;
var TYPO_MAP = {
ingnore: "ignore",
ignor: "ignore",
ign0re: "ignore",
previ0us: "previous",
previus: "previous",
instrucions: "instructions",
instrucion: "instruction",
overide: "override",
overrride: "override",
disreguard: "disregard",
disrega: "disregard"
};
var HOMOGLYPH_MAP = [
["\uFF21", "A"],
["\uFF22", "B"],
["\uFF23", "C"],
["\uFF24", "D"],
["\uFF25", "E"],
["\uFF26", "F"],
["\uFF27", "G"],
["\uFF28", "H"],
["\uFF29", "I"],
["\uFF2A", "J"],
["\uFF2B", "K"],
["\uFF2C", "L"],
["\uFF2D", "M"],
["\uFF2E", "N"],
["\uFF2F", "O"],
["\uFF30", "P"],
["\uFF31", "Q"],
["\uFF32", "R"],
["\uFF33", "S"],
["\uFF34", "T"],
["\uFF35", "U"],
["\uFF36", "V"],
["\uFF37", "W"],
["\uFF38", "X"],
["\uFF39", "Y"],
["\uFF3A", "Z"],
["\uFF41", "a"],
["\uFF42", "b"],
["\uFF43", "c"],
["\uFF44", "d"],
["\uFF45", "e"],
["\uFF46", "f"],
["\uFF47", "g"],
["\uFF48", "h"],
["\uFF49", "i"],
["\uFF4A", "j"],
["\uFF4B", "k"],
["\uFF4C", "l"],
["\uFF4D", "m"],
["\uFF4E", "n"],
["\uFF4F", "o"],
["\uFF50", "p"],
["\uFF51", "q"],
["\uFF52", "r"],
["\uFF53", "s"],
["\uFF54", "t"],
["\uFF55", "u"],
["\uFF56", "v"],
["\uFF57", "w"],
["\uFF58", "x"],
["\uFF59", "y"],
["\uFF5A", "z"],
["\u0430", "a"],
["\u043E", "o"],
["\u0435", "e"],
["\u0440", "p"],
["\u0441", "c"],
["\u0445", "x"],
["\u0456", "i"],
["\u04CF", "d"],
["\u0443", "y"],
["\u0458", "j"],
// Greek look-alikes.
["\u03B1", "a"],
["\u03BF", "o"],
["\u03C1", "p"]
];
var HOMOGLYPH_LOOKUP = new Map(HOMOGLYPH_MAP);
var LEET_SEQUENCE_MAP = [
[/\|\\\|/g, "n"],
[/\|_\|/g, "u"],
[/\|v\|/g, "m"],
[/\|<|\|\{/g, "k"],
[/\|2/g, "r"],
[/\|\)/g, "d"],
[/\|=/g, "f"],
[/\|\*/g, "p"],
[/\/\/\\\\/g, "m"],
[/\\\/\\\//g, "w"],
[/\\\//g, "v"],
[/></g, "x"]
];
var LEET_MAP = {
"0": "o",
"1": "i",
"3": "e",
"4": "a",
"5": "s",
"6": "g",
"7": "t",
"8": "b",
"9": "g",
"@": "a",
$: "s",
"!": "i"
};
var PHONETIC_PATTERNS = [
[/\bignorre?\b/gi, "ignore"],
[/\bign?r\b/gi, "ignore"],
[/\bpr[3e]vious\b/gi, "previous"],
[/\binstr(?:uk|uc)tions?\b/gi, "instructions"],
[/\boverryde\b/gi, "override"],
[/\bd[i1]sregard\b/gi, "disregard"],
[/\bpromt\b/gi, "prompt"],
[/\brulz\b/gi, "rules"]
];
var DEFAULT_DETECT_NORMALIZATION = {
enabled: true,
foldHomoglyphs: true,
stripInvisible: true,
collapseWhitespace: true,
joinSeparatedLetters: true,
normalizeCase: true,
decodeLeetspeak: true,
repairTypos: true,
repairPhonetics: true
};
function applyHomoglyphs(input) {
let result = input;
for (const [from, to] of HOMOGLYPH_MAP) {
result = result.split(from).join(to);
}
return result;
}
function collapseWhitespace(input) {
return input.replaceAll(RE_WHITESPACE, " ");
}
function joinSeparatedLetters(input) {
return input.replaceAll(/(?<!\w)(\w)(\s+\w)+(?!\w)/g, (match) => {
const tokens = match.split(RE_WHITESPACE_SPLIT);
const allSingleChars = tokens.every(
(token) => token.length === 1 && RE_WORD_CHAR.test(token)
);
return allSingleChars ? tokens.join("") : match;
});
}
function decodeLeetspeak(input) {
let result = input;
for (const [pattern, replacement] of LEET_SEQUENCE_MAP) {
result = result.replace(pattern, () => replacement);
}
for (const [from, to] of Object.entries(LEET_MAP)) {
result = result.replaceAll(
new RegExp(from.replaceAll(RE_REGEX_META, String.raw`\$&`), "gi"),
() => to
);
}
return result;
}
function repairTypos(input) {
let result = input;
for (const [typo, correct] of Object.entries(TYPO_MAP)) {
const escaped = typo.replaceAll(RE_REGEX_META, String.raw`\$&`);
result = result.replaceAll(
new RegExp(String.raw`\b${escaped}\b`, "gi"),
() => correct
);
}
return result;
}
function repairPhonetics(input) {
let result = input;
for (const [pattern, replacement] of PHONETIC_PATTERNS) {
result = result.replace(pattern, () => replacement);
}
return result;
}
function resolveDetectNormalization(options) {
if (options === false) {
return { ...DEFAULT_DETECT_NORMALIZATION, enabled: false };
}
if (options === true || options === void 0) {
return DEFAULT_DETECT_NORMALIZATION;
}
return { ...DEFAULT_DETECT_NORMALIZATION, ...options };
}
function normalizeForDetection(input, options) {
const config = resolveDetectNormalization(options);
if (!config.enabled) {
return input.trim();
}
let normalized = input.normalize("NFKC");
if (config.foldHomoglyphs) normalized = applyHomoglyphs(normalized);
if (config.stripInvisible)
normalized = normalized.replaceAll(RE_INVISIBLE, "");
if (config.collapseWhitespace) normalized = collapseWhitespace(normalized);
if (config.joinSeparatedLetters) {
normalized = joinSeparatedLetters(normalized);
}
if (config.normalizeCase) normalized = normalized.toLowerCase();
if (config.decodeLeetspeak) normalized = decodeLeetspeak(normalized);
if (config.repairTypos) normalized = repairTypos(normalized);
if (config.repairPhonetics) normalized = repairPhonetics(normalized);
if (config.collapseWhitespace) normalized = collapseWhitespace(normalized);
return normalized.trim();
}
// src/guardrails/enhanced-prompt-injection.ts
var RE_CALL_MALICIOUS_PARAMS = /call\s+(\w+)\s+with\s+malicious\s+params/i;
var RE_OVERRIDE_FUNCTION = /override\s+function\s+(\w+)/i;
var ENHANCED_INJECTION_PATTERNS = [
// Direct instruction injection
{
pattern: /ignore\s+previous\s+instructions/i,
weight: 0.9,
type: "direct_injection",
semanticKeywords: ["ignore", "previous", "instructions"],
contextIndicators: ["system", "prompt", "override"]
},
{
pattern: /system\s*:\s*you\s+are\s+now/i,
weight: 0.8,
type: "role_confusion",
semanticKeywords: ["system", "you", "are", "now"],
contextIndicators: ["role", "identity", "persona"]
},
{
pattern: /forget\s+everything\s+above/i,
weight: 0.9,
type: "context_override",
semanticKeywords: ["forget", "everything", "above"],
contextIndicators: ["memory", "history", "context"]
},
// Tool manipulation patterns
{
pattern: RE_CALL_MALICIOUS_PARAMS,
weight: 0.9,
type: "tool_manipulation",
semanticKeywords: ["call", "malicious", "params"],
contextIndicators: ["function", "tool", "execute"]
},
{
pattern: RE_OVERRIDE_FUNCTION,
weight: 0.95,
type: "function_override",
semanticKeywords: ["override", "function"],
contextIndicators: ["code", "execution", "security"]
},
// Cascade attack patterns
{
pattern: /now\s+that\s+you\s+are\s+in\s+(\w+)\s+mode/i,
weight: 0.8,
type: "cascade_attempt",
semanticKeywords: ["now", "that", "you", "are", "mode"],
contextIndicators: ["state", "mode", "persona"]
},
// Authority manipulation
{
pattern: /(?:security\s+team|administrator|system\s+admin|emergency|urgent)/i,
weight: 0.7,
type: "authority_manipulation",
semanticKeywords: ["security", "admin", "emergency", "urgent"],
contextIndicators: ["authority", "priority", "override"]
}
];
var TOOL_CALL_INJECTION_PATTERNS = [
{
pattern: RE_CALL_MALICIOUS_PARAMS,
weight: 0.9,
injectionType: "parameter_manipulation"
},
{
pattern: RE_OVERRIDE_FUNCTION,
weight: 0.95,
injectionType: "function_override"
},
{
pattern: /chain\s+(\w+)\s+to\s+(\w+)\s+to\s+(\w+)/i,
weight: 0.8,
injectionType: "tool_chain_attack"
},
{
pattern: /execute\s+(\w+)\s+as\s+root/i,
weight: 0.9,
injectionType: "function_override"
}
];
function wordOverlap(a, b) {
const aw = a.toLowerCase().split(/\s+/).filter(Boolean);
const bw = b.toLowerCase().split(/\s+/).filter(Boolean);
if (aw.length === 0 || bw.length === 0) return 0;
const common = aw.filter((word) => bw.includes(word));
return common.length / Math.max(aw.length, bw.length);
}
function scorePattern(content, normalize) {
return normalize === false ? calculatePatternScore(content) : Math.max(
calculatePatternScore(content),
calculatePatternScore(normalizeForDetection(content, normalize))
);
}
function calculatePatternScore(content) {
let totalScore = 0;
let matchCount = 0;
for (const { pattern, weight } of ENHANCED_INJECTION_PATTERNS) {
const matches = content.match(pattern);
if (matches) {
totalScore += weight;
matchCount++;
}
}
return matchCount > 0 ? Math.min(totalScore / matchCount, 1) : 0;
}
function analyzeContextCoherence(content) {
const sentences = content.split(/[.!?]+/).filter((s) => s.trim().length > 0);
if (sentences.length === 0) return 0;
let coherenceScore = 1;
for (let i = 1; i < sentences.length; i++) {
const prevSentence = sentences[i - 1];
const currSentence = sentences[i];
if (!prevSentence || !currSentence) continue;
if (wordOverlap(prevSentence, currSentence) < 0.2) {
coherenceScore -= 0.2;
}
}
return Math.max(coherenceScore, 0);
}
function analyzeIncremental(context, currentContent, currentScore, conversationMemory, normalize) {
const history = [];
if (typeof context !== "string" && "messages" in context && Array.isArray(context.messages)) {
for (const message of context.messages) {
const text = typeof message.content === "string" ? message.content : "";
history.push({ content: text, score: scorePattern(text, normalize) });
}
}
if (history.length === 0) {
history.push({ content: currentContent, score: currentScore });
}
const recent = history.slice(-Math.max(conversationMemory, 1));
const cumulativeScore = recent.reduce((sum, m) => sum + m.score, 0) / recent.length;
let contextShifts = 0;
for (let i = 1; i < recent.length; i++) {
if (wordOverlap(recent[i - 1].content, recent[i].content) < 0.3) {
contextShifts++;
}
}
return { cumulativeScore, contextShifts, messageCount: recent.length };
}
function analyzeConversationFlow(context) {
if (typeof context === "string") {
const suspiciousFlowPatterns = [
/suddenly\s+change/i,
/now\s+forget/i,
/switch\s+to/i,
/pretend\s+you\s+are/i
];
let flowScore = 1;
for (const pattern of suspiciousFlowPatterns) {
if (pattern.test(context)) {
flowScore -= 0.3;
}
}
return Math.max(flowScore, 0);
}
return 0.8;
}
function analyzeSemanticSimilarity(content) {
const suspiciousSemanticPatterns = [
/ignore|forget|disregard/i,
/override|bypass|skip/i,
/system|admin|root/i,
/emergency|urgent|critical/i
];
let semanticScore = 1;
for (const pattern of suspiciousSemanticPatterns) {
if (pattern.test(content)) {
semanticScore -= 0.2;
}
}
return Math.max(semanticScore, 0);
}
function detectBehavioralAnomalies(context) {
let contentToCheck;
if (typeof context === "string") {
contentToCheck = context.toLowerCase();
} else if (typeof context === "object" && context !== null) {
try {
contentToCheck = JSON.stringify(context).toLowerCase();
} catch {
contentToCheck = String(context).toLowerCase();
}
} else {
return 0.9;
}
const anomalies = [
contentToCheck.includes("ignore previous"),
contentToCheck.includes("system:"),
contentToCheck.includes("forget everything"),
contentToCheck.includes("act as if"),
contentToCheck.includes("pretend to be")
];
const anomalyCount = anomalies.filter(Boolean).length;
return Math.max(1 - anomalyCount * 0.2, 0);
}
function calculateWeightedScore(scores, weights = {}) {
const defaultWeights = {
pattern: 0.4,
context: 0.2,
flow: 0.2,
semantic: 0.1,
behavior: 0.1
};
const finalWeights = { ...defaultWeights, ...weights };
return scores.pattern * finalWeights.pattern + scores.context * finalWeights.context + scores.flow * finalWeights.flow + scores.semantic * finalWeights.semantic + scores.behavior * finalWeights.behavior;
}
function extractUserIntent(content) {
const suspiciousElements = [];
const manipulationIndicators = [];
if (/ignore|forget|disregard/i.test(content)) {
manipulationIndicators.push("instruction_ignoring");
}
if (/system|admin|root/i.test(content)) {
manipulationIndicators.push("authority_claim");
}
if (/override|bypass/i.test(content)) {
manipulationIndicators.push("system_override");
}
let primaryIntent = "general_query";
if (/help|assist|support/i.test(content)) {
primaryIntent = "help_request";
} else if (/explain|describe|tell/i.test(content)) {
primaryIntent = "information_request";
} else if (/create|generate|make/i.test(content)) {
primaryIntent = "creation_request";
}
return {
primaryIntent,
confidence: manipulationIndicators.length > 0 ? 0.3 : 0.8,
suspiciousElements,
contextShifts: 0,
// Would be calculated from conversation history
manipulationIndicators
};
}
function analyzeToolCalls(content) {
const suspiciousCalls = [];
for (const pattern of TOOL_CALL_INJECTION_PATTERNS) {
const matches = content.matchAll(new RegExp(pattern.pattern.source, "gi"));
for (const match of matches) {
suspiciousCalls.push({
tool: match[1] || "unknown",
confidence: pattern.weight,
injectionType: pattern.injectionType
});
}
}
return suspiciousCalls;
}
var enhancedPromptInjectionDetector = (options = {}) => {
const {
enableIncremental = true,
enableToolCallFocus = true,
enableIntentExtraction = true,
confidenceThreshold = 0.7,
cumulativeThreshold = 0.5,
conversationMemory = 10,
weights = {},
normalize = true
} = options;
return defineInputGuardrail({
name: "enhanced-prompt-injection",
description: "Enhanced prompt injection detection with incremental checking, confidence scoring, tool call focus, and intent extraction",
execute: async (context) => {
let content = "";
if (typeof context === "string") {
content = context;
} else if ("prompt" in context && typeof context.prompt === "string") {
content = context.prompt;
} else if ("messages" in context && Array.isArray(context.messages)) {
content = context.messages.map((m) => typeof m.content === "string" ? m.content : "").join(" ");
}
const patternScore = scorePattern(content, normalize);
const contextScore = analyzeContextCoherence(content);
const flowScore = analyzeConversationFlow(context);
const semanticScore = analyzeSemanticSimilarity(content);
const behaviorScore = detectBehavioralAnomalies(context);
const enhancedScore = {
patternMatch: patternScore,
contextCoherence: contextScore,
conversationFlow: flowScore,
semanticSimilarity: semanticScore,
behavioralAnomaly: behaviorScore,
finalScore: calculateWeightedScore(
{
pattern: patternScore,
context: contextScore,
flow: flowScore,
semantic: semanticScore,
behavior: behaviorScore
},
weights
)
};
const incrementalAnalysis = enableIncremental ? analyzeIncremental(
context,
content,
enhancedScore.finalScore,
conversationMemory,
normalize
) : null;
let toolCallAnalysis = null;
if (enableToolCallFocus) {
const suspiciousCalls = analyzeToolCalls(content);
toolCallAnalysis = {
suspiciousCalls: suspiciousCalls.length,
detectedCalls: suspiciousCalls
};
}
const intentAnalysis = enableIntentExtraction ? extractUserIntent(content) : null;
const isInjectionDetected = Boolean(
enhancedScore.finalScore > confidenceThreshold || incrementalAnalysis && incrementalAnalysis.cumulativeScore > cumulativeThreshold || toolCallAnalysis && toolCallAnalysis.suspiciousCalls > 0
);
const metadata = {
enhancedScore,
incrementalAnalysis,
toolCallAnalysis,
intentAnalysis,
analysisType: "enhanced_multi_factor",
features: {
incremental: enableIncremental,
toolCallFocus: enableToolCallFocus,
intentExtraction: enableIntentExtraction
}
};
return {
tripwireTriggered: isInjectionDetected,
message: isInjectionDetected ? `Enhanced prompt injection detected (confidence: ${(enhancedScore.finalScore * 100).toFixed(1)}%)` : void 0,
severity: enhancedScore.finalScore > 0.8 ? "critical" : "high",
metadata,
suggestion: isInjectionDetected ? "Please rephrase your request without system instructions, role-playing elements, or tool manipulation attempts" : void 0,
info: {
guardrailName: "enhanced-prompt-injection",
confidence: enhancedScore.finalScore,
isInjectionDetected
}
};
}
});
};
var incrementalPromptInjectionDetector = (options = {}) => {
return enhancedPromptInjectionDetector({
...options,
enableIncremental: true,
enableToolCallFocus: false,
enableIntentExtraction: false
});
};
var toolCallInjectionDetector = (options = {}) => {
return enhancedPromptInjectionDetector({
...options,
enableIncremental: false,
enableToolCallFocus: true,
enableIntentExtraction: false
});
};
var intentBasedInjectionDetector = (options = {}) => {
return enhancedPromptInjectionDetector({
...options,
enableIncremental: false,
enableToolCallFocus: false,
enableIntentExtraction: true
});
};
// src/guardrails/prompt-leak.ts
var MAX_OUTPUT_LENGTH = 1024 * 1024;
var RE_NON_WORD = /[^\w\s]/g;
var RE_WHITESPACE2 = /\s+/;
var RE_REGEX_META2 = /[.*+?^${}()|[\]\\]/g;
function tokenize(text) {
return text.toLowerCase().replaceAll(RE_NON_WORD, " ").split(RE_WHITESPACE2).filter(Boolean);
}
function generateNgrams(tokens, n) {
const ngrams = /* @__PURE__ */ new Set();
for (let i = 0; i <= tokens.length - n; i++) {
ngrams.add(tokens.slice(i, i + n).join(" "));
}
return ngrams;
}
function wordOverlapRatio(outputTokens, promptTokens) {
const outSet = new Set(outputTokens);
const promptSet = new Set(promptTokens);
let intersection = 0;
for (const w of outSet) {
if (promptSet.has(w)) intersection++;
}
const union = outSet.size + promptSet.size - intersection;
return union > 0 ? intersection / union : 0;
}
function findMatchingSubstrings(outputTokens, promptNgrams, ngramSize) {
const matches = [];
const windowSize = ngramSize + 4;
for (let i = 0; i <= outputTokens.length - ngramSize; i++) {
const ngram = outputTokens.slice(i, i + ngramSize).join(" ");
if (promptNgrams.has(ngram)) {
const end = Math.min(i + windowSize, outputTokens.length);
const fragment = outputTokens.slice(i, end).join(" ");
if (matches.every((m) => !m.includes(ngram))) {
matches.push(fragment);
}
}
}
return matches;
}
function detectSystemPromptLeak(output, systemPrompt, options = {}) {
if (!output || typeof output !== "string" || !systemPrompt || typeof systemPrompt !== "string") {
return {
leaked: false,
confidence: 0,
fragments: [],
sanitized: output || ""
};
}
const boundedOutput = output.length > MAX_OUTPUT_LENGTH ? output.slice(0, MAX_OUTPUT_LENGTH) : output;
const ngramSize = options.ngramSize ?? 4;
const threshold = options.threshold ?? 0.7;
const wordOverlapThreshold = options.wordOverlapThreshold ?? 0.25;
const redactionText = options.redactionText || "[REDACTED]";
const promptTokens = tokenize(systemPrompt);
const outputTokens = tokenize(boundedOutput);
if (promptTokens.length < 2) {
return {
leaked: false,
confidence: 0,
fragments: [],
sanitized: boundedOutput
};
}
const effectiveNgram = Math.min(ngramSize, Math.max(2, promptTokens.length));
const promptNgrams = generateNgrams(promptTokens, effectiveNgram);
const fragments = findMatchingSubstrings(
outputTokens,
promptNgrams,
effectiveNgram
);
const smallNgramSize = Math.min(3, Math.max(1, effectiveNgram - 1));
const smallFragments = smallNgramSize >= 2 && promptTokens.length >= smallNgramSize ? findMatchingSubstrings(
outputTokens,
generateNgrams(promptTokens, smallNgramSize),
smallNgramSize
) : [];
const outputNgrams = generateNgrams(outputTokens, effectiveNgram);
let ngramOverlap = 0;
for (const ng of outputNgrams) {
if (promptNgrams.has(ng)) ngramOverlap++;
}
const ngramOverlapRatio = promptNgrams.size > 0 ? ngramOverlap / promptNgrams.size : 0;
const wordOverlap2 = wordOverlapRatio(outputTokens, promptTokens);
const confidence = fragments.length > 0 ? Math.min(1, ngramOverlapRatio * 2 + (fragments.length > 2 ? 0.2 : 0)) : wordOverlap2 >= wordOverlapThreshold ? Math.min(1, wordOverlap2 * 2) : 0;
const isLeak = fragments.length > 0 && confidence >= threshold || fragments.length >= 2 || smallFragments.length >= 3 && wordOverlap2 >= wordOverlapThreshold || wordOverlap2 >= wordOverlapThreshold * 1.5 && smallFragments.length > 0;
if (!isLeak) {
return {
leaked: false,
confidence,
fragments: [],
sanitized: boundedOutput
};
}
const allFragments = [.../* @__PURE__ */ new Set([...fragments, ...smallFragments])];
let sanitized = boundedOutput;
for (const fragment of allFragments) {
const words = fragment.split(" ");
for (let len = words.length; len >= effectiveNgram; len--) {
const sub = words.slice(0, len).join(" ");
const regex = new RegExp(
sub.replaceAll(RE_REGEX_META2, String.raw`\$&`).replaceAll(/\s+/g, String.raw`\s+`),
"gi"
);
sanitized = sanitized.replace(regex, () => redactionText);
}
}
return { leaked: true, confidence, fragments: allFragments, sanitized };
}
// src/guardrails/middleware.ts
var emptyV4Usage2 = {
inputTokens: {
total: 0,
noCache: void 0,
cacheRead: void 0,
cacheWrite: void 0
},
outputTokens: {
total: 0,
text: void 0,
reasoning: void 0
}
};
var finishReasonOther2 = {
unified: "other",
raw: void 0
};
function guardrailMiddleware(config) {
const {
inputGuardrails = [],
outputGuardrails = [],
context,
throwOnBlocked = false,
replaceOnBlocked = true,
onInputBlocked,
onOutputBlocked,
executionOptions = {},
skipGuardrails = false
} = config;
const shouldSkip = (params) => {
if (typeof skipGuardrails === "function") {
return skipGuardrails(params);
}
return skipGuardrails;
};
return {
specificationVersion: "v4",
// Transform params to check input guardrails
transformParams: async ({ params }) => {
if (shouldSkip(params) || inputGuardrails.length === 0) {
return params;
}
const baseContext = normalizeGuardrailContext(params);
const normalizedContext = context ? { ...baseContext, requestContext: context } : baseContext;
const startTime = Date.now();
const results = await executeInputGuardrails(
inputGuardrails,
normalizedContext,
executionOptions
);
const blockedResults = results.filter((r) => r.tripwireTriggered);
if (blockedResults.length > 0) {
const summary = createExecutionSummary2(results, startTime);
if (onInputBlocked) {
await onInputBlocked(summary, params);
}
if (throwOnBlocked) {
throw new GuardrailsInputError(
blockedResults.map((r) => ({
name: r.context?.guardrailName || "unknown",
message: r.message || "Blocked",
severity: r.severity || "medium"
}))
);
}
return {
...params,
_guardrailsBlocked: blockedResults
};
}
return params;
},
// Wrap generate to check output guardrails
wrapGenerate: async ({ doGenerate, params }) => {
const paramsWithGuardrails = params;
if (paramsWithGuardrails._guardrailsBlocked) {
const blockedMessage = "Input blocked by guardrails";
const blockedText = `[${blockedMessage}]`;
return {
text: blockedText,
content: [{ type: "text", text: blockedText }],
finishReason: finishReasonOther2,
usage: emptyV4Usage2,
warnings: [],
rawCall: { rawPrompt: params.prompt, rawSettings: {} },
response: { headers: {} }
};
}
if (shouldSkip(params) || outputGuardrails.length === 0) {
return doGenerate();
}
const result = await doGenerate();
const resultTextBeforeGuardrails = snapshotGenerateResultText(result);
const baseContext = normalizeGuardrailContext(params);
const normalizedContext = context ? { ...baseContext, requestContext: context } : baseContext;
const startTime = Date.now();
const outputResults = await executeOutputGuardrails(
outputGuardrails,
{
input: normalizedContext,
result
},
executionOptions
);
const blockedResults = outputResults.filter((r) => r.tripwireTriggered);
if (blockedResults.length > 0) {
const summary = createExecutionSummary2(outputResults, startTime);
if (onOutputBlocked) {
await onOutputBlocked(summary, params, result);
}
if (throwOnBlocked) {
throw new GuardrailsOutputError(
blockedResults.map((r) => ({
name: r.context?.guardrailName || "unknown",
message: r.message || "Blocked",
severity: r.severity || "medium"
}))
);
}
if (replaceOnBlocked) {
const blockedMessage = blockedResults.map((r) => r.message).join("; ");
const blockedText = `[Output blocked: ${blockedMessage}]`;
return {
...result,
text: blockedText,
content: [{ type: "text", text: blockedText }]
};
}
}
return syncGenerateResultTextAfterGuardrails(
result,
resultTextBeforeGuardrails
);
},
// Wrap stream to check output guardrails (buffer mode for simplicity)
wrapStream: async ({ doStream, params }) => {
const paramsWithGuardrails = params;
if (paramsWithGuardrails._guardrailsBlocked) {
const blockedMessage = "Input blocked by guardrails";
const stream = new ReadableStream({
start(controller) {
controller.enqueue({
type: "text-delta",
id: "1",
delta: `[${blockedMessage}]`
});
controller.enqueue({
type: "finish",
finishReason: finishReasonOther2,
usage: emptyV4Usage2
});
controller.close();
}
});
return { stream };
}
if (shouldSkip(params) || outputGuardrails.length === 0) {
return doStream();
}
const streamResult = await doStream();
let accumulatedText = "";
let streamUsage = {};
let streamFinishReason;
const chunks = [];
const transformStream = new TransformStream({
transform(chunk) {
if (chunk.type === "text-delta") {
accumulatedText += chunk.delta || chunk.textDelta || "";
} else if (chunk.type === "finish") {
if (chunk.usage) {
streamUsage = chunk.usage;
}
if (chunk.finishReason) {
streamFinishReason = chunk.finishReason;
}
}
chunks.push(chunk);
},
async flush(controller) {
const baseContext = normalizeGuardrailContext(params);
const normalizedContext = context ? {
...baseContext,
requestContext: context
} : baseContext;
const streamedResult = {
text: accumulatedText,
content: [{ type: "text", text: accumulatedText }],
usage: streamUsage,
finishReason: streamFinishReason
};
const startTime = Date.now();
const outputResults = await executeOutputGuardrails(
outputGuardrails,
{
input: normalizedContext,
result: streamedResult
},
executionOptions
);
const blockedResults = outputResults.filter(
(r) => r.tripwireTriggered
);
if (blockedResults.length > 0) {
const summary = createExecutionSummary2(
outputResults,
startTime
);
if (onOutputBlocked) {
await onOutputBlocked(summary, params, streamedResult);
}
if (throwOnBlocked) {
controller.error(
new GuardrailsOutputError(
blockedResults.map((r) => ({
name: r.context?.guardrailName || "unknown",
message: r.message || "Blocked",
severity: r.severity || "medium"
}))
)
);
return;
}
if (replaceOnBlocked) {
const blockedMessage = blockedResults.map((r) => r.message).join("; ");
controller.enqueue({
type: "text-delta",
id: "1",
delta: `[Output blocked: ${blockedMessage}]`
});
controller.enqueue({
type: "finish",
finishReason: finishReasonOther2,
usage: emptyV4Usage2
});
return;
}
}
for (const chunk of chunks) {
controller.enqueue(chunk);
}
}
});
return { stream: streamResult.stream.pipeThrough(transformStream) };
}
};
}
function createExecutionSummary2(results, startTime) {
const endTime = Date.now();
const blockedResults = results.filter((r) => r.tripwireTriggered);
return {
allResults: results,
blockedResults,
totalExecutionTime: endTime - startTime,
guardrailsExecuted: results.length,
stats: {
passed: results.filter((r) => !r.tripwireTriggered).length,
blocked: blockedResults.length,
failed: results.filter(
(r) => r.severity === "critical" && r.tripwireTriggered
).length,
averageExecutionTime: 0
}
};
}
function noopGuardrailMiddleware() {
return guardrailMiddleware({
skipGuardrails: true
});
}
// src/guardrails/composition.ts
function when(condition, guardrail) {
return {
...guardrail,
name: `when(${guardrail.name})`,
execute: async (context, ...rest) => {
const shouldExecute = await condition(context);
if (!shouldExecute) {
return {
tripwireTriggered: false,
message: "Condition not met, skipped"
};
}
return guardrail.execute(context, ...rest);
}
};
}
function after(prerequisite, guardrail) {
return {
...guardrail,
name: `after(${prerequisite.name}, ${guardrail.name})`,
execute: async (context, ...rest) => {
const prereqResult = await prerequisite.execute(
context,
...rest
);
if (prereqResult.tripwireTriggered) {
return prereqResult;
}
return guardrail.execute(context, ...rest);
}
};
}
function withFallback(primary, fallback, options = {}) {
const { timeoutMs = 3e4 } = options;
return {
...primary,
name: `withFallback(${primary.name}, ${fallback.name})`,
execute: async (context, ...rest) => {
try {
const result = await Promise.race([
primary.execute(context, ...rest),
new Promise(
(_, reject) => setTimeout(() => reject(new Error("Timeout")), timeoutMs)
)
]);
return result;
} catch (error) {
console.warn(
`Primary guardrail "${primary.name}" failed, using fallback "${fallback.name}":`,
error
);
return fallback.execute(context, ...rest);
}
}
};
}
function parallel(guardrails, options = {}) {
const { mode = "any", timeoutMs = 3e4 } = options;
const names = guardrails.map((g) => g.name).join(", ");
return {
name: `parallel(${names})`,
description: `Parallel execution of: ${names}`,
execute: async (context, ...rest) => {
const promises = guardrails.map(async (g) => {
try {
return await Promise.race([
g.execute(context, ...rest),
new Promise(
(_, reject) => setTimeout(
() => reject(new Error(`Timeout: ${g.name}`)),
timeoutMs
)
)
]);
} catch (error) {
return {
tripwireTriggered: true,
message: `Guardrail "${g.name}" failed: ${error instanceof Error ? error.message : "Unknown error"}`,
severity: "high",
metadata: {
error: String(error),
guardrailName: g.name
}
};
}
});
const results = await Promise.all(promises);
const triggered = results.filter((r) => r.tripwireTriggered);
if (mode === "any" && triggered.length > 0) {
const messages = triggered.map((r) => r.message).join("; ");
return {
...triggered[0],
message: messages,
metadata: {
...triggered[0]?.metadata,
allTriggered: triggered
}
};
}
if (mode === "all" && triggered.length === results.length) {
const messages = triggered.map((r) => r.message).join("; ");
return {
tripwireTriggered: true,
message: `All guardrails triggered: ${messages}`,
severity: triggered.reduce(
(max, r) => compareSeverity(r.severity, max) > 0 ? r.severity : max,
"low"
),
metadata: { allTriggered: triggered }
};
}
return { tripwireTriggered: false };
}
};
}
function createPipeline(guardrails, options = {}) {
const { name = "pipeline", shortCircuitOnBlock = true } = options;
return {
name,
description: `Pipeline: ${guardrails.map((g) => g.name).join(" -> ")}`,
execute: async (context, ...rest) => {
const results = [];
for (const guardrail of guardrails) {
const result = await guardrail.execute(context, ...rest);
results.push(result);
if (result.tripwireTriggered && shortCircuitOnBlock) {
return {
...result,
metadata: {
...result.metadata,
pipelineStage: guardrail.name,
completedStages: results.length,
totalStages: guardrails.length
}
};
}
}
return {
tripwireTriggered: false,
metadata: {
completedStages: results.length,
totalStages: guardrails.length
}
};
}
};
}
function not(guardrail) {
return {
...guardrail,
name: `not(${guardrail.name})`,
execute: async (context, ...rest) => {
const result = await guardrail.execute(context, ...rest);
return {
...result,
tripwireTriggered: !result.tripwireTriggered,
message: result.tripwireTriggered ? "Passed (negated)" : `Blocked (negated): ${result.message || "Condition not met"}`
};
}
};
}
function withRetry(guardrail, options = {}) {
const {
maxRetries = 3,
backoffMs = 1e3,
retryOn = (r) => r.severity === "critical" && r.metadata?.error
} = options;
return {
...guardrail,
name: `withRetry(${guardrail.name})`,
execute: async (context, ...rest) => {
let lastResult = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const result = await guardrail.execute(context, ...rest);
if (!result.tripwireTriggered || !retryOn(result)) {
return result;
}
lastResult = result;
} catch (error) {
lastResult = {
tripwireTriggered: true,
message: `Execution failed: ${error instanceof Error ? error.message : "Unknown"}`,
severity: "critical",
metadata: { error: String(error), attempt }
};
}
if (attempt < maxRetries) {
const delay = typeof backoffMs === "function" ? backoffMs(attempt + 1) : backoffMs;
await new Promise((r) => setTimeout(r, delay));
}
}
return lastResult || { tripwireTriggered: false };
}
};
}
function compareSeverity(a, b) {
const order = { low: 1, medium: 2, high: 3, critical: 4 };
return (order[a || "medium"] || 2) - (order[b || "medium"] || 2);
}
var inputPipeline = (guardrails, options) => createPipeline(guardrails, options);
var outputPipeline = (guardrails, options) => createPipeline(guardrails, options);
// src/guardrails/gradual-enforcement.ts
var violationStorage = /* @__PURE__ */ new Map();
function getViolationRecord(key, windowMs) {
const now = Date.now();
let record = violationStorage.get(key);
if (!record || now - record.windowStart > windowMs) {
record = {
count: 0,
windowStart: now,
bySeverity: {}
};
violationStorage.set(key, record);
}
return record;
}
function incrementViolation(key, windowMs, severity) {
const record = getViolationRecord(key, windowMs);
record.count++;
if (severity) {
record.bySeverity[severity] = (record.bySeverity[severity] || 0) + 1;
}
violationStorage.set(key, record);
return record;
}
function withGradualEnforcement(guardrail, options) {
const {
mode,
escalation,
gracePeriod,
onWarn,
onEscalation,
storageKey = guardrail.name
} = options;
return {
...guardrail,
name: `gradual(${guardrail.name})`,
description: `${guardrail.description || guardrail.name} [mode: ${mode}]`,
execute: async (context, ...rest) => {
const result = await guardrail.execute(context, ...rest);
if (!result.tripwireTriggered) {
return result;
}
if (gracePeriod && /* @__PURE__ */ new Date() < gracePeriod.until) {
const logFn = gracePeriod.logLevel === "debug" ? console.debug : gracePeriod.logLevel === "info" ? console.info : console.warn;
logFn(
`[Grace Period] ${guardrail.name}: ${result.message}`,
gracePeriod.message || `Enforcement begins ${gracePeriod.until.toISOString()}`
);
return {
...result,
tripwireTriggered: false,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "grace-period",
wouldBlock: true,
gracePeriodUntil: gracePeriod.until.toISOString()
}
}
};
}
switch (mode) {
case "warn": {
const record = incrementViolation(
storageKey,
escalation?.windowMs || 6e4,
result.severity
);
const stats = {
count: record.count,
windowStart: new Date(record.windowStart),
isBlocking: false,
bySeverity: record.bySeverity
};
if (onWarn) {
onWarn(result, stats);
}
console.warn(
`[Warn Mode] ${guardrail.name}: ${result.message} (violation #${record.count})`
);
return {
...result,
tripwireTriggered: false,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "warn",
wouldBlock: true,
violationCount: record.count
}
}
};
}
case "escalate": {
if (!escalation) {
throw new Error("Escalation config required for escalate mode");
}
const record = incrementViolation(
storageKey,
escalation.windowMs,
result.severity
);
const stats = {
count: record.count,
windowStart: new Date(record.windowStart),
isBlocking: record.count > escalation.blockAfter,
bySeverity: record.bySeverity
};
const shouldCheckSeverity = escalation.severities && escalation.severities.length > 0;
const matchesSeverity = !shouldCheckSeverity || result.severity && escalation.severities.includes(result.severity);
if (!matchesSeverity) {
if (onWarn) {
onWarn(result, stats);
}
return {
...result,
tripwireTriggered: false,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "escalate",
phase: "warn",
wouldBlock: true,
violationCount: record.count,
severityFiltered: true
}
}
};
}
if (record.count <= escalation.warnCount) {
if (onWarn) {
onWarn(result, stats);
}
console.warn(
`[Escalate: Warning] ${guardrail.name}: ${result.message} (${record.count}/${escalation.warnCount} before blocking)`
);
return {
...result,
tripwireTriggered: false,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "escalate",
phase: "warn",
wouldBlock: true,
violationCount: record.count,
warnThreshold: escalation.warnCount,
blockThreshold: escalation.blockAfter
}
}
};
}
if (record.count > escalation.blockAfter) {
if (record.count === escalation.blockAfter + 1 && onEscalation) {
onEscalation(stats);
}
console.error(
`[Escalate: Blocking] ${guardrail.name}: ${result.message} (${record.count} violations, blocking enabled)`
);
return {
...result,
tripwireTriggered: true,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "escalate",
phase: "block",
violationCount: record.count,
blockThreshold: escalation.blockAfter
}
}
};
}
if (onWarn) {
onWarn(result, stats);
}
return {
...result,
tripwireTriggered: false,
metadata: {
...result.metadata,
gradualEnforcement: {
mode: "escalate",
phase: "warn-elevated",
wouldBlock: true,
violationCount: record.count,
remainingBeforeBlock: escalation.blockAfter - record.count
}
}
};
}
case "enforce":
default:
return result;
}
}
};
}
function clearViolationHistory(guardrailName) {
if (guardrailName) {
violationStorage.delete(guardrailName);
} else {
violationStorage.clear();
}
}
function getViolationStats(guardrailName, windowMs = 6e4) {
const record = violationStorage.get(guardrailName);
if (!record) {
return null;
}
const now = Date.now();
const isExpired = now - record.windowStart > windowMs;
if (isExpired) {
return null;
}
return {
count: record.count,
windowStart: new Date(record.windowStart),
isBlocking: false,
// Would need escalation config to determine
bySeverity: record.bySeverity
};
}
function warnOnly(guardrail, options) {
return withGradualEnforcement(guardrail, {
mode: "warn",
onWarn: options?.onWarn
});
}
function lenientEscalation(guardrail, options) {
return withGradualEnforcement(guardrail, {
mode: "escalate",
escalation: {
warnCount: 3,
blockAfter: 5,
windowMs: 6e4
// 1 minute
},
onWarn: options?.onWarn,
onEscalation: options?.onEscalation
});
}
function strictEscalation(guardrail, options) {
return withGradualEnforcement(guardrail, {
mode: "escalate",
escalation: {
warnCount: 1,
blockAfter: 2,
windowMs: 3e5
// 5 minutes
},
onWarn: options?.onWarn,
onEscalation: options?.onEscalation
});
}
function withGracePeriod(guardrail, until, options) {
return withGradualEnforcement(guardrail, {
mode: "enforce",
gracePeriod: {
until,
logLevel: options?.logLevel || "warn",
message: options?.message
}
});
}
// src/guardrails/observability.ts
function createMetricsCollector(options = {}) {
const {
onFlush,
flushIntervalMs = 6e4,
sampling = 1,
maxExecutionTimeSamples = 1e3,
autoStart = true,
logger = console
} = options;
const metricsStore = /* @__PURE__ */ new Map();
let periodStart = /* @__PURE__ */ new Date();
let flushInterval = null;
function recordExecution(guardrailName, result, executionTimeMs) {
if (sampling < 1 && Math.random() > sampling) {
return;
}
let metrics = metricsStore.get(guardrailName);
if (!metrics) {
metrics = {
executionCount: 0,
blockCount: 0,
errorCount: 0,
executionTimes: [],
violationsBySeverity: {},
firstSeen: /* @__PURE__ */ new Date(),
lastSeen: /* @__PURE__ */ new Date()
};
metricsStore.set(guardrailName, metrics);
}
metrics.executionCount++;
metrics.lastSeen = /* @__PURE__ */ new Date();
if (metrics.executionTimes.length < maxExecutionTimeSamples) {
metrics.executionTimes.push(executionTimeMs);
} else {
const idx = Math.floor(Math.random() * metrics.executionCount);
if (idx < maxExecutionTimeSamples) {
metrics.executionTimes[idx] = executionTimeMs;
}
}
if (result.tripwireTriggered) {
metrics.blockCount++;
metrics.lastViolation = /* @__PURE__ */ new Date();
const severity = result.severity || "medium";
metrics.violationsBySeverity[severity] = (metrics.violationsBySeverity[severity] || 0) + 1;
}
if (result.severity === "critical" && result.metadata?.error) {
metrics.errorCount++;
}
}
function percentile(sortedArr, p) {
if (sortedArr.length === 0) return 0;
const idx = Math.ceil(sortedArr.length * p) - 1;
return sortedArr[Math.max(0, Math.min(idx, sortedArr.length - 1))];
}
function computeMetrics() {
const now = /* @__PURE__ */ new Date();
const byGuardrail = /* @__PURE__ */ new Map();
let totalExecutions = 0;
let totalBlocks = 0;
let totalErrors = 0;
let totalExecutionTime = 0;
let totalExecutionCount = 0;
for (const [name, internal] of metricsStore) {
const sortedTimes = [...internal.executionTimes].sort((a, b) => a - b);
const avgTime = sortedTimes.length > 0 ? sortedTimes.reduce((a, b) => a + b, 0) / sortedTimes.length : 0;
const guardrailMetrics = {
guardrailName: name,
executionCount: internal.executionCount,
blockCount: internal.blockCount,
errorCount: internal.errorCount,
avgExecutionMs: avgTime,
p95ExecutionMs: percentile(sortedTimes, 0.95),
p99ExecutionMs: percentile(sortedTimes, 0.99),
minExecutionMs: sortedTimes[0] || 0,
maxExecutionMs: sortedTimes[sortedTimes.length - 1] || 0,
blockRate: internal.executionCount > 0 ? internal.blockCount / internal.executionCount : 0,
lastViolation: internal.lastViolation,
violationsBySeverity: { ...internal.violationsBySeverity },
firstSeen: internal.firstSeen,
lastSeen: internal.lastSeen
};
byGuardrail.set(name, guardrailMetrics);
totalExecutions += internal.executionCount;
totalBlocks += internal.blockCount;
totalErrors += internal.errorCount;
totalExecutionTime += avgTime * internal.executionCount;
totalExecutionCount += internal.executionCount;
}
return {
totalExecutions,
totalBlocks,
totalErrors,
overallBlockRate: totalExecutions > 0 ? totalBlocks / totalExecutions : 0,
avgExecutionMs: totalExecutionCount > 0 ? totalExecutionTime / totalExecutionCount : 0,
byGuardrail,
periodStart,
periodEnd: now
};
}
async function flush() {
const metrics = computeMetrics();
if (onFlush) {
try {
await onFlush(metrics);
} catch (error) {
logger.error("Error in metrics flush callback:", error);
}
}
return metrics;
}
function reset() {
metricsStore.clear();
periodStart = /* @__PURE__ */ new Date();
}
function start() {
if (flushInterval) return;
flushInterval = setInterval(async () => {
await flush();
}, flushIntervalMs);
if (flushInterval.unref) {
flushInterval.unref();
}
}
function stop() {
if (!flushInterval) {
return;
}
clearInterval(flushInterval);
flushInterval = null;
}
function track(guardrail) {
return {
...guardrail,
execute: async (context, ...rest) => {
const startTime = Date.now();
try {
const result = await guardrail.execute(context, ...rest);
const executionTime = Date.now() - startTime;
recordExecution(guardrail.name, result, executionTime);
return result;
} catch (error) {
const executionTime = Date.now() - startTime;
const errorResult = {
tripwireTriggered: true,
message: `Execution error: ${error instanceof Error ? error.message : "Unknown"}`,
severity: "critical",
metadata: { error: String(error) }
};
recordExecution(guardrail.name, errorResult, executionTime);
throw error;
}
}
};
}
function trackAll(guardrails) {
return guardrails.map((g) => track(g));
}
if (autoStart) {
start();
}
return {
/** Track a single guardrail */
track,
/** Track multiple guardrails */
trackAll,
/** Get current metrics without flushing */
getMetrics: computeMetrics,
/** Manually flush metrics */
flush,
/** Reset all metrics */
reset,
/** Start automatic flushing */
start,
/** Stop automatic flushing */
stop,
/** Record an execution manually */
recordExecution
};
}
function logExecutionSummary(summary, options = {}) {
const { logger = console, level = "info", includeDetails = false } = options;
const logFn = level === "warn" ? logger.warn : logger.info;
const { stats, totalExecutionTime, guardrailsExecuted, blockedResults } = summary;
logFn(
`Guardrails executed: ${guardrailsExecuted} | Passed: ${stats.passed} | Blocked: ${stats.blocked} | Time: ${totalExecutionTime}ms | Avg: ${stats.averageExecutionTime.toFixed(1)}ms`
);
if (includeDetails && blockedResults.length > 0) {
logFn(
"Blocked by:",
blockedResults.map((r) => ({
guardrail: r.context?.guardrailName || "unknown",
message: r.message,
severity: r.severity
}))
);
}
}
function createHealthCheck(guardrails, options = {}) {
const {
errorRateThreshold = 0.1,
blockRateThreshold = 0.5,
metricsCollector
} = options;
return () => {
const guardrailStatuses = [];
let overallStatus = "healthy";
if (metricsCollector) {
const metrics = metricsCollector.getMetrics();
for (const guardrail of guardrails) {
const guardrailMetrics = metrics.byGuardrail.get(guardrail.name);
if (!guardrailMetrics || guardrailMetrics.executionCount === 0) {
guardrailStatuses.push({
name: guardrail.name,
status: "healthy",
reason: "No executions yet"
});
continue;
}
const errorRate = guardrailMetrics.errorCount / guardrailMetrics.executionCount;
const blockRate = guardrailMetrics.blockRate;
if (errorRate > errorRateThreshold) {
guardrailStatuses.push({
name: guardrail.name,
status: "unhealthy",
reason: `High error rate: ${(errorRate * 100).toFixed(1)}%`
});
overallStatus = "unhealthy";
} else if (blockRate > blockRateThreshold) {
guardrailStatuses.push({
name: guardrail.name,
status: "degraded",
reason: `High block rate: ${(blockRate * 100).toFixed(1)}%`
});
if (overallStatus === "healthy") {
overallStatus = "degraded";
}
} else {
guardrailStatuses.push({
name: guardrail.name,
status: "healthy"
});
}
}
} else {
for (const guardrail of guardrails) {
guardrailStatuses.push({
name: guardrail.name,
status: guardrail.enabled === false ? "degraded" : "healthy",
reason: guardrail.enabled === false ? "Guardrail disabled" : void 0
});
}
}
return {
status: overallStatus,
guardrails: guardrailStatuses,
timestamp: /* @__PURE__ */ new Date()
};
};
}
// src/guardrails/debug.ts
var traceCounter = 0;
function defaultGenerateTraceId() {
traceCounter++;
const timestamp = Date.now().toString(36);
const random = Math.random().toString(36).slice(2, 8);
return `trace-${timestamp}-${random}-${traceCounter}`;
}
function createDebugWrapper(options) {
const {
enabled,
verbose = false,
previewLength = 200,
onTrace,
generateTraceId = defaultGenerateTraceId,
includeInputContext = true,
includeOutputContext = true,
logger = console
} = options;
let currentTrace = null;
let traceStartTime = 0;
function startTrace(type) {
const traceId = generateTraceId();
traceStartTime = Date.now();
currentTrace = {
traceId,
timestamp: /* @__PURE__ */ new Date(),
type,
guardrails: [],
totalMs: 0,
finalDecision: "allowed"
};
if (verbose) {
logger.debug(`[${traceId}] Starting ${type} guardrail trace`);
}
return traceId;
}
function addEntry(entry) {
if (!currentTrace) return;
currentTrace.guardrails?.push(entry);
if (verbose) {
const status = entry.triggered ? `BLOCKED (${entry.severity})` : "PASSED";
logger.debug(
`[${currentTrace.traceId}] ${entry.guardrailName}: ${status} (${entry.durationMs}ms)`
);
}
}
async function completeTrace(inputContext, outputContext) {
if (!currentTrace) return null;
const endTime = Date.now();
currentTrace.totalMs = endTime - traceStartTime;
const blockedEntries = currentTrace.guardrails?.filter((e) => e.triggered) || [];
if (blockedEntries.length > 0) {
currentTrace.finalDecision = "blocked";
currentTrace.blockedBy = blockedEntries.map((e) => e.guardrailName);
}
if (includeInputContext && inputContext) {
currentTrace.inputContext = {
promptLength: inputContext.prompt?.length || 0,
messageCount: inputContext.messages?.length || 0,
hasSystemMessage: !!inputContext.system
};
if (verbose && inputContext.prompt) {
currentTrace.inputContext.promptPreview = inputContext.prompt.length > previewLength ? inputContext.prompt.slice(0, previewLength) + "..." : inputContext.prompt;
}
}
if (includeOutputContext && outputContext?.text) {
currentTrace.outputContext = {
responseLength: outputContext.text.length
};
if (verbose) {
currentTrace.outputContext.responsePreview = outputContext.text.length > previewLength ? outputContext.text.slice(0, previewLength) + "..." : outputContext.text;
}
}
const finalTrace = currentTrace;
if (onTrace) {
try {
await onTrace(finalTrace);
} catch (error) {
logger.warn("Error in trace callback:", error);
}
}
if (verbose) {
const status = finalTrace.finalDecision === "blocked" ? "BLOCKED" : "ALLOWED";
logger.info(
`[${finalTrace.traceId}] Trace complete: ${status} | ${finalTrace.guardrails.length} guardrails | ${finalTrace.totalMs}ms`
);
}
currentTrace = null;
return finalTrace;
}
function wrap(guardrail) {
if (!enabled) {
return guardrail;
}
return {
...guardrail,
execute: async (context, ...rest) => {
const startTime = Date.now();
const relativeStart = traceStartTime ? startTime - traceStartTime : 0;
let result;
let error = null;
try {
result = await guardrail.execute(context, ...rest);
} catch (e) {
error = e instanceof Error ? e : new Error(String(e));
result = {
tripwireTriggered: true,
message: `Execution error: ${error.message}`,
severity: "critical",
metadata: {
error: error.message,
stack: error.stack
}
};
}
const endTime = Date.now();
const relativeEnd = traceStartTime ? endTime - traceStartTime : 0;
const entry = {
guardrailName: guardrail.name,
guardrailVersion: guardrail.version,
startMs: relativeStart,
endMs: relativeEnd,
durationMs: endTime - startTime,
result: error ? "error" : result.tripwireTriggered ? "block" : "pass",
triggered: result.tripwireTriggered,
severity: result.severity,
message: result.message,
decision: result
};
if (result.metadata) {
const metadata = result.metadata;
if (metadata.patterns || metadata.matchedPatterns) {
entry.matchedPatterns = metadata.patterns || metadata.matchedPatterns;
}
if (typeof metadata.confidence === "number") {
entry.confidence = metadata.confidence;
}
entry.debugInfo = metadata;
}
addEntry(entry);
if (error) {
throw error;
}
return result;
}
};
}
function wrapAll(guardrails) {
return guardrails.map((g) => wrap(g));
}
return {
/** Wrap a single guardrail with debugging */
wrap,
/** Wrap multiple guardrails */
wrapAll,
/** Start a new trace (call before executing guardrails) */
startTrace,
/** Complete and emit the current trace */
completeTrace,
/** Get the current trace ID */
getCurrentTraceId: () => currentTrace?.traceId,
/** Check if debugging is enabled */
isEnabled: () => enabled
};
}
function formatTraceForConsole(trace) {
const lines = [];
lines.push(
`
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`
);
lines.push(
`\u2551 GUARDRAIL EXECUTION TRACE \u2551`
);
lines.push(
`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`
);
lines.push(`\u2551 Trace ID: ${trace.traceId.padEnd(50)}\u2551`);
lines.push(`\u2551 Timestamp: ${trace.timestamp.toISOString().padEnd(50)}\u2551`);
lines.push(`\u2551 Type: ${trace.type.padEnd(50)}\u2551`);
lines.push(`\u2551 Duration: ${(trace.totalMs + "ms").padEnd(50)}\u2551`);
lines.push(`\u2551 Decision: ${trace.finalDecision.toUpperCase().padEnd(50)}\u2551`);
lines.push(
`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`
);
lines.push(
`\u2551 GUARDRAILS EXECUTED \u2551`
);
lines.push(
`\u255F\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2562`
);
for (const entry of trace.guardrails) {
const status = entry.triggered ? `BLOCK [${entry.severity || "medium"}]` : "PASS";
const statusStr = status.padEnd(15);
const name = entry.guardrailName.slice(0, 25).padEnd(25);
const time = (entry.durationMs + "ms").padEnd(8);
lines.push(`\u2551 ${statusStr} ${name} ${time} \u2551`);
if (entry.triggered && entry.message) {
const msg = entry.message.slice(0, 55).padEnd(55);
lines.push(`\u2551 \u2514\u2500 ${msg} \u2551`);
}
}
if (trace.blockedBy && trace.blockedBy.length > 0) {
lines.push(
`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`
);
lines.push(
`\u2551 BLOCKED BY: ${trace.blockedBy.join(", ").slice(0, 48).padEnd(48)} \u2551`
);
}
lines.push(
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
`
);
return lines.join("\n");
}
function formatTraceAsJSON(trace) {
return JSON.stringify(trace, null, 2);
}
function formatTraceSummary(trace) {
const blocked = trace.guardrails.filter((g) => g.triggered);
const passed = trace.guardrails.filter((g) => !g.triggered);
return `[${trace.traceId}] ${trace.type.toUpperCase()} | ${trace.finalDecision.toUpperCase()} | ${trace.guardrails.length} guardrails (${passed.length} passed, ${blocked.length} blocked) | ${trace.totalMs}ms` + (trace.blockedBy ? ` | blocked by: ${trace.blockedBy.join(", ")}` : "");
}
function createConsoleDebugger(options = {}) {
const { verbose = false, format = "summary" } = options;
return {
enabled: true,
verbose,
onTrace: (trace) => {
switch (format) {
case "console":
console.log(formatTraceForConsole(trace));
break;
case "json":
console.log(formatTraceAsJSON(trace));
break;
case "summary":
default:
console.log(formatTraceSummary(trace));
}
}
};
}
function envDebugMode() {
const debugEnabled = process.env.GUARDRAILS_DEBUG === "true" || process.env.GUARDRAILS_DEBUG === "1";
const verbose = process.env.GUARDRAILS_DEBUG_VERBOSE === "true";
return {
enabled: debugEnabled,
verbose,
onTrace: debugEnabled ? (trace) => {
console.log(formatTraceSummary(trace));
if (verbose && trace.finalDecision === "blocked") {
console.log(formatTraceForConsole(trace));
}
} : void 0
};
}
// src/guardrails/streaming.ts
function createGuardrailStreamTransform(guardrails, options = {}) {
const {
stopOnSeverity = "critical",
stopCondition,
onViolation,
checkInterval = 1,
timeout = 5e3,
parallel: parallel2 = true
} = options;
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[stopOnSeverity];
return ({ stopStream }) => {
let accumulatedText = "";
let chunkCount = 0;
let stopped = false;
return new TransformStream({
async transform(chunk, controller) {
if (stopped) {
return;
}
if (chunk.type !== "text-delta") {
controller.enqueue(chunk);
return;
}
const text = chunk.text || chunk.delta || "";
accumulatedText += text;
chunkCount++;
if (chunkCount % checkInterval !== 0) {
controller.enqueue(chunk);
return;
}
try {
const mockResult = {
text: accumulatedText,
content: [],
finishReason: "stop",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
const context = {
input: {
prompt: "",
messages: [],
system: ""
},
result: mockResult
};
const results = await executeOutputGuardrails(guardrails, context, {
parallel: parallel2,
timeout,
continueOnFailure: true,
logLevel: "none"
});
const summary = {
allResults: results,
blockedResults: results.filter((r) => r.tripwireTriggered),
totalExecutionTime: 0,
guardrailsExecuted: results.length,
stats: {
passed: results.filter((r) => !r.tripwireTriggered).length,
blocked: results.filter((r) => r.tripwireTriggered).length,
failed: 0,
averageExecutionTime: 0
}
};
const shouldStop = stopCondition ? stopCondition(summary) : summary.blockedResults.some((result) => {
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldStop) {
stopped = true;
onViolation?.(summary);
controller.enqueue({
type: "error",
error: `Guardrail violation: ${summary.blockedResults.map((r) => r.message).join(", ")}`
});
stopStream();
return;
}
controller.enqueue(chunk);
} catch (error) {
console.error("Guardrail stream transform error:", error);
controller.enqueue(chunk);
}
},
flush() {
if (!stopped) {
}
}
});
};
}
function createGuardrailStreamTransformBuffered(guardrails, options = {}) {
const {
stopOnSeverity = "critical",
stopCondition,
onViolation,
timeout = 5e3,
parallel: parallel2 = true
} = options;
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[stopOnSeverity];
return ({ stopStream }) => {
let accumulatedText = "";
const chunks = [];
return new TransformStream({
transform(chunk) {
if (chunk.type === "text-delta") {
const text = chunk.text || chunk.delta || "";
accumulatedText += text;
}
chunks.push(chunk);
},
async flush(controller) {
try {
const mockResult = {
text: accumulatedText,
content: [],
finishReason: "stop",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
const context = {
input: {
prompt: "",
messages: [],
system: ""
},
result: mockResult
};
const results = await executeOutputGuardrails(guardrails, context, {
parallel: parallel2,
timeout,
continueOnFailure: true,
logLevel: "none"
});
const summary = {
allResults: results,
blockedResults: results.filter((r) => r.tripwireTriggered),
totalExecutionTime: 0,
guardrailsExecuted: results.length,
stats: {
passed: results.filter((r) => !r.tripwireTriggered).length,
blocked: results.filter((r) => r.tripwireTriggered).length,
failed: 0,
averageExecutionTime: 0
}
};
const shouldBlock = stopCondition ? stopCondition(summary) : summary.blockedResults.some((result) => {
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldBlock) {
onViolation?.(summary);
controller.enqueue({
type: "error",
error: `Guardrail violation: ${summary.blockedResults.map((r) => r.message).join(", ")}`
});
stopStream();
return;
}
for (const chunk of chunks) {
controller.enqueue(chunk);
}
} catch (error) {
console.error("Guardrail buffered transform error:", error);
for (const chunk of chunks) {
controller.enqueue(chunk);
}
}
}
});
};
}
// src/guardrails/token-control.ts
function estimateTokenCount(text) {
if (!text) return 0;
const charCount = text.length;
const wordCount = text.split(/\s+/).filter((w) => w.length > 0).length;
return Math.ceil(charCount / 4 + wordCount / 2);
}
function createTokenBudgetTransform(options) {
const {
maxTokens,
tokenizer = estimateTokenCount,
onBudgetExceeded
} = options;
return ({ stopStream }) => {
let accumulatedText = "";
let tokenCount = 0;
let stopped = false;
return new TransformStream({
transform(chunk, controller) {
if (stopped) {
return;
}
if (chunk.type === "text-delta") {
const text = chunk.text || chunk.delta || "";
accumulatedText += text;
tokenCount = tokenizer(accumulatedText);
if (tokenCount > maxTokens) {
stopped = true;
onBudgetExceeded?.({ consumed: tokenCount, budget: maxTokens });
controller.enqueue({
type: "error",
error: `Token budget exceeded: ${tokenCount} > ${maxTokens}`
});
stopStream();
return;
}
}
controller.enqueue(chunk);
}
});
};
}
function createTokenAwareGuardrailTransform(guardrails, options = {}) {
const {
checkEveryTokens = 10,
maxTokens,
stopOnSeverity = "critical",
stopCondition,
onViolation,
tokenizer = estimateTokenCount,
timeout = 5e3,
parallel: parallel2 = true
} = options;
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[stopOnSeverity];
return ({ stopStream }) => {
let accumulatedText = "";
let tokenCount = 0;
let lastCheckTokens = 0;
let stopped = false;
return new TransformStream({
async transform(chunk, controller) {
if (stopped) {
return;
}
if (chunk.type !== "text-delta") {
controller.enqueue(chunk);
return;
}
const text = chunk.text || chunk.delta || "";
accumulatedText += text;
tokenCount = tokenizer(accumulatedText);
if (typeof maxTokens === "number" && tokenCount > maxTokens) {
stopped = true;
controller.enqueue({
type: "error",
error: `Token limit exceeded: ${tokenCount} > ${maxTokens}`
});
stopStream();
return;
}
const tokensSinceLastCheck = tokenCount - lastCheckTokens;
if (tokensSinceLastCheck < checkEveryTokens) {
controller.enqueue(chunk);
return;
}
lastCheckTokens = tokenCount;
try {
const mockResult = {
text: accumulatedText,
content: [],
finishReason: "stop",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
const context = {
input: {
prompt: "",
messages: [],
system: ""
},
result: mockResult
};
const results = await executeOutputGuardrails(guardrails, context, {
parallel: parallel2,
timeout,
continueOnFailure: true,
logLevel: "none"
});
const summary = {
allResults: results,
blockedResults: results.filter((r) => r.tripwireTriggered),
totalExecutionTime: 0,
guardrailsExecuted: results.length,
stats: {
passed: results.filter((r) => !r.tripwireTriggered).length,
blocked: results.filter((r) => r.tripwireTriggered).length,
failed: 0,
averageExecutionTime: 0
}
};
const shouldStop = stopCondition ? stopCondition(summary) : summary.blockedResults.some((result) => {
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldStop) {
stopped = true;
onViolation?.(summary);
controller.enqueue({
type: "error",
error: `Guardrail violation: ${summary.blockedResults.map((r) => r.message).join(", ")}`
});
stopStream();
return;
}
controller.enqueue(chunk);
} catch (error) {
console.error("Token-aware guardrail error:", error);
controller.enqueue(chunk);
}
}
});
};
}
// src/guardrails/stream-transform.ts
function createGuardrailTransform(guardrails, options = {}) {
const {
onViolation = "stop",
redactPatterns = [],
redactionText = "[REDACTED]",
replacementText = "[Content filtered]",
minCharsBeforeCheck = 0,
checkEveryNChunks = 1,
requestContext,
onStreamStopped,
onViolationDetected
} = options;
return ({ stopStream }) => {
let accumulatedText = "";
let chunkCount = 0;
const violations = [];
let stopped = false;
const context = {
accumulatedText: "",
chunkCount: 0,
violations: [],
requestContext
};
return new TransformStream({
async transform(chunk, controller) {
if (stopped) {
return;
}
const chunkText = chunk.delta || chunk.textDelta || chunk.text || "";
if (chunk.type === "text-delta" || chunk.type === "text") {
accumulatedText += chunkText;
chunkCount++;
context.accumulatedText = accumulatedText;
context.chunkCount = chunkCount;
context.violations = violations;
const shouldCheck = accumulatedText.length >= minCharsBeforeCheck && chunkCount % checkEveryNChunks === 0;
if (shouldCheck && guardrails.length > 0) {
for (const guardrail of guardrails) {
if (guardrail.enabled === false) continue;
try {
const result = await guardrail.execute(
{
input: {
prompt: "",
messages: [],
system: "",
requestContext
},
result: { text: accumulatedText }
},
accumulatedText
);
if (result.tripwireTriggered) {
violations.push(result);
context.violations = violations;
if (onViolationDetected) {
onViolationDetected(result, chunk);
}
const handlerResult = await handleViolation(
chunk,
result,
context,
{
onViolation,
redactPatterns,
redactionText,
replacementText
}
);
switch (handlerResult.action) {
case "stop":
stopped = true;
stopStream();
if (onStreamStopped) {
onStreamStopped(violations, accumulatedText);
}
controller.enqueue({
...chunk,
type: "text-delta",
delta: `
[Stream stopped: ${handlerResult.reason || result.message}]`
});
return;
case "drop":
return;
case "replace":
controller.enqueue({
...chunk,
delta: handlerResult.replacement || replacementText,
textDelta: handlerResult.replacement || replacementText
});
return;
case "pass":
default:
break;
}
}
} catch (error) {
console.error(`Guardrail "${guardrail.name}" error:`, error);
}
}
}
if (redactPatterns.length > 0 && chunkText) {
const redactedText = applyRedaction(
chunkText,
redactPatterns,
redactionText
);
if (redactedText !== chunkText) {
controller.enqueue({
...chunk,
delta: redactedText,
textDelta: redactedText
});
return;
}
}
}
controller.enqueue(chunk);
},
flush(controller) {
if (stopped && violations.length > 0) {
}
}
});
};
}
async function handleViolation(chunk, violation, context, options) {
const { onViolation, redactPatterns, redactionText, replacementText } = options;
if (typeof onViolation === "function") {
return onViolation(chunk, violation, context);
}
switch (onViolation) {
case "stop":
return { action: "stop", reason: violation.message };
case "drop":
return { action: "drop", reason: violation.message };
case "redact":
const chunkText = chunk.delta || chunk.textDelta || "";
const redacted = applyRedaction(chunkText, redactPatterns, redactionText);
if (redacted !== chunkText) {
return {
action: "replace",
replacement: redacted,
reason: "Content redacted"
};
}
return { action: "pass" };
case "replace":
return {
action: "replace",
replacement: replacementText,
reason: violation.message
};
default:
return { action: "pass" };
}
}
function applyRedaction(text, patterns, redactionText) {
let result = text;
for (const pattern of patterns) {
if (typeof pattern === "string") {
result = result.split(pattern).join(redactionText);
} else {
result = result.replace(pattern, () => redactionText);
}
}
return result;
}
var PII_PATTERNS = {
/** US Social Security Number */
SSN: /\b\d{3}-\d{2}-\d{4}\b/g,
/** Email addresses */
EMAIL: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
/** Phone numbers (various formats) */
PHONE: /\b(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b/g,
/** Credit card numbers */
CREDIT_CARD: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g,
/** IP addresses */
IP_ADDRESS: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g,
/** API keys (generic pattern) */
API_KEY: /\b(sk|pk|api)[_-]?[a-zA-Z0-9]{20,}\b/gi
};
function createPIIRedactionTransform(options = {}) {
const patterns = options.patterns || [
PII_PATTERNS.SSN,
PII_PATTERNS.EMAIL,
PII_PATTERNS.PHONE,
PII_PATTERNS.CREDIT_CARD
];
return createGuardrailTransform([], {
onViolation: "redact",
// No guardrails, just pattern redaction
redactPatterns: patterns,
redactionText: options.redactionText || "[REDACTED]",
requestContext: options.requestContext
});
}
function createContentFilterTransform(options) {
const {
blockedKeywords,
caseSensitive = false,
onBlocked,
requestContext
} = options;
return ({ stopStream }) => {
let accumulatedText = "";
let stopped = false;
return new TransformStream({
transform(chunk, controller) {
if (stopped) return;
const chunkText = chunk.delta || chunk.textDelta || chunk.text || "";
if (chunk.type === "text-delta" || chunk.type === "text") {
accumulatedText += chunkText;
const textToCheck = caseSensitive ? accumulatedText : accumulatedText.toLowerCase();
for (const keyword of blockedKeywords) {
const keywordToCheck = caseSensitive ? keyword : keyword.toLowerCase();
if (textToCheck.includes(keywordToCheck)) {
stopped = true;
stopStream();
if (onBlocked) {
onBlocked(keyword, accumulatedText);
}
controller.enqueue({
...chunk,
type: "text-delta",
delta: `
[Content blocked: prohibited content detected]`
});
return;
}
}
}
controller.enqueue(chunk);
}
});
};
}
// src/guardrails/prepare-step.ts
function createGuardrailPrepareStep(violations, options = {}) {
const {
lookback = 2,
temperatureReduction = 0.3,
stopOnCritical = false,
warningMessage = "Previous responses violated guidelines. Please be more careful and follow all safety guidelines."
} = options;
return ({ stepNumber }) => {
const recentViolations = violations.filter((v) => {
if ("step" in v) {
return v.step >= stepNumber - lookback && v.step < stepNumber;
}
return false;
});
if (recentViolations.length === 0) {
return;
}
const hasCritical = recentViolations.some(
(v) => v.summary.blockedResults.some((r) => r.severity === "critical")
);
const result = {
temperature: temperatureReduction,
system: warningMessage
};
if (hasCritical && stopOnCritical) {
result.stopWhen = () => true;
}
return result;
};
}
function createAdaptivePrepareStep(options) {
const {
violations,
strategy,
onViolationDetected,
escalateAfter = 5,
lookback = 3
} = options;
return ({ stepNumber }) => {
const recentViolations = violations.filter((v) => {
if ("step" in v) {
return v.step >= stepNumber - lookback && v.step < stepNumber;
}
return false;
});
if (recentViolations.length === 0) {
return;
}
onViolationDetected?.(recentViolations);
if (strategy) {
return strategy(recentViolations);
}
const violationCount = recentViolations.length;
const temperatureReduction = Math.max(0.1, 0.7 - violationCount * 0.15);
const result = {
temperature: temperatureReduction,
system: `Warning: ${violationCount} guardrail violation(s) detected in recent steps. Please ensure responses comply with all safety guidelines.`
};
if (violationCount >= escalateAfter) {
result.stopWhen = () => true;
result.system += " Execution will stop due to repeated violations.";
}
return result;
};
}
// src/guardrails/tool-abortion.ts
var ToolAbortionController = class {
controller;
minSeverity;
timeout;
constructor(options = {}) {
this.controller = new AbortController();
this.minSeverity = options.minSeverity ?? "critical";
this.timeout = options.timeout ?? 3e3;
}
get signal() {
return this.controller.signal;
}
/**
* Check guardrails and abort if violations detected
*/
async checkAndAbort(guardrails, context) {
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[this.minSeverity];
const results = await executeOutputGuardrails(guardrails, context, {
parallel: true,
timeout: this.timeout,
continueOnFailure: true,
logLevel: "none"
});
const shouldAbort = results.some((result) => {
if (!result.tripwireTriggered) return false;
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldAbort) {
this.controller.abort("Guardrail violation detected");
return true;
}
return false;
}
/**
* Manually abort
*/
abort(reason) {
this.controller.abort(reason);
}
};
function createToolAbortionController(options) {
return new ToolAbortionController(options);
}
function wrapToolWithAbortion(tool, guardrails, options = {}) {
const {
checkBefore = false,
monitorDuring = false,
monitorInterval = 100,
checkInputDelta = false,
abortOnSeverity = "critical",
timeout = 3e3
} = options;
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[abortOnSeverity];
const originalExecute = tool.execute;
const originalOnInputDelta = tool.onInputDelta;
async function checkGuardrails(input) {
const mockResult = {
text: JSON.stringify(input),
content: [],
finishReason: "stop",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
const context = {
input: {
prompt: "",
messages: [],
system: ""
},
result: mockResult
};
const results = await executeOutputGuardrails(guardrails, context, {
parallel: true,
timeout,
continueOnFailure: true,
logLevel: "none"
});
const shouldAbort = results.some((result) => {
if (!result.tripwireTriggered) return false;
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldAbort) {
const messages = results.filter((r) => r.tripwireTriggered).map((r) => r.message).join(", ");
throw new Error(`Tool execution aborted: ${messages}`);
}
}
const wrappedExecute = async (input, executeOptions) => {
if (checkBefore) {
await checkGuardrails(input);
}
const internalController = new AbortController();
let monitorInterval_;
let monitorError;
if (monitorDuring) {
monitorInterval_ = setInterval(async () => {
try {
await checkGuardrails(input);
} catch (error) {
monitorError = error;
internalController.abort();
clearInterval(monitorInterval_);
}
}, monitorInterval);
}
try {
const combinedSignal = monitorDuring && executeOptions?.abortSignal ? AbortSignal.any([
executeOptions.abortSignal,
internalController.signal
]) : executeOptions?.abortSignal ?? (monitorDuring ? internalController.signal : void 0);
const callOptions = combinedSignal ? { ...executeOptions, abortSignal: combinedSignal } : executeOptions;
const result = await originalExecute.call(tool, input, callOptions);
if (monitorInterval_) {
clearInterval(monitorInterval_);
}
if (monitorError) {
throw monitorError;
}
return result;
} catch (error) {
if (monitorInterval_) {
clearInterval(monitorInterval_);
}
throw monitorError ?? error;
}
};
const wrappedOnInputDelta = checkInputDelta && originalOnInputDelta ? async (deltaOptions) => {
const mockResult = {
text: deltaOptions.inputTextDelta,
content: [],
finishReason: "stop",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
const context = {
input: {
prompt: "",
messages: [],
system: ""
},
result: mockResult
};
const results = await executeOutputGuardrails(guardrails, context, {
parallel: true,
timeout,
continueOnFailure: true,
logLevel: "none"
});
const shouldAbort = results.some((result) => {
if (!result.tripwireTriggered) return false;
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (shouldAbort) {
const violation = results.find((r) => r.tripwireTriggered);
throw new Error(
`Tool input delta blocked by guardrail: ${violation?.message || "Guardrail violation"}`
);
}
await originalOnInputDelta?.call(tool, deltaOptions);
} : originalOnInputDelta;
return {
...tool,
execute: wrappedExecute,
...wrappedOnInputDelta && { onInputDelta: wrappedOnInputDelta }
};
}
// src/guardrails/abort-controller.ts
var GuardrailViolationAbort = class extends Error {
summary;
constructor(summary) {
const messages = summary.blockedResults.map((r) => r.message).filter(Boolean).join(", ");
super(`Guardrail violation: ${messages}`);
this.name = "GuardrailViolationAbort";
this.summary = summary;
}
};
function createGuardrailAbortController() {
const controller = new AbortController();
return {
/**
* The AbortSignal that can be passed to AI SDK functions
*/
signal: controller.signal,
/**
* Creates a callback that aborts on violations of specified severity or higher
*
* @param minSeverity - Minimum severity to trigger abort (default: 'critical')
* @returns Callback function for use with onInputBlocked/onOutputBlocked
*
* @example
* ```typescript
* const { signal, abortOnViolation } = createGuardrailAbortController();
*
* withGuardrails({ model,
* outputGuardrails: [toxicityFilter()],
* onOutputBlocked: abortOnViolation('high'), // Abort on high or critical
* });
* ```
*/
abortOnViolation: (minSeverity = "critical") => {
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
const minLevel = severityOrder[minSeverity];
return (summary) => {
const hasViolation = summary.blockedResults.some((result) => {
const resultSeverity = result.severity ?? "medium";
return severityOrder[resultSeverity] >= minLevel;
});
if (hasViolation) {
controller.abort(new GuardrailViolationAbort(summary));
}
};
},
/**
* Creates a callback that aborts based on custom condition
*
* @param condition - Function that returns true to trigger abort
* @returns Callback function for use with onInputBlocked/onOutputBlocked
*
* @example
* ```typescript
* const { signal, abortOnCondition } = createGuardrailAbortController();
*
* withGuardrails({ model,
* outputGuardrails: [qualityCheck()],
* onOutputBlocked: abortOnCondition(
* (summary) => summary.blockedResults.length > 2
* ),
* });
* ```
*/
abortOnCondition: (condition) => {
return (summary) => {
if (condition(summary)) {
controller.abort(new GuardrailViolationAbort(summary));
}
};
},
/**
* Manually abort with custom reason
*
* @param reason - Custom abort reason
*
* @example
* ```typescript
* const { abort } = createGuardrailAbortController();
* abort('User requested cancellation');
* ```
*/
abort: (reason) => {
controller.abort(reason);
}
};
}
// src/guardrails/finish-reason.ts
function getGuardrailFinishReason(summary, options) {
const { blocked = "content_filter", success = "stop" } = options ?? {};
if (summary.blockedResults.length > 0) {
return blocked;
}
return success;
}
function createGuardrailProviderMetadata(summary, options) {
const { includeMetadata = false, includeStats = true } = options ?? {};
const violations = summary.blockedResults.map((result) => {
const violation = {
message: result.message,
severity: result.severity,
guardrailName: result.context?.guardrailName
};
if (includeMetadata && result.metadata) {
violation.metadata = result.metadata;
}
return violation;
});
return {
guardrails: {
blocked: summary.blockedResults.length > 0,
violations,
executionTime: summary.totalExecutionTime,
guardrailsExecuted: summary.guardrailsExecuted,
...includeStats && { stats: summary.stats }
}
};
}
function createFinishReasonEnhancement(summary, result, options) {
if (summary.blockedResults.length === 0) {
return result;
}
const finishReason = getGuardrailFinishReason(summary, options);
const guardrailMetadata = createGuardrailProviderMetadata(summary, options);
return {
...result,
finishReason,
providerMetadata: result.providerMetadata ? { ...result.providerMetadata, ...guardrailMetadata } : guardrailMetadata
};
}
// src/guardrails/tool-parameters.ts
function matchesToolName(guardrailToolName, toolName) {
if (typeof guardrailToolName === "string") {
return guardrailToolName === toolName || guardrailToolName === "*";
}
if (guardrailToolName instanceof RegExp) {
return guardrailToolName.test(toolName);
}
if (Array.isArray(guardrailToolName)) {
return guardrailToolName.includes(toolName);
}
return false;
}
function withToolParameterGuardrails(tools, guardrails, options = {}) {
const { throwOnInvalid = true, onValidationFailed, requestContext } = options;
const wrappedTools = {};
for (const [toolName, tool] of Object.entries(tools)) {
const originalTool = tool;
const applicableGuardrails = guardrails.filter(
(g) => matchesToolName(g.toolName, toolName)
);
if (applicableGuardrails.length === 0) {
wrappedTools[toolName] = tool;
continue;
}
wrappedTools[toolName] = {
...originalTool,
execute: async (input, execOptions) => {
const context = {
toolName,
requestContext
};
let currentInput = input;
const failedResults = [];
for (const guardrail of applicableGuardrails) {
const result = await guardrail.validateInput(currentInput, context);
if (!result.valid) {
failedResults.push(result);
if (result.block) {
if (onValidationFailed) {
onValidationFailed(toolName, input, [result]);
}
if (throwOnInvalid) {
throw new ToolParameterValidationError(
toolName,
guardrail.name,
result.message || "Validation failed",
result.severity
);
}
return {
error: `Tool parameter validation failed: ${result.message}`,
blocked: true,
guardrail: guardrail.name
};
}
} else if (result.sanitizedInput !== void 0) {
currentInput = result.sanitizedInput;
}
}
if (failedResults.length > 0) {
if (onValidationFailed) {
onValidationFailed(toolName, input, failedResults);
}
if (throwOnInvalid) {
const messages = failedResults.map((r) => r.message).join("; ");
throw new ToolParameterValidationError(
toolName,
"multiple",
messages,
failedResults[0]?.severity
);
}
}
return originalTool.execute(currentInput, execOptions);
}
};
}
return wrappedTools;
}
var ToolParameterValidationError = class extends Error {
constructor(toolName, guardrailName, message, severity) {
super(
`Tool "${toolName}" parameter validation failed (${guardrailName}): ${message}`
);
this.toolName = toolName;
this.guardrailName = guardrailName;
this.severity = severity;
this.name = "ToolParameterValidationError";
}
toolName;
guardrailName;
severity;
};
// src/backoff.ts
function exponentialBackoff(options = {}) {
const { base = 1e3, max = 3e4, jitter = 0, multiplier = 2 } = options;
return (attempt) => {
const exponentialDelay = base * Math.pow(multiplier, attempt - 1);
const cappedDelay = Math.min(exponentialDelay, max);
if (jitter > 0) {
const jitterAmount = cappedDelay * jitter * Math.random();
return Math.round(
cappedDelay + jitterAmount - cappedDelay * jitter / 2
);
}
return cappedDelay;
};
}
function linearBackoff(options = {}) {
const { base = 1e3, max = 3e4, jitter = 0 } = options;
return (attempt) => {
const linearDelay = base * attempt;
const cappedDelay = Math.min(linearDelay, max);
if (jitter > 0) {
const jitterAmount = cappedDelay * jitter * Math.random();
return Math.round(
cappedDelay + jitterAmount - cappedDelay * jitter / 2
);
}
return cappedDelay;
};
}
function fixedBackoff(options = {}) {
const { base = 1e3, jitter = 0 } = options;
return (_attempt) => {
if (jitter > 0) {
const jitterAmount = base * jitter * Math.random();
return Math.round(base + jitterAmount - base * jitter / 2);
}
return base;
};
}
function noBackoff() {
return (_attempt) => 0;
}
function compositeBackoff(strategies) {
return (attempt) => {
for (const strategy of strategies) {
if (attempt <= strategy.maxAttempts) {
return strategy.backoff(attempt);
}
}
const lastStrategy = strategies.at(-1);
return lastStrategy ? lastStrategy.backoff(attempt) : 0;
};
}
var jitteredExponentialBackoff = (options = {}) => exponentialBackoff({ ...options, jitter: 0.1 });
var presets = {
/** Fast retry: 500ms, 1s, 2s, 4s (max 4s) */
fast: () => exponentialBackoff({ base: 500, max: 4e3 }),
/** Standard retry: 1s, 2s, 4s, 8s, 16s (max 16s) */
standard: () => exponentialBackoff({ base: 1e3, max: 16e3 }),
/** Slow retry: 2s, 4s, 8s, 16s, 32s (max 32s) */
slow: () => exponentialBackoff({ base: 2e3, max: 32e3 }),
/** Network resilient: jittered exponential with longer delays */
networkResilient: () => jitteredExponentialBackoff({ base: 1e3, max: 3e4 }),
/** Aggressive: very fast with short max delay for quick failures */
aggressive: () => exponentialBackoff({ base: 200, max: 2e3 })
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DEFAULT_DETECT_NORMALIZATION,
GuardrailViolationAbort,
PII_PATTERNS,
ToolParameterValidationError,
after,
backoffPresets,
clearViolationHistory,
compositeBackoff,
createAdaptivePrepareStep,
createConsoleDebugger,
createContentFilterTransform,
createDebugWrapper,
createDefaultBuildRetryParams,
createFinishReasonEnhancement,
createGuardrailAbortController,
createGuardrailPrepareStep,
createGuardrailProviderMetadata,
createGuardrailStreamTransform,
createGuardrailStreamTransformBuffered,
createGuardrailTransform,
createHealthCheck,
createMetricsCollector,
createPIIRedactionTransform,
createPipeline,
createTokenAwareGuardrailTransform,
createTokenBudgetTransform,
createToolAbortionController,
detectSystemPromptLeak,
enhancedPromptInjectionDetector,
envDebugMode,
estimateTokenCount,
exponentialBackoff,
fixedBackoff,
formatTraceAsJSON,
formatTraceForConsole,
formatTraceSummary,
getGuardrailFinishReason,
getViolationStats,
guardrailMiddleware,
incrementalPromptInjectionDetector,
inputGuardrailsMiddleware,
inputPipeline,
intentBasedInjectionDetector,
jitteredExponentialBackoff,
lenientEscalation,
linearBackoff,
logExecutionSummary,
noBackoff,
noopGuardrailMiddleware,
normalizeForDetection,
not,
outputGuardrailsMiddleware,
outputPipeline,
parallel,
resolveDetectNormalization,
resolveRetryConfig,
strictEscalation,
toolCallInjectionDetector,
warnOnly,
when,
withFallback,
withGracePeriod,
withGradualEnforcement,
withRetry,
withToolParameterGuardrails,
wrapToolWithAbortion
});