import { M as Message } from './message-CyXbT7Zj.js'; import 'json-schema'; interface PIIRule { name: string; /** Pattern to match. Use global flag for full replacement. */ pattern: RegExp; /** * Replacement — either a literal string or a function that receives * the matched substring and returns the redacted value. */ replacer?: string | ((match: string) => string); } interface PIIRedactionMatch { offset: number; length: number; } interface PIIRedactionHit { rule: string; count: number; matches: PIIRedactionMatch[]; } interface PIIRedactionResult { value: TPayload; hits: PIIRedactionHit[]; } interface PIIRedactor { redact: (input: string) => PIIRedactionResult; redactMessages: (messages: Message[]) => PIIRedactionResult; } /** * Default PII patterns — email, phone (US + E.164), SSN, IPv4, credit-card, * and RFC 4122 UUIDs. Every pattern is a reasonable baseline, none is * regulator-grade. For high-stakes use, layer a model-based classifier * or a commercial PII detector on top. */ declare const DEFAULT_PII_RULES: PIIRule[]; /** * Build a regex-based PII redactor. Every `redact` call returns both * the cleaned string and a per-rule hit count — plug into observability * so you can see *which* rules are firing without inspecting payloads. */ declare function createPIIRedactor(options?: { rules?: PIIRule[]; }): PIIRedactor; /** * JSON-friendly form of a PIIRule. Patterns become a string + flags so * the taxonomy can be loaded from a manifest file (or fetched from a * remote source) without `eval`. */ interface PIITaxonomyEntry { name: string; /** Regex source (the body, no slashes). */ pattern: string; /** Regex flags. Defaults to `g` if omitted. The `g` flag is required and added if missing. */ flags?: string; /** Literal replacement string. If omitted, defaults to `[REDACTED_]`. */ replacer?: string; /** Optional human-readable description shown in lint reports / dashboards. */ description?: string; } interface PIITaxonomy { /** Schema version. Must be `'1'`. */ version: '1'; /** Optional taxonomy id (e.g. `'us-baseline'`, `'br-lgpd'`) — purely informational. */ id?: string; rules: PIITaxonomyEntry[]; } interface TaxonomyValidationIssue { /** Rule index in the input array (-1 for top-level / shape errors). */ index: number; /** Field path (e.g. `'rules[3].pattern'`). */ path: string; message: string; } interface TaxonomyValidationResult { ok: boolean; issues: TaxonomyValidationIssue[]; } /** * Pure-data validation. Does not throw — returns the full list of * issues so a CLI can present them all at once. */ declare function validatePIITaxonomy(input: unknown): TaxonomyValidationResult; /** * Compile a validated taxonomy into the runtime `PIIRule[]` shape used * by `createPIIRedactor`. Throws if the taxonomy fails validation — * call `validatePIITaxonomy` first if you want to surface every issue. */ declare function compilePIITaxonomy(taxonomy: PIITaxonomy): PIIRule[]; /** JSON Schema (Draft 7) for the taxonomy file — exported so tooling can publish it. */ declare const PII_TAXONOMY_JSON_SCHEMA: { readonly $schema: "http://json-schema.org/draft-07/schema#"; readonly $id: "https://www.agentskit.io/schemas/pii-taxonomy/v1.json"; readonly title: "AgentsKit PII Taxonomy"; readonly type: "object"; readonly required: readonly ["version", "rules"]; readonly additionalProperties: false; readonly properties: { readonly version: { readonly const: "1"; }; readonly id: { readonly type: "string"; }; readonly rules: { readonly type: "array"; readonly items: { readonly type: "object"; readonly required: readonly ["name", "pattern"]; readonly additionalProperties: false; readonly properties: { readonly name: { readonly type: "string"; readonly pattern: string; }; readonly pattern: { readonly type: "string"; readonly minLength: 1; }; readonly flags: { readonly type: "string"; }; readonly replacer: { readonly type: "string"; }; readonly description: { readonly type: "string"; }; }; }; }; }; }; /** * Reveal-by-role flow for PII that must be recoverable. Where the * default `createPIIRedactor` produces irreversible `[REDACTED_*]` * tags, `tokenize()` stores the original keyed by an opaque token in * a `RedactionVault`. `reveal()` looks the originals back up only * when an actor matches the configured `allowedRoles`. * * The vault contract is one interface with three methods so it can * be backed by anything append-only: in-memory (this module), KMS- * encrypted blob storage, HSM, etc. Originals never live on the write * path the agent / model can see. * * Closes the reveal-by-role half of issue #791. */ interface RevealActor { /** Stable identity (email, OIDC subject, service-account name). */ id: string; /** Roles granted to this actor. Match against `allowedRoles` on reveal. */ roles: string[]; } interface VaultEntry { /** ISO 8601 timestamp the value was tokenized. */ storedAt: string; /** Originally redacted text. */ plaintext: string; /** Roles permitted to reveal. Empty array means no actor can reveal. */ allowedRoles: string[]; /** Opaque metadata to help auditors correlate (request id, tenant id). */ metadata?: Record; } interface RedactionVault { put: (token: string, entry: VaultEntry) => Promise; /** Returns the entry with no role-check; reveal() does the check. */ get: (token: string) => Promise; delete?: (token: string) => Promise; } interface RedactionAuditEvent { type: 'pii:redact' | 'pii:reveal' | 'pii:reveal-denied'; /** ISO 8601 timestamp. */ at: string; /** Number of distinct tokens involved. */ tokens: number; /** Per-rule hit counts (only for redact events). */ rules?: Array<{ rule: string; count: number; }>; /** Actor id for reveal events. */ actor?: string; /** Optional opaque correlation id. */ context?: Record; } type RedactionAuditSink = (event: RedactionAuditEvent) => void | Promise; interface TokenizeOptions { /** * Rules driving the match. Same shape as `PIIRedactor`'s rules; pass * `DEFAULT_PII_RULES` for the baseline set or `compilePIITaxonomy(...)` * for a custom JSON taxonomy. * * Tokenize walks the rules directly against the input rather than * post-processing a redactor's output — this keeps the algorithm * correct for adjacent matches, PII whose value collides with the * surrounding literal text, and custom replacers that don't emit * the bracketed `[REDACTED_*]` form. */ rules: PIIRule[]; vault: RedactionVault; /** Roles allowed to reveal the originals. */ allowedRoles: string[]; /** Optional audit sink — receives one `pii:redact` event per call. */ audit?: RedactionAuditSink; /** Per-call correlation metadata threaded into both vault + audit. */ context?: Record; } interface RevealOptions { vault: RedactionVault; actor: RevealActor; /** Optional audit sink — receives `pii:reveal` or `pii:reveal-denied`. */ audit?: RedactionAuditSink; context?: Record; } /** * Replace every PII match in `input` with an opaque `<>` * marker, storing the original in the vault keyed by the marker. * Emits one `pii:redact` audit event per call. * * Walks the supplied rules against the input directly: * 1. collect every match with its [start, end) interval + rule name * 2. sort by start, drop overlaps (earlier rule wins) * 3. rebuild the output by interleaving literal slices with tokens * * Correct on adjacent matches, on PII whose value collides with the * surrounding literal text, and for custom replacers — the algorithm * never reads the redactor's substituted output. */ declare function tokenize(input: string, options: TokenizeOptions): Promise<{ value: string; tokens: string[]; }>; /** * Replace every `<>` in `input` with the original from the * vault, but only for tokens whose `allowedRoles` overlap with the * actor's roles. Tokens the actor cannot reveal are left in place * unchanged. Emits at most one `pii:reveal` event (when something was * revealed) and at most one `pii:reveal-denied` (when something was * denied) per call. * * **Security note on deny counts.** The number of tokens that existed * in the vault but were not revealable by this actor is intentionally * *not* returned to the caller. Surfacing it back to the triggering * actor would let them probe arbitrary token IDs to learn which exist * in the vault (token-existence oracle). The count is delivered only * through the audit sink, which should be routed to operators rather * than back into the calling actor's response. */ declare function reveal(input: string, options: RevealOptions): Promise<{ value: string; revealed: number; }>; /** * Process-local in-memory vault. Suitable for tests and single-node * deployments. Production should back the vault with KMS-encrypted * blob storage so plaintext never lives unencrypted at rest. */ declare function createInMemoryRedactionVault(): RedactionVault; /** * Fence untrusted content before embedding it in a prompt. The companion to * `createInjectionDetector` (which DETECTS attacks): this MITIGATES them by * wrapping attacker-influenced text (a fetched web page, a pasted document, a PR * diff, a user message) in a unique per-call sentinel and telling the model that * everything inside is DATA to process, never instructions to follow. * * ```ts * import { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security' * const skill = { systemPrompt: `${BASE}\n\n${UNTRUSTED_CONTENT_DIRECTIVE}` } * const task = `Review this document:\n${fenceUntrustedContent(doc)}` * ``` */ /** Prepend to a system prompt when the task contains fenced untrusted content. */ declare const UNTRUSTED_CONTENT_DIRECTIVE: string; interface FenceOptions { /** Human label shown in the marker, e.g. 'WEB PAGE', 'DOCUMENT'. Default 'INPUT'. */ label?: string; /** Provide a fixed marker id (e.g. for snapshot tests). Default: random per call. */ id?: string; } /** * Wrap `content` in a sentinel pair. The id is random per call (unless provided), * so content inside cannot close the fence early and inject trailing instructions. */ declare function fenceUntrustedContent(content: string, opts?: FenceOptions): string; interface InjectionHeuristic { name: string; pattern: RegExp; /** Weight contribution when pattern matches. Range ~0..1. */ weight: number; } interface InjectionVerdict { score: number; blocked: boolean; hits: Array<{ name: string; weight: number; }>; source: 'heuristic' | 'classifier' | 'hybrid'; } interface InjectionDetectorOptions { /** Threshold above which `blocked = true`. Default 0.7. */ threshold?: number; /** Extra or replacement heuristics. */ heuristics?: InjectionHeuristic[]; /** * External classifier — Llama Guard, Prompt Guard, Rebuff, any HTTP * moderation endpoint. When provided, its score is blended with * the heuristic score (max of the two). */ classifier?: (input: string) => Promise | number; } interface InjectionDetector { check: (input: string) => Promise; } /** * **Defense-in-depth, not a moat.** These heuristics catch obvious * English-language injections — they do not stop a determined attacker * who paraphrases, encodes, translates, or splits the payload across * turns. Always combine with at least one of: * * - a model classifier (Llama Guard, Prompt Guard, Rebuff, an HTTP * moderation endpoint) wired through `InjectionDetectorOptions.classifier` * - tool-call gating (`@agentskit/sandbox` mandatory sandbox + allow/deny) * - PII/secret redaction (`@agentskit/core/security` vault + redactor) * - audit + rate-limit on the calling actor * * Example with a classifier: * ```ts * import { createInjectionDetector } from '@agentskit/core/security' * * const detector = createInjectionDetector({ * threshold: 0.7, * classifier: async input => { * const r = await fetch('https://guard.example.com/score', { * method: 'POST', * body: JSON.stringify({ input }), * }) * const { score } = await r.json() as { score: number } * return score * }, * }) * * const verdict = await detector.check(userMessage) * if (verdict.blocked) throw new Error('injection blocked') * ``` * * Default heuristics aimed at the classic prompt-injection families: * instruction override, system-prompt leakage, tool-call smuggling, * role confusion, and policy-bypass attempts. Curated — not * exhaustive; pair with a model classifier for production. */ declare const DEFAULT_INJECTION_HEURISTICS: InjectionHeuristic[]; /** * Build a detector that scores input against heuristics and (optionally) * a model classifier. The returned `verdict.score` is the max of both * sources, so a single strong signal flags the request. */ declare function createInjectionDetector(options?: InjectionDetectorOptions): InjectionDetector; interface RateLimitBucket { /** Tokens available per window. */ capacity: number; /** Tokens refilled per `windowMs`. */ refill: number; /** Refill interval in ms. */ windowMs: number; } interface RateLimitDecision { allowed: boolean; remaining: number; /** Milliseconds until the next token is available (0 when allowed). */ retryAfterMs: number; key: string; bucket: string; } interface RateLimiterOptions { /** Extract the key to bucket against — user id, IP, API key, etc. */ keyOf: (context: TContext) => string; /** Buckets keyed by name — caller selects via `bucketOf`. */ buckets: Record; /** Pick the bucket for a given context. Default: 'default'. */ bucketOf?: (context: TContext) => string; /** Clock override for tests. */ now?: () => number; /** * Maximum distinct (bucket, key) pairs tracked. Oldest-touched entry * is evicted on overflow so a flood of unique keys cannot grow the * in-memory map without bound. Default 100_000. */ maxEntries?: number; /** * Drop any bucket entry that has been idle (no `check`) for longer * than this. Default 1 hour. Idle drop happens lazily on `check`, * so there is no background timer. */ ttlMs?: number; } interface RateLimiter { check: (context: TContext) => RateLimitDecision; /** Drop bucket state for a specific key (e.g. on logout). */ reset: (key: string) => void; /** Current state snapshot — tests + dashboards. */ inspect: () => Array<{ key: string; bucket: string; tokens: number; }>; } /** * Token-bucket rate limiter. Per-key state is in-memory — for * multi-process deployments, swap in a Redis-backed implementation * with the same `RateLimiter` contract. * * Memory is bounded: * - `maxEntries` caps distinct (bucket, key) pairs; oldest-touch entry * is evicted on overflow so a stream of unique keys can't grow the * map past the cap. * - `ttlMs` drops entries that have been idle longer than the window * lazily on `check`. */ declare function createRateLimiter(options: RateLimiterOptions): RateLimiter; /** * SSO helpers for production AgentsKit deployments. Audit log and * multi-tenant cost-guard already shipped; this fills in the * authentication half — verifying OIDC ID tokens issued by your IdP * (Okta, Auth0, Azure AD, Keycloak, Cognito) so a runtime can map an * inbound request to a tenant. * * Pure, dependency-free: signature verification uses WebCrypto * (`crypto.subtle`), available in Node 18+ and every modern browser / * edge runtime. SAML is included as a parser stub — full SAML * verification needs an XML/XML-DSig library, so the contract here is * "bring your own validator" with a typed shape. * * Closes part of issue #203 (SSO half). */ interface OidcVerifierOptions { /** Expected `iss` claim. Required. */ issuer: string; /** Expected `aud` claim — string or one of multiple acceptable audiences. */ audience: string | string[]; /** * JWKS URL. If omitted, derived from issuer as * `${issuer}/.well-known/jwks.json`. Override when your IdP uses a * non-standard path. */ jwksUrl?: string; /** * Cache TTL for JWKS keys, ms. Default 1h. JWKS rotation is rare; * the cache also bounds outbound traffic from a busy runtime. */ jwksTtlMs?: number; /** * Allowed clock skew in seconds when checking `exp` / `nbf`. * Default 30s — tolerates routine NTP drift across regions. */ clockSkewSeconds?: number; /** Custom fetch (testing / runtime injection). */ fetch?: typeof fetch; } interface OidcClaims { iss: string; sub: string; aud: string | string[]; exp: number; iat: number; nbf?: number; /** IdP-specific tenant claim. Common keys: `tid`, `org_id`, `tenant`. */ [claim: string]: unknown; } interface OidcVerifier { /** Verify a JWT. Throws on invalid signature, claims, or expiry. */ verify: (token: string) => Promise; /** Force a JWKS refresh (useful after a known IdP key rotation). */ refreshJwks: () => Promise; } declare function createOidcVerifier(options: OidcVerifierOptions): OidcVerifier; interface SamlAttribute { name: string; values: string[]; } interface SamlAssertion { /** SAML NameID — usually the user's stable identifier. */ subject: string; /** IdP entity id (`Issuer` element). */ issuer: string; /** Audience restriction — your SP entity id. */ audience: string; /** ISO timestamps. */ notBefore?: string; notOnOrAfter: string; attributes: SamlAttribute[]; } interface SamlVerifierOptions { /** Expected `Issuer` (IdP entity id). */ issuer: string; /** Expected audience (SP entity id). */ audience: string; /** * X.509 cert (PEM) that signs the IdP's assertions. Required — * AgentsKit does not parse SAML metadata XML. */ signingCertPem: string; /** Allowed clock skew in seconds. Default 30. */ clockSkewSeconds?: number; } interface SamlVerifier { /** * Verify a parsed SAML assertion. Signature verification is * delegated to your SAML library (`samlify`, `node-saml`, etc.) — * pass the parsed shape here for the AgentsKit-side claim checks. */ verifyClaims: (assertion: SamlAssertion) => void; /** Reusable claim extraction. */ extractTenant: (assertion: SamlAssertion, attributeName: string) => string | undefined; } declare function createSamlVerifier(options: SamlVerifierOptions): SamlVerifier; export { DEFAULT_INJECTION_HEURISTICS, DEFAULT_PII_RULES, type FenceOptions, type InjectionDetector, type InjectionDetectorOptions, type InjectionHeuristic, type InjectionVerdict, type OidcClaims, type OidcVerifier, type OidcVerifierOptions, type PIIRedactionHit, type PIIRedactionMatch, type PIIRedactionResult, type PIIRedactor, type PIIRule, type PIITaxonomy, type PIITaxonomyEntry, PII_TAXONOMY_JSON_SCHEMA, type RateLimitBucket, type RateLimitDecision, type RateLimiter, type RateLimiterOptions, type RedactionAuditEvent, type RedactionAuditSink, type RedactionVault, type RevealActor, type RevealOptions, type SamlAssertion, type SamlAttribute, type SamlVerifier, type SamlVerifierOptions, type TaxonomyValidationIssue, type TaxonomyValidationResult, type TokenizeOptions, UNTRUSTED_CONTENT_DIRECTIVE, type VaultEntry, compilePIITaxonomy, createInMemoryRedactionVault, createInjectionDetector, createOidcVerifier, createPIIRedactor, createRateLimiter, createSamlVerifier, fenceUntrustedContent, reveal, tokenize, validatePIITaxonomy };