@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
161 lines • 7.07 kB
TypeScript
/**
* Guardrails: detectors that look at text on its way to or from a model — prompts, answers, tool
* arguments, tool results, tool descriptions — and rules that decide what to do when they fire.
*
* Nothing in this module depends on the agent loop, on NestJS or on a runtime API beyond the
* language itself. A process that proxies raw provider traffic uses the detectors, `scan`, `Vault`
* and `StreamGuard` directly; `createGuardrails` is the adapter onto the loop's processor seams.
*/
/** Where in the traffic a rule applies. */
export type GuardrailStage =
/** Prompt + attached context sent to a model (system, user, assistant history). */
'llm_request'
/** What the model answered (text and tool-call arguments), streaming included. */
| 'llm_response'
/** Arguments of a tool call, before the tool runs. */
| 'tool_args'
/** What a tool returned, before it flows back into the model (indirect prompt injection). */
| 'tool_result'
/** Tool descriptions and schemas of a remote tool server (tool poisoning). */
| 'tool_description';
export declare const GUARDRAIL_STAGES: readonly GuardrailStage[];
/**
* - `allow`: exemption. When it matches (its detectors fire, or it has none), later rules are skipped.
* - `log`: record the hit only (a "flag").
* - `redact`: replace what was found by placeholders (reversible for values sent to a model).
* - `approve`: human-in-the-loop — refuse until someone approves this exact content. A caller that
* has no approval flow of its own treats it as `block`.
* - `block`: refuse with the rule's user-facing message.
*/
export type GuardrailAction = 'allow' | 'log' | 'redact' | 'approve' | 'block';
/** Severity order used to combine several rules: the most severe wins. */
export declare const ACTION_SEVERITY: Record<GuardrailAction, number>;
export type PiiType = 'email' | 'phone' | 'cpf' | 'cnpj' | 'ssn' | 'credit_card' | 'iban' | 'ip_address';
export declare const PII_TYPES: readonly PiiType[];
export type SecretType = 'openai_key' | 'anthropic_key' | 'aws_access_key' | 'aws_secret_key' | 'github_token' | 'gitlab_token' | 'slack_token' | 'google_api_key' | 'stripe_key' | 'jwt' | 'bearer_token' | 'private_key' | 'generic_secret';
export declare const SECRET_TYPES: readonly SecretType[];
/** Something a detector found. `value` is the matched text; keep it in-process. */
export interface Finding {
/** The detector kind (or a custom detector's name). */
detector: string;
/** e.g. `pii.credit_card`, `secret.aws_access_key`, `injection.ignore_previous`. */
category: string;
start: number;
end: number;
score: number;
value: string;
/**
* Whether the finding can be redacted by replacing its span. `false` for whole-text verdicts
* (a moderation or classifier call), which replace the whole segment when redacting.
*/
spanned?: boolean;
/**
* What a redaction puts in place of this span. Undefined → a vault placeholder (`[EMAIL_1]`).
* Detectors whose finding is an instruction rather than a value (injection, tool poisoning) set
* this so the text is removed rather than tokenized.
*/
replacement?: string;
}
/** Who wrote a piece of text (model request stage). */
export type SegmentSource = 'system' | 'user' | 'assistant' | 'tool';
/** A piece of text to scan, with where it came from. */
export interface Segment {
text: string;
source?: SegmentSource;
/**
* Part of the newest turn. Content that is only in re-sent history is acted upon (redacted
* one-way instead of blocked) but its hits are reported as not fresh. Undefined → fresh.
*/
fresh?: boolean;
}
/**
* What the traffic is about. `stage` is the only field the engine reads itself; `tool` feeds the
* `match.tools` globs. Extend it with whatever your rules' `when` predicates need (tenant, actor,
* model, destination…).
*/
export interface GuardContext {
stage: GuardrailStage;
/** Tool name, on tool stages. */
tool?: string;
}
/** A detector that runs your own code — NER, a classifier, a moderation endpoint, an LLM judge. */
export interface CustomDetector<C extends GuardContext = GuardContext> {
kind: 'custom';
/** Identifies the detector in findings and hits (`detector`). Also its memoization key. */
name: string;
detect(text: string, ctx: C): Finding[] | Promise<Finding[]>;
}
/** A detector as configured on a rule. */
export type DetectorSpec<C extends GuardContext = GuardContext> =
/** Regex + checksum validators (Luhn, CPF/CNPJ mod 11, IBAN mod 97…). */
{
kind: 'pii';
types?: readonly PiiType[];
}
/** API keys, tokens and private keys (patterns + entropy). */
| {
kind: 'secrets';
types?: readonly SecretType[];
}
/** Prompt-injection / jailbreak heuristics (EN, PT-BR, ES; hidden Unicode; encoded payloads). */
| {
kind: 'injection';
threshold?: number;
}
/** Suspicious instructions hidden in tool descriptions (tool poisoning). */
| {
kind: 'tool_poisoning';
threshold?: number;
}
/** Your own regular expressions (`(?i)` prefix = case-insensitive). */
| {
kind: 'regex';
patterns: readonly string[];
label?: string;
}
/** Words or phrases (case- and accent-insensitive, whole words). */
| {
kind: 'keywords';
words: readonly string[];
label?: string;
} | CustomDetector<C>;
export type DetectorKind = DetectorSpec['kind'];
/** Scope a rule to part of the traffic. Every matcher that is set must match. */
export interface GuardrailMatch {
/** Model request stage: which messages are scanned (default all). */
sources?: readonly SegmentSource[];
/** Tool stages: tool-name globs (`*` any run, `?` one character). Never matches model traffic. */
tools?: readonly string[];
}
export interface GuardrailOptions {
/** Shown to the person when the rule blocks or asks for approval. */
message?: string;
/** What happens when a detector throws: `open` = ignore (default), `closed` = block. */
failMode?: 'open' | 'closed';
/**
* `redact` on `llm_request`: put the original values back into the answer the caller receives
* (the model only ever sees placeholders). Default true.
*/
restore?: boolean;
}
export interface GuardrailRule<C extends GuardContext = GuardContext> {
id: string;
/** Shown in hits and decisions. Default: `id`. */
name?: string;
/** Ascending order of evaluation; ties by name. Default 100. */
priority?: number;
/** Default true. */
enabled?: boolean;
stages: readonly GuardrailStage[];
/** Empty → the rule fires on scope alone (an exemption, or a blanket block). */
detectors: readonly DetectorSpec<C>[];
action: GuardrailAction;
match?: GuardrailMatch;
/**
* Anything scope-like the engine does not know about — the tenant, the actor's roles, the model
* or its destination. Returning false leaves the rule out of this scan.
*/
when?: (ctx: C) => boolean;
options?: GuardrailOptions;
}
//# sourceMappingURL=types.d.ts.map