ai-sdk-guardrails
Version:
Input and output guardrails middleware for Vercel AI SDK.
1,153 lines • 50.3 kB
TypeScript
import { I as InputGuardrail, a as InputGuardrailContext, L as Logger, G as GuardrailResult, O as OutputGuardrail, b as OutputGuardrailContext, N as NormalizedGuardrailContext, c as OutputGuardrailsMiddlewareConfig, d as InputGuardrailsMiddlewareConfig, e as GuardrailExecutionSummary, R as RequestContext, A as AIResult } from './types-C7t6e3EI.js';
export { j as EmbedParams, m as EmbedResult, E as ExtractGuardrailMetadata, i as GenerateTextParams, k as GenerateTextResult, n as GuardrailRetryConfig, f as GuardrailsParams, g as InferInputMetadata, h as InferOutputMetadata, p as RetryInstruction, o as RetryInstructionContext, S as StreamTextParams, l as StreamTextResult, U as UnionFromGuardrails } from './types-C7t6e3EI.js';
import { LanguageModel, ToolSet, StopCondition, GenerateTextOnStepEndCallback, ToolApprovalStatus } from 'ai';
export { CallWarning, FinishReason, LanguageModel, LanguageModelMiddleware, LanguageModelUsage, ProviderMetadata, ToolSet } from 'ai';
import { T as ToolParameterGuardrail, a as ToolValidationResult } from './stop-conditions-DZzszrpo.js';
export { G as GuardrailViolation, b as SystemPromptLeakMetadata, S as SystemPromptLeakOptions, e as ToolValidationContext, l as allOf, k as anyOf, m as customStopCondition, j as hasConsecutiveViolations, h as hasCriticalViolation, g as hasGuardrailViolation, f as hasViolationSeverity, i as isViolationCount, d as parameterLengthGuardrail, p as pathTraversalGuardrail, c as sqlInjectionGuardrail, s as systemPromptLeakDetector, t as toolRBACGuardrail } from './stop-conditions-DZzszrpo.js';
export { NormalizedUsage, biasDetector, blockedContent, complianceChecker, confidenceThreshold, contentConsistencyChecker, costQuotaRails, customValidation as customOutputValidation, enhancedHallucinationDetector, extractContent, factualAccuracyChecker, hallucinationDetector, jsonValidation, minLengthRequirement, normalizeUsage, outputLengthLimit, performanceMonitor, privacyLeakageDetector, retryAfterIntegration, schemaValidation, secretRedaction, sensitiveDataFilter, stringifyContent, tokenUsageLimit, toxicityFilter, unsafeContentDetector } from './guardrails/output.js';
export { AllowedToolsOptions, BlockedWordsOptions, CodeGenerationMode, CodeGenerationOptions, CustomValidationInput, CustomValidationOptions, CustomValidationResult, HighEntropyOptions, LengthLimitOptions, MathHomeworkOptions, ProfanityCategory, ProfanityFilterOptions, PromptInjectionOptions, RateLimitingOptions, allowedToolsGuardrail, blockedKeywords, blockedWords, codeGenerationLimiter, contentLengthLimit, customValidation as customInputValidation, extractMetadata, extractTextContent, highEntropyDetector, inputLengthLimit, mathHomeworkDetector, piiDetector, profanityFilter, promptInjectionDetector, rateLimiting, toxicityDetector } from './guardrails/input.js';
export { ExpectedToolUseMetadata, ExpectedToolUseOptions, ToolEgressPolicyOptions, expectedToolUse, extractToolNamesFromResult, toolEgressPolicy } from './guardrails/tools.js';
export { D as DetectNormalizationOptions } from './normalization-D6TVuWIv.js';
import { P as PlanRiskAssessment } from './peer-CxUvjCqU.js';
import { LanguageModelV4CallOptions, AISDKError } from '@ai-sdk/provider';
/**
* Internal guardrail execution engine and shared substrate.
*
* This module holds the performance-sensitive machinery shared by the public
* authoring API ({@link ../guardrails}) and the V4 middleware factories
* ({@link ./middleware-factories}): context normalization + caching, the pooled
* timeout runner, batch execution, the execution-summary builder, and the
* `executeInput/OutputGuardrails` engine. It deliberately depends on nothing in
* `../guardrails`, so the public barrel can re-export from here without a cycle.
*/
type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
/**
* Normalizes AI SDK parameters into a consistent guardrail context
* This improves type safety and reduces coupling to specific AI SDK parameter types
* Uses caching to avoid redundant processing of the same parameters
*/
declare function normalizeGuardrailContext(params: LanguageModelV4CallOptions): NormalizedGuardrailContext;
/** Shared execution options accepted by both engines. */
interface ExecuteOptions {
/** Execute guardrails in parallel (default: true) */
parallel?: boolean;
/** Maximum execution time in milliseconds */
timeout?: number;
/** Whether to continue on first failure */
continueOnFailure?: boolean;
/** Logging level */
logLevel?: LogLevel;
/** Custom logger instance */
logger?: Logger;
}
/**
* Executes input guardrails with enhanced performance monitoring and error handling
* @param guardrails - Array of input guardrails to execute
* @param params - Parameters for guardrail execution
* @param options - Execution options
* @returns Promise resolving to array of guardrail results
*/
declare function executeInputGuardrails<M extends Record<string, unknown> = Record<string, unknown>>(guardrails: InputGuardrail<M>[], params: InputGuardrailContext, options?: ExecuteOptions): Promise<GuardrailResult<M>[]>;
/**
* Executes output guardrails with enhanced performance monitoring and error handling
* @param guardrails - Array of output guardrails to execute
* @param params - Parameters for guardrail execution
* @param options - Execution options
* @returns Promise resolving to array of guardrail results
*/
declare function executeOutputGuardrails<M extends Record<string, unknown> = Record<string, unknown>>(guardrails: OutputGuardrail<M>[], params: OutputGuardrailContext, options?: ExecuteOptions & {
/** Accumulated text for streaming scenarios */
accumulatedText?: string;
}): Promise<GuardrailResult<M>[]>;
/**
* Creates a well-structured input guardrail with enhanced metadata
* @param guardrail - The guardrail configuration
* @returns Enhanced input guardrail with automatic metadata injection
*/
declare function defineInputGuardrail<M extends Record<string, unknown> = Record<string, unknown>>(guardrail: InputGuardrail<M>): InputGuardrail<M>;
/**
* Creates a well-structured output guardrail with enhanced metadata
* @param guardrail - The guardrail configuration
* @returns Enhanced output guardrail with automatic metadata injection
*/
declare function defineOutputGuardrail<M extends Record<string, unknown> = Record<string, unknown>>(guardrail: OutputGuardrail<M>): OutputGuardrail<M>;
/**
* Configuration shared by {@link withGuardrails} and {@link createGuardrails}.
* The model itself is supplied separately by `withGuardrails`.
*/
interface GuardrailModelConfig<MIn extends Record<string, unknown> = Record<string, unknown>, MOut extends Record<string, unknown> = Record<string, unknown>> {
inputGuardrails?: InputGuardrail<MIn>[];
outputGuardrails?: OutputGuardrail<MOut>[];
throwOnBlocked?: boolean;
replaceOnBlocked?: boolean;
streamMode?: 'buffer' | 'progressive';
stopOnGuardrailViolation?: OutputGuardrailsMiddlewareConfig<MOut>['stopOnGuardrailViolation'];
executionOptions?: InputGuardrailsMiddlewareConfig<MIn>['executionOptions'];
onInputBlocked?: InputGuardrailsMiddlewareConfig<MIn>['onInputBlocked'];
onOutputBlocked?: OutputGuardrailsMiddlewareConfig<MOut>['onOutputBlocked'];
retry?: OutputGuardrailsMiddlewareConfig<MOut>['retry'];
}
/**
* Primary guardrails API - wraps a language model with input and/or output guardrails
*
* This is the main entry point for applying guardrails to AI models. Use this decorator-like
* function for most use cases.
*
* @param config - The model to wrap plus input/output guardrail configuration
* @returns Wrapped language model with guardrails applied
*
* @example
* ```typescript
* import { openai } from '@ai-sdk/openai';
* import { withGuardrails } from 'ai-sdk-guardrails';
* import { piiDetector } from 'ai-sdk-guardrails/guardrails/input';
* import { minLength } from 'ai-sdk-guardrails/guardrails/output';
*
* const guardedModel = withGuardrails({
* model: openai('gpt-4o'),
* inputGuardrails: [piiDetector()],
* outputGuardrails: [minLength(100)],
* throwOnBlocked: true,
* });
* ```
*/
declare function withGuardrails<MIn extends Record<string, unknown> = Record<string, unknown>, MOut extends Record<string, unknown> = Record<string, unknown>>(config: GuardrailModelConfig<MIn, MOut> & {
/** The language model to wrap. */
model: LanguageModel;
}): LanguageModel;
/**
* Creates a reusable guardrails configuration factory
*
* Use this factory when you want to apply the same guardrails configuration to multiple
* models, or when building composable guardrail systems.
*
* @param config - Configuration for both input and output guardrails
* @returns Function that accepts a model and returns a wrapped model
*
* @example
* ```typescript
* import { openai } from '@ai-sdk/openai';
* import { anthropic } from '@ai-sdk/anthropic';
* import { createGuardrails } from 'ai-sdk-guardrails';
* import { piiDetector } from 'ai-sdk-guardrails/guardrails/input';
* import { qualityCheck } from 'ai-sdk-guardrails/guardrails/output';
*
* // Create reusable guardrails configuration
* const productionGuards = createGuardrails({
* inputGuardrails: [piiDetector()],
* outputGuardrails: [qualityCheck()],
* throwOnBlocked: true,
* });
*
* // Apply to multiple models
* const gpt4 = productionGuards(openai('gpt-4o'));
* const claude = productionGuards(anthropic('claude-3-sonnet'));
*
* // Compose multiple guardrail sets
* const strictLimits = createGuardrails({ inputGuardrails: [maxLength(500)] });
* const piiProtection = createGuardrails({ inputGuardrails: [piiDetector()] });
* const model = piiProtection(strictLimits(openai('gpt-4o')));
* ```
*/
declare function createGuardrails<MIn extends Record<string, unknown> = Record<string, unknown>, MOut extends Record<string, unknown> = Record<string, unknown>>(config: GuardrailModelConfig<MIn, MOut>): (model: LanguageModel) => LanguageModel;
type AnyRecord = Record<string, unknown>;
interface AgentGuardrailsConfig<TOOLS extends ToolSet = ToolSet> {
/** The language model the agent runs on, wrapped with the guardrails below. */
model: LanguageModel;
/** Input guardrails — run as model middleware on the request. */
inputGuardrails?: Array<InputGuardrail<any>>;
/** Output guardrails — run as model middleware on the response. */
outputGuardrails?: Array<OutputGuardrail<any>>;
/**
* Output guardrails applied to tool calls/results (e.g. `toolEgressPolicy`).
* Folded into the model's output guardrails — they see tool-call content in the
* model result. For *parameter*-level pre-execution gating, set the agent's
* native `toolApproval` with `guardrailApproval([...])` instead.
*/
toolGuardrails?: Array<OutputGuardrail<any>>;
/** Throw on a blocked input/output instead of replacing. */
throwOnBlocked?: boolean;
/** Replace blocked output text with a message (default true). */
replaceOnBlocked?: boolean;
/** Auto-retry config forwarded to the model's output-guardrail middleware. */
retry?: OutputGuardrailsMiddlewareConfig<AnyRecord>['retry'];
executionOptions?: {
parallel?: boolean;
timeout?: number;
continueOnFailure?: boolean;
logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
onInputBlocked?: InputGuardrailsMiddlewareConfig<AnyRecord>['onInputBlocked'];
onOutputBlocked?: OutputGuardrailsMiddlewareConfig<AnyRecord>['onOutputBlocked'];
/**
* A `stopWhen` condition to compose with the guardrail-violation stop
* condition. Pass it here (not to `ToolLoopAgent`) so it is combined rather
* than overwritten by the returned fragment.
*/
stopWhen?: StopCondition<TOOLS> | Array<StopCondition<TOOLS>>;
/**
* Halt the agent loop when output guardrails trip. The model middleware still
* enforces (block/replace) content; this drives *loop termination*, which has
* no native equivalent. Each real block recorded by the middleware (via
* `onOutputBlocked`) counts toward the threshold — the stop decision rides the
* exact same evaluation the middleware enforced, never a re-run.
*
* - `true` stop on 3 violations or any critical
* - `number` stop after N violations
* - fn custom predicate over the violation history
*/
stopOnGuardrailViolation?: boolean | number | ((violations: Array<{
step: number;
summary: GuardrailExecutionSummary;
}>) => boolean);
}
/**
* Native `ToolLoopAgentSettings` fragments contributed by the guardrails layer.
* Spread into your own `new ToolLoopAgent({ ... })` — the result is a *real*
* `ToolLoopAgent`, so streaming, structured `output`, `runtimeContext`, and
* `InferAgentUIMessage<typeof agent>` all keep working.
*/
interface AgentGuardrailsFragments<TOOLS extends ToolSet = ToolSet> {
/** The model wrapped with input/output content guardrails. */
model: LanguageModel;
/** Present only when `stopOnGuardrailViolation` is set (composed with yours). */
stopWhen?: StopCondition<TOOLS> | Array<StopCondition<TOOLS>>;
/**
* Present only when `stopOnGuardrailViolation` is set. Stamps each recorded
* block with its real agent step index (does NOT re-run guardrails). If you
* also need your own `onStepEnd`, compose it after spreading these fragments.
*/
onStepEnd?: GenerateTextOnStepEndCallback<TOOLS>;
}
/**
* Build native AI SDK `ToolLoopAgentSettings` fragments that add guardrails to an
* agent. Spread the result into your own `ToolLoopAgent` — guardrails ride the
* SDK's own primitives (model middleware, `stopWhen`, `onStepEnd`,
* `telemetry.integrations`) rather than wrapping the agent:
*
* ```ts
* import { ToolLoopAgent } from 'ai';
* import { agentGuardrails, piiDetector, sensitiveDataFilter } from 'ai-sdk-guardrails';
*
* const agent = new ToolLoopAgent({
* ...agentGuardrails({
* model,
* inputGuardrails: [piiDetector()],
* outputGuardrails: [sensitiveDataFilter()],
* stopOnGuardrailViolation: true,
* }),
* instructions: 'You are a helpful assistant.',
* tools,
* });
*
* await agent.generate({ prompt: '...' }); // .stream() is guarded too
* ```
*
* Input/output content guardrails run as model middleware (the only layer that
* can block, replace, or retry the model's output). Tool *parameter* gating stays
* native: set `toolApproval: guardrailApproval([...])` on the agent yourself.
*/
declare function agentGuardrails<TOOLS extends ToolSet = ToolSet>(config: AgentGuardrailsConfig<TOOLS>): AgentGuardrailsFragments<TOOLS>;
declare const symbol: unique symbol;
/**
* Base class for all guardrails-related errors. Extends the AI SDK's
* {@link AISDKError} so guardrail failures sit in the same error hierarchy as the
* rest of the SDK and are catchable via `AISDKError.isInstance(err)`.
*/
declare abstract class GuardrailsError extends AISDKError {
private readonly [symbol];
abstract readonly code: string;
readonly timestamp: Date;
readonly metadata: Record<string, unknown>;
constructor(name: string, message: string, metadata?: Record<string, unknown>, cause?: unknown);
/**
* Convert the error to a serializable object for logging/reporting
*/
toJSON(): {
name: string;
code: string;
message: string;
timestamp: string;
metadata: Record<string, unknown>;
stack: string | undefined;
};
/**
* Check if this error is of a specific guardrails error subclass.
*/
is<T extends GuardrailsError>(errorClass: new (...args: any[]) => T): this is T;
/**
* Checks whether the given value is a guardrails error, across package
* versions (marker-based, like `AISDKError.isInstance`).
*/
static isInstance(error: unknown): error is GuardrailsError;
}
/**
* Thrown when guardrail validation fails
*/
declare class GuardrailValidationError extends GuardrailsError {
readonly code = "GUARDRAIL_VALIDATION_FAILED";
readonly guardrailName: string;
readonly validationErrors: ValidationError[];
constructor(guardrailName: string, validationErrors: ValidationError[], metadata?: Record<string, unknown>);
}
/**
* Thrown when guardrail execution encounters an error
*/
declare class GuardrailExecutionError extends GuardrailsError {
readonly code = "GUARDRAIL_EXECUTION_FAILED";
readonly guardrailName: string;
readonly originalError?: Error;
constructor(guardrailName: string, originalError?: Error, metadata?: Record<string, unknown>);
}
/**
* Thrown when a guardrail times out during execution
*/
declare class GuardrailTimeoutError extends GuardrailsError {
readonly code = "GUARDRAIL_TIMEOUT";
readonly guardrailName: string;
readonly timeoutMs: number;
constructor(guardrailName: string, timeoutMs: number, metadata?: Record<string, unknown>);
}
/**
* Thrown when guardrail configuration is invalid
*/
declare class GuardrailConfigurationError extends GuardrailsError {
readonly code = "GUARDRAIL_CONFIG_INVALID";
readonly configPath?: string;
readonly configErrors: string[];
constructor(configErrors: string[], configPath?: string, metadata?: Record<string, unknown>);
}
/**
* Thrown when input to guardrails is blocked/rejected
*/
declare class GuardrailsInputError extends GuardrailsError {
readonly code = "INPUT_BLOCKED";
readonly blockedGuardrails: Array<{
name: string;
message: string;
severity: 'low' | 'medium' | 'high' | 'critical';
}>;
constructor(blockedGuardrails: GuardrailsInputError['blockedGuardrails'], metadata?: Record<string, unknown>);
}
/**
* Thrown when output from AI model is blocked/rejected
*/
declare class GuardrailsOutputError extends GuardrailsError {
readonly code = "OUTPUT_BLOCKED";
readonly blockedGuardrails: Array<{
name: string;
message: string;
severity: 'low' | 'medium' | 'high' | 'critical';
}>;
constructor(blockedGuardrails: GuardrailsOutputError['blockedGuardrails'], metadata?: Record<string, unknown>);
}
/**
* Thrown when middleware encounters an error
*/
declare class MiddlewareError extends GuardrailsError {
readonly code = "MIDDLEWARE_ERROR";
readonly middlewareType: 'input' | 'output';
readonly phase: 'transform' | 'wrap' | 'execute';
readonly originalError?: Error;
constructor(middlewareType: 'input' | 'output', phase: 'transform' | 'wrap' | 'execute', originalError?: Error, metadata?: Record<string, unknown>);
}
/**
* Individual validation error within a guardrail
*/
interface ValidationError {
field?: string;
message: string;
code?: string;
value?: unknown;
}
/**
* Utility function to check if an error is a guardrails error.
* Equivalent to {@link GuardrailsError.isInstance}.
*/
declare function isGuardrailsError(error: unknown): error is GuardrailsError;
/**
* Utility function to extract error information for logging
*/
declare function extractErrorInfo(error: unknown): {
name: string;
message: string;
code?: string;
metadata?: Record<string, unknown>;
};
declare function createInputGuardrail(name: string, description: string, execute: InputGuardrail['execute']): InputGuardrail;
declare function createOutputGuardrail<M extends Record<string, unknown> = Record<string, unknown>>(name: string, execute: OutputGuardrail<NoInfer<M>>['execute']): OutputGuardrail<M>;
interface RetryAttemptInfo<R = unknown> {
attempt: number;
totalAttempts: number;
lastResult?: R;
waitMs?: number;
isRetry: boolean;
}
/**
* Configuration options for the retry function
*
* @example Basic retry with token increase
* ```ts
* const result = await retry({
* generate: (params) => generateText(params),
* params: { prompt: 'Explain AI', maxOutputTokens: 100 },
* validate: (result) => ({
* blocked: result.text.length < 200,
* message: 'Response too short'
* }),
* buildRetryParams: retryHelpers.increaseTokens(200),
* maxRetries: 2
* });
* ```
*
* @example Advanced retry with custom backoff and error handling
* ```ts
* const result = await retry({
* generate: (params, signal) => generateText(params),
* params: { prompt: 'Write essay', maxOutputTokens: 500 },
* validate: async (result) => ({
* blocked: await checkQuality(result.text) < 0.8,
* message: 'Quality too low',
* metadata: { quality: await checkQuality(result.text) }
* }),
* buildRetryParams: ({ summary, lastParams }) => ({
* ...lastParams,
* temperature: Math.min(0.9, lastParams.temperature + 0.1),
* maxOutputTokens: lastParams.maxOutputTokens + 100
* }),
* maxRetries: 3,
* backoffMs: (attempt) => attempt * 1000, // 1s, 2s, 3s
* signal: controller.signal,
* onAttempt: ({ attempt, isRetry }) =>
* logger.info(`Attempt ${attempt}${isRetry ? ' (retry)' : ''}`),
* retryOnError: (error, attempt) =>
* error instanceof RateLimitError && attempt <= 2,
* onError: (error, attempt) =>
* logger.warn(`Generation failed on attempt ${attempt}:`, error),
* onExhausted: 'throw'
* });
* ```
*/
interface RetryOptions<P, R> {
/** Function to generate results - receives params and optional AbortSignal */
generate: ((params: P, signal?: AbortSignal) => Promise<R>) | ((params: P) => Promise<R>);
/** Initial parameters for generation */
params: P;
/**
* Function to validate results - return { blocked: true } to trigger retry
* @example
* ```ts
* validate: (result) => ({
* blocked: result.text.length < 100,
* message: 'Too short',
* metadata: { actualLength: result.text.length }
* })
* ```
*/
validate: ((result: R) => Promise<{
blocked: boolean;
message?: string;
metadata?: unknown;
}>) | ((result: R) => {
blocked: boolean;
message?: string;
metadata?: unknown;
});
/**
* Function to build retry parameters based on previous attempts
* @example
* ```ts
* buildRetryParams: ({ lastParams, summary }) => ({
* ...lastParams,
* maxOutputTokens: lastParams.maxOutputTokens + 200,
* temperature: Math.min(0.9, lastParams.temperature + 0.1)
* })
* ```
*/
buildRetryParams: (args: {
summary: {
blockedResults: Array<{
message?: string;
metadata?: unknown;
}>;
totalAttempts?: number;
attempts?: Array<{
attempt: number;
result?: R;
blocked: boolean;
waitMs?: number;
}>;
};
originalParams: P;
lastParams: P;
lastResult?: R;
}) => P;
/** Maximum number of retry attempts (default: 1) */
maxRetries?: number;
/**
* Backoff delay between retries in milliseconds
* @example
* ```ts
* backoffMs: 1000 // Fixed 1 second delay
* backoffMs: (attempt) => attempt * 500 // Progressive: 500ms, 1s, 1.5s...
* ```
*/
backoffMs?: number | ((attempt: number) => number);
/**
* Optional cancellation signal - passed to generate function and honored during backoff
* @example
* ```ts
* const controller = new AbortController();
* setTimeout(() => controller.abort(), 30000); // Cancel after 30s
* signal: controller.signal
* ```
*/
signal?: AbortSignal;
/**
* Callback for each attempt (including initial) - useful for logging/metrics
* @example
* ```ts
* onAttempt: ({ attempt, isRetry, waitMs }) =>
* logger.info(`${isRetry ? 'Retry' : 'Initial'} attempt ${attempt}, waiting ${waitMs}ms`)
* ```
*/
onAttempt?: (info: RetryAttemptInfo<R>) => void;
/**
* Retry on generation errors (not just validation failures)
* @example
* ```ts
* retryOnError: (error, attempt) =>
* error instanceof RateLimitError && attempt <= 2
* ```
*/
retryOnError?: (error: unknown, attempt: number) => boolean;
/**
* Callback when generation errors occur
* @example
* ```ts
* onError: (error, attempt) =>
* logger.error(`Generation failed on attempt ${attempt}:`, error)
* ```
*/
onError?: (error: unknown, attempt: number) => void;
/**
* Behavior when max retries exhausted: 'return-last' (default) | 'throw'
* - 'return-last': Return the last generated result even if it failed validation
* - 'throw': Throw an error when retries are exhausted
*/
onExhausted?: 'return-last' | 'throw';
}
/**
* Lightweight helper for DX-friendly retries with comprehensive error handling and cancellation support
*
* @example Simple retry with helpers
* ```ts
* const result = await retry({
* generate: (params) => generateText(params),
* params: { prompt: 'Write a story', maxOutputTokens: 200 },
* validate: (result) => ({ blocked: result.text.length < 500 }),
* buildRetryParams: retryHelpers.improveResponse(300, 'Please write more detail'),
* maxRetries: 2
* });
* ```
*
* Exported for users and reused internally by guardrail middleware
*/
declare function retry<P, R>(options: RetryOptions<P, R>): Promise<R>;
interface RetryBuilderArgs<P, R> {
summary: {
blockedResults: Array<{
message?: string;
metadata?: unknown;
}>;
totalAttempts?: number;
attempts?: Array<{
attempt: number;
result: R;
blocked: boolean;
waitMs?: number;
}>;
};
originalParams: P;
lastParams: P;
lastResult: R;
}
/**
* Parameter building helpers for common retry patterns
*
* @example
* ```ts
* // Simple token increase
* buildRetryParams: retryHelpers.increaseTokens(300)
*
* // Add encouraging prompt
* buildRetryParams: retryHelpers.addEncouragingPrompt(
* 'Please be more specific and detailed.'
* )
*
* // Combine both strategies
* buildRetryParams: retryHelpers.improveResponse(300, 'Try again with more detail.')
* ```
*/
declare const retryHelpers: {
/**
* Increases max output tokens for retry attempts
*/
readonly increaseTokens: (increase?: number) => <P extends {
maxOutputTokens?: number;
}>({ lastParams, }: RetryBuilderArgs<P, unknown>) => P;
/**
* Adds encouraging prompt for retry attempts
*/
readonly addEncouragingPrompt: (encouragement?: string) => <P extends {
prompt?: unknown;
}>({ lastParams, summary, }: RetryBuilderArgs<P, unknown>) => P;
/**
* Combines token increase with encouraging prompt
*/
readonly improveResponse: (tokenIncrease?: number, encouragement?: string) => <P extends {
maxOutputTokens?: number;
prompt?: unknown;
}>(args: RetryBuilderArgs<P, unknown>) => P;
/**
* Simple parameter passthrough (no changes)
*/
readonly noChange: <P>() => ({ lastParams }: RetryBuilderArgs<P, unknown>) => P;
};
/**
* System-prompt hardening — append (or prepend) a block of defensive rules that
* establish a trust boundary between instructions and user-controlled content,
* resist prompt extraction, and anchor the model's persona. A prompt-engineering
* complement to the runtime guardrails: hardening reduces the odds an injection
* lands, guardrails catch the ones that do.
*
* Implemented as a small, dependency-free hardening helper.
*/
interface HardenOptions {
/** Omit the persona-anchor rule. */
skipPersonaAnchor?: boolean;
/** Omit the anti-extraction rules. */
skipAntiExtraction?: boolean;
/** Extra rules appended to the security block. */
customRules?: string[];
/** Where to place the security block relative to the prompt. Default `append`. */
position?: 'prepend' | 'append';
}
/**
* Wrap a system prompt with a defensive security-rules block.
*
* @example
* const system = hardenSystemPrompt('You are a financial advisor.');
* await generateText({ model, system, prompt: userInput });
*/
declare function hardenSystemPrompt(prompt: string, options?: HardenOptions): string;
/**
* Prompt-defense evaluator. Grades a system prompt against OWASP-LLM defense
* vectors (role boundary, instruction boundary, data protection, indirect
* injection, etc.) and reports which protections are missing.
*
* The inverse of `hardenSystemPrompt`: hardening *adds* defensive rules; this
* *checks* whether a prompt already has them — useful as a CI gate or a
* pre-deploy lint on your system prompts.
*
* Pure, dependency-free OWASP-LLM defense ruleset.
*/
type Severity$1 = 'critical' | 'high' | 'medium' | 'low';
interface PromptDefenseFinding {
vectorId: string;
name: string;
owasp: string;
defended: boolean;
confidence: number;
severity: Severity$1;
evidence: string;
matchedPatterns: number;
requiredPatterns: number;
}
interface PromptDefenseReport {
/** Letter grade A–F from the coverage score. */
grade: string;
/** 0-100 coverage score. */
score: number;
defended: number;
total: number;
coverage: string;
/** vectorIds with no defense found. */
missing: string[];
findings: PromptDefenseFinding[];
/** Stable non-cryptographic hash of the evaluated prompt. */
promptHash: string;
/** True when the grade is below `minGrade` (default `C`). */
isBlocking: (minGrade?: string) => boolean;
}
interface PromptDefenseOptions {
/** Restrict evaluation to these vectorIds. Defaults to all. */
vectors?: string[];
}
/**
* Evaluate a system prompt's defensive coverage against OWASP-LLM vectors.
*
* @example
* const report = evaluatePromptDefense(systemPrompt);
* if (report.isBlocking('B')) {
* throw new Error(`Prompt defenses too weak (${report.grade}): missing ${report.missing.join(', ')}`);
* }
*/
declare function evaluatePromptDefense(prompt: string, options?: PromptDefenseOptions): PromptDefenseReport;
/**
* Canonical ordinal severity scale, shared by guardrail results, plan-risk
* verdicts, and tool-approval gating. One rank table and one risk mapping so the
* ordering can't drift between subsystems.
*/
type Severity = 'low' | 'medium' | 'high' | 'critical';
/**
* Tool approval adapter for AI SDK v7's first-class `toolApproval`.
*
* The library's tool-parameter guardrails already validate a tool call by name +
* input and return a graded result. `guardrailApproval()` re-homes those
* guardrails into the `toolApproval` slot of `generateText` / `streamText` /
* `Agent`, so the same rules gain pause/resume and human-in-the-loop for free:
*
* ```ts
* import { generateText } from 'ai';
* import { guardrailApproval, sqlInjectionGuardrail, toolRBACGuardrail } from 'ai-sdk-guardrails';
*
* await generateText({
* model,
* tools,
* prompt,
* toolApproval: guardrailApproval([sqlInjectionGuardrail(), toolRBACGuardrail({ ... })]),
* });
* ```
*
* Decision mapping (first failing guardrail wins):
* - `valid: true` → `approved`
* - `valid: false`, severity ≥ `denyAtOrAbove` → `denied` (with `message` as `reason`)
* - `valid: false`, severity < `denyAtOrAbove` → `user-approval` (human-in-the-loop)
* - no guardrail matches the tool → `not-applicable`
*/
interface GuardrailApprovalOptions<TContext = Record<string, unknown>> {
/**
* Severity at or above which a blocking failure becomes an outright `denied`
* instead of escalating to human `user-approval`. Default: `'high'`.
*/
denyAtOrAbove?: Severity;
/**
* Explicit override for how a *blocking* failure maps to a decision,
* ignoring severity:
* - `'deny'` : always auto-deny
* - `'user-approval'` : always escalate to a human
* When omitted, severity decides via {@link GuardrailApprovalOptions.denyAtOrAbove}.
*/
onBlock?: 'deny' | 'user-approval';
/** Request-scoped context (user, role, session) passed to each guardrail. */
requestContext?: RequestContext<TContext>;
/** Observability hook — fired once per tool call with the final decision. */
onDecision?: (info: {
toolName: string;
toolCallId: string;
status: ToolApprovalStatus;
guardrail?: string;
result?: ToolValidationResult;
}) => void;
}
/**
* A generic tool-approval function: it reads only the `toolCall` (tool name and
* input) and is therefore assignable to the SDK's per-tool-set approval function
* for *any* tool set, via parameter contravariance. This matters because the
* SDK types `toolApproval` with `NoInfer<TOOLS>` — `TOOLS` is inferred from
* `tools`, not from the approval argument — so a `ToolSet`-defaulted return type
* would not be assignable at the call site. Keeping the signature broad lets the
* result drop straight into `generateText` / `streamText` / `ToolLoopAgent`.
*/
type GuardrailApprovalFunction = (args: {
toolCall: {
toolName: string;
toolCallId: string;
input: unknown;
};
}) => Promise<ToolApprovalStatus>;
/**
* Turn one or more tool-parameter guardrails into a v7 `toolApproval` function.
* The result is assignable directly to the `toolApproval` option of
* `generateText` / `streamText` / `ToolLoopAgent` (the recommended agent API) —
* no type arguments or casts required at the call site.
*
* Because the return value is a native `ToolApprovalConfiguration`, it composes
* with the SDK-ecosystem policy helpers in `@ai-sdk/policy-opa` for free — no
* extra coupling on either side:
*
* ```ts
* import { shadow, wrapMcpTools } from '@ai-sdk/policy-opa';
*
* const gate = guardrailApproval([sqlInjectionGuardrail({ toolName: 'executeSQL' })]);
*
* // Shadow-mode rollout: evaluate the gate and log what it WOULD do, but let
* // every call through until you flip `enforce: true`.
* const toolApproval = shadow(gate, {
* enforce: process.env.ENFORCE === 'true',
* onDecision: (e) => logger.info('guardrail.decision', e),
* });
*
* // Total coverage over a discovered MCP tool set: anything the gate does not
* // govern falls back to human approval instead of being silently allowed.
* const { tools, toolApproval } = wrapMcpTools(await mcp.tools(), gate, {
* default: 'user-approval',
* });
* ```
*
* Prefer these SDK-native helpers over re-implementing shadow mode or MCP
* fallback coverage in the guardrails layer.
*/
declare function guardrailApproval<TContext = Record<string, unknown>>(guardrails: ToolParameterGuardrail<any, TContext>[], options?: GuardrailApprovalOptions<TContext>): GuardrailApprovalFunction;
interface McpSecurityMetadata extends Record<string, unknown> {
injectionPatternsDetected?: number;
exfiltrationAttempts?: number;
suspiciousUrls?: number;
encodedContentDetected?: boolean;
cascadeRiskLevel?: 'low' | 'medium' | 'high' | 'critical';
blockedPatterns?: string[];
detectedAttacks?: Array<{
type: string;
pattern: string;
severity: 'low' | 'medium' | 'high' | 'critical';
position?: number;
}>;
}
interface McpSecurityOptions {
/** Threshold for prompt injection confidence (0-1). Default: 0.7 */
injectionThreshold?: number;
/** Maximum allowed suspicious URLs in response. Default: 0 */
maxSuspiciousUrls?: number;
/** Whether to scan for encoded content (base64, hex, etc.). Default: true */
scanEncodedContent?: boolean;
/** Whether to detect data exfiltration attempts. Default: true */
detectExfiltration?: boolean;
/** Allowed domains for URL construction. Default: [] */
allowedDomains?: string[];
/** Block responses that attempt to trigger additional tool calls. Default: true */
blockCascadingCalls?: boolean;
/** Maximum content size to analyze in bytes. Default: 51200 (50KB) */
maxContentSize?: number;
/** Minimum encoded content length to consider suspicious. Default: 20 */
minEncodedLength?: number;
/** Threshold for encoded content + injection score. Default: 0.3 */
encodedInjectionThreshold?: number;
/** High risk threshold for cascade blocking. Default: 0.5 */
highRiskThreshold?: number;
/** Additional suspicious domain patterns (regex strings) */
customSuspiciousDomains?: string[];
/** Authority manipulation detection threshold. Default: 0.7 */
authorityThreshold?: number;
}
/**
* MCP Security Guardrail - Detects malicious content in MCP tool responses
*
* This guardrail specifically addresses the "lethal trifecta" vulnerability by:
* 1. Detecting prompt injection in tool responses
* 2. Preventing data exfiltration through URL construction
* 3. Blocking cascading tool call attempts
* 4. Scanning for encoded malicious instructions
*/
declare const mcpSecurityGuardrail: (options?: McpSecurityOptions) => OutputGuardrail;
/**
* Response Sanitizer - Cleans MCP tool responses of potentially malicious content
*/
declare const mcpResponseSanitizer: () => OutputGuardrail;
/**
* MCP tool-definition scanner. Inspects an MCP tool's *definition* (name +
* description) at registration time for supply-chain threats — before the tool
* is ever exposed to the model. Complements `mcpSecurityGuardrail`, which scans
* tool *output* at runtime.
*
* Pure, dependency-free detection for MCP supply-chain threats.
*/
type McpThreatType = 'tool_poisoning' | 'typosquatting' | 'hidden_instruction' | 'rug_pull';
interface McpThreat {
type: McpThreatType;
severity: 'low' | 'medium' | 'high' | 'critical';
description: string;
evidence?: string;
}
interface McpToolDefinition {
name: string;
description: string;
parameters?: Record<string, unknown>;
}
interface McpScanResult {
toolName: string;
threats: McpThreat[];
/** 0-100, capped. */
riskScore: number;
safe: boolean;
}
interface McpToolScanOptions {
/** Well-known tool names used for typosquatting detection. Has sensible defaults. */
knownToolNames?: string[];
/** Description length over which rug-pull heuristics apply. Default 500. */
rugPullDescriptionLength?: number;
/** Instruction-pattern matches needed to flag a rug-pull. Default 2. */
rugPullMinInstructionMatches?: number;
}
/**
* Scan a single MCP tool definition for supply-chain threats.
*
* @example
* const result = scanMcpTool({ name: 'read_flie', description: 'Reads a file.' });
* if (!result.safe) console.warn(result.threats);
*/
declare function scanMcpTool(tool: McpToolDefinition, options?: McpToolScanOptions): McpScanResult;
/** Scan many MCP tool definitions; returns one result per tool. */
declare function scanMcpTools(tools: McpToolDefinition[], options?: McpToolScanOptions): McpScanResult[];
/**
* Session-scoped tool-plan accumulator for step-aware plan-risk classification.
*/
interface PlanRiskSession {
record(toolNames: string[]): string[];
reset(): void;
readonly toolSequence: readonly string[];
}
declare function createPlanRiskSession(): PlanRiskSession;
/**
* Plan-risk guardrail — a first-class **SAIF Layer-2 reasoning-based defense**.
*
* Google's *Secure AI Agents* paper warns that iterative tool-use planning is
* where rogue plans translate into real-world impact — especially the
* "untrusted read → destructive action" exfiltration chain. This guardrail
* inspects the tool calls a model proposes, runs a pluggable risk classifier
* over the proposed tool *sequence*, and blocks (or warns) when the verdict
* crosses a threshold — before the tools execute.
*
* It works standalone with a dependency-free built-in heuristic, and — when the
* optional `autotel-genai` peer is present — records the verdict as canonical
* `agent.plan.risk.*` attributes on the active span (see
* `ai-sdk-guardrails/governance`). Pass your own `classifier` to plug in a
* model-based predictor (Model Armor, Llama Guard, an LLM judge, …).
*
* ```ts
* import { withGuardrails, planRiskGuardrail } from 'ai-sdk-guardrails';
*
* const model = withGuardrails({ model: baseModel,
* outputGuardrails: [planRiskGuardrail({ blockAtOrAbove: 'high' })],
* throwOnBlocked: true,
* });
* ```
*/
type PlanRiskVerdict = PlanRiskAssessment['verdict'];
/**
* Classifies a proposed tool plan. Return `undefined` to abstain (treated as no
* risk). May be async (e.g. an LLM/Model-Armor call).
*/
type PlanRiskClassifier = (input: {
toolSequence: string[];
}) => PlanRiskAssessment | undefined | Promise<PlanRiskAssessment | undefined>;
interface PlanRiskGuardrailOptions {
/**
* Risk classifier. Defaults to {@link builtinPlanRiskClassifier} — a
* dependency-free heuristic that flags untrusted-read→destructive chains and
* over-long tool sequences. Swap in a model-based classifier for production.
*/
classifier?: PlanRiskClassifier;
/**
* Verdict at or above which the guardrail trips. Default: `'high'`.
*/
blockAtOrAbove?: PlanRiskVerdict;
/**
* Override how tool-call names are read from the result. Defaults to the
* shared heuristic extractor.
*/
toolExtractor?: (result: AIResult) => string[];
/**
* When autotel-genai is present, also emit a `llm.plan.risk.elevated` security
* event for non-`low` verdicts (in addition to the span attributes).
* Default: `false`.
*/
emitSecurityEvent?: boolean;
/**
* Accumulate tool names across agent steps so classifiers see the full running
* plan (e.g. list → upload → fetch across turns). Use {@link createPlanRiskSession}.
*/
session?: PlanRiskSession;
}
interface PlanRiskMetadata extends Record<string, unknown> {
toolSequence: string[];
verdict: PlanRiskVerdict;
score?: number;
categories?: string[];
reason?: string;
}
/**
* Dependency-free first-pass plan-risk heuristic, mirroring autotel-genai's. It
* flags a mixed untrusted-read + destructive tool plan (the exfiltration chain)
* as `high`, and long tool sequences (≥ 8) as `medium`.
*/
declare function builtinPlanRiskClassifier(): PlanRiskClassifier;
/**
* Build a plan-risk output guardrail. The guardrail extracts the proposed tool
* sequence from the model's output, classifies it, records the verdict to
* autotel-genai (best-effort, when present), and trips when the verdict reaches
* `blockAtOrAbove`.
*/
declare function planRiskGuardrail(options?: PlanRiskGuardrailOptions): OutputGuardrail<PlanRiskMetadata>;
/**
* Evaluation-scope guardrail — blocks eval agents from scope-creeping to
* external infrastructure or using shared registry storage as a message board.
*/
interface EvaluationScopeGuardrailOptions {
allowedHosts?: (string | RegExp)[];
blockedHosts?: (string | RegExp)[];
registryTools?: string[];
suspiciousFilenamePatterns?: RegExp[];
blockBase64Payloads?: boolean;
minBase64Length?: number;
/** Deny all registry/package-manager write tools (read-only eval sandbox). */
denyRegistryWrites?: boolean;
/** Trip on coordination filename patterns on registry writes. Default true. */
denySuspiciousFilenames?: boolean;
/** Tag violations with a shared store id (e.g. artifactory instance). */
sharedStoreId?: string;
}
interface EvaluationScopeMetadata extends Record<string, unknown> {
violations: string[];
observedTools: string[];
sharedStoreId?: string;
}
declare function evaluationScopeGuardrail(options?: EvaluationScopeGuardrailOptions): OutputGuardrail<EvaluationScopeMetadata>;
/**
* Budget guardrail — a single cumulative cost / token / tool-call kill-switch
* driven automatically by the guardrails pipeline.
*
* The per-request `tokenUsageLimit` / `costQuotaRails` guardrails check one
* response in isolation. A *budget* accumulates across every call in a session
* and trips once a ceiling is crossed — the same job as autotel-genai's
* `createGenAiBudget`. Rather than track cumulative spend in two places, this
* guardrail feeds each call's usage into **one** shared budget object.
*
* The {@link GuardrailBudget} interface is satisfied structurally by
* autotel-genai's `GenAiGuard` — so the canonical kill-switch (abort signal,
* `GEN_AI_GUARD_STOP`, `gen_ai.guard.*` telemetry) becomes the single source of
* truth, fed by the middleware:
*
* ```ts
* import { createGenAiBudget } from 'autotel-genai/guard';
* import { estimateLLMCost } from 'autotel-genai/cost';
* import { withGuardrails, budgetGuardrail } from 'ai-sdk-guardrails';
*
* const budget = createGenAiBudget({ maxCostUsd: 5, warnAtUsd: 4, onStop: 'abort' });
* const model = withGuardrails({ model: base,
* outputGuardrails: [
* budgetGuardrail({
* budget,
* estimateCost: (u) => estimateLLMCost('gpt-4o', u),
* }),
* ],
* });
* ```
*
* With no autotel-genai installed, use the built-in {@link createGuardrailBudget}
* — same interface, dependency-free accumulator.
*/
/** Per-step usage contribution. Mirrors autotel-genai's `GuardUsage`. */
interface BudgetUsage {
costUsd?: number;
inputTokens?: number;
outputTokens?: number;
}
/** A supervised step fed to the budget. Mirrors autotel-genai's `GenAiGuardStep`. */
interface BudgetStep {
kind?: string;
name?: string;
error?: boolean;
usage?: BudgetUsage;
}
/** Read-only accumulated budget state. A subset of autotel-genai's `GuardState`. */
interface BudgetState {
costUsd: number;
inputTokens: number;
outputTokens: number;
stepCount: number;
toolCallCount: number;
errorCount: number;
}
/**
* The minimal budget surface the guardrail drives. autotel-genai's `GenAiGuard`
* (from `createGenAiBudget` / `createGenAiGuard`) satisfies this structurally, so
* it can be passed directly — no adapter, no import from this package's side.
*/
interface GuardrailBudget {
/** Record a step and accumulate its usage. Return value is ignored here. */
record(step: BudgetStep): unknown;
/** `true` once a stop ceiling has been crossed. */
readonly stopped: boolean;
/** Current accumulated totals. */
readonly state: BudgetState;
}
interface CreateGuardrailBudgetOptions {
/** Hard cumulative cost ceiling in USD. */
maxCostUsd?: number;
/** Hard cumulative token ceiling (input + output). */
maxTokens?: number;
/** Hard cumulative tool-call ceiling. */
maxToolCalls?: number;
}
/**
* Dependency-free cumulative budget — the standalone counterpart to
* autotel-genai's `createGenAiBudget`. Accumulates cost / tokens / tool calls
* and flips {@link GuardrailBudget.stopped} once a ceiling is crossed.
*/
declare function createGuardrailBudget(options?: CreateGuardrailBudgetOptions): GuardrailBudget;
interface BudgetMetadata extends Record<string, unknown> {
costUsd: number;
inputTokens: number;
outputTokens: number;
stepCount: number;
stopped: boolean;
}
interface BudgetGuardrailOptions {
/** The shared budget to feed and check. autotel-genai's `GenAiGuard` fits. */
budget: GuardrailBudget;
/** Estimate USD cost for this call from its token usage. */
estimateCost?: (usage: {
inputTokens?: number;
outputTokens?: number;
}) => number | undefined;
/** Trip the guardrail once the budget has stopped. Default: `true`. */
blockOnStop?: boolean;
/** Custom block message. */
message?: string;
}
/**
* Output guardrail that records each response's usage into a shared
* {@link GuardrailBudget} and trips once the budget stops. This makes the budget
* the single cumulative source of truth — no double-tracking against a
* separately-driven kill-switch.
*
* An autotel-genai `GenAiGuard` configured with `onStop: 'throw'` will throw
* from `record()` when a ceiling is crossed; the guardrail catches that and
* converts it into a normal block, so it composes with `throwOnBlocked`,
* `onOutputBlocked`, and the `governance` option.
*/
declare function budgetGuardrail(options: BudgetGuardrailOptions): OutputGuardrail<BudgetMetadata>;
export { AIResult, type AgentGuardrailsConfig, type AgentGuardrailsFragments, type BudgetGuardrailOptions, type BudgetMetadata, type BudgetState, type BudgetStep, type BudgetUsage, type CreateGuardrailBudgetOptions, type EvaluationScopeGuardrailOptions, type EvaluationScopeMetadata, type GuardrailApprovalFunction, type GuardrailApprovalOptions, type GuardrailBudget, GuardrailConfigurationError, GuardrailExecutionError, GuardrailExecutionSummary, GuardrailResult, GuardrailTimeoutError, GuardrailValidationError, GuardrailsError, GuardrailsInputError, GuardrailsOutputError, type HardenOptions, InputGuardrail, InputGuardrailContext, InputGuardrailsMiddlewareConfig, Logger, type McpScanResult, type McpSecurityMetadata, type McpSecurityOptions, type McpThreat, type McpThreatType, type McpToolDefinition, type McpToolScanOptions, MiddlewareError, NormalizedGuardrailContext, OutputGuardrail, OutputGuardrailContext, OutputGuardrailsMiddlewareConfig, PlanRiskAssessment, type PlanRiskClassifier, type PlanRiskGuardrailOptions, type PlanRiskMetadata, type PlanRiskSession, type PlanRiskVerdict, type PromptDefenseFinding, type PromptDefenseOptions, type PromptDefenseReport, RequestContext, type RetryAttemptInfo, type RetryBuilderArgs, type RetryOptions, ToolParameterGuardrail, ToolValidationResult, agentGuardrails, budgetGuardrail, builtinPlanRiskClassifier, createGuardrailBudget, createGuardrails, createInputGuardrail, createOutputGuardrail, createPlanRiskSession, defineInputGuardrail, defineOutputGuardrail, evaluatePromptDefense, evaluationScopeGuardrail, executeInputGuardrails, executeOutputGuardrails, extractErrorInfo, guardrailApproval, hardenSystemPrompt, isGuardrailsError, mcpResponseSanitizer, mcpSecurityGuardrail, normalizeGuardrailContext, planRiskGuardrail, retry, retryHelpers, scanMcpTool, scanMcpTools, withGuardrails };