import { Context } from 'effect'; import { Effect } from 'effect'; import { generateObject } from '@voltro/ai'; import { Schema } from 'effect'; import { VoltroPlugin } from '@voltro/protocol'; import { YieldableError } from 'effect/Cause'; /** AI moderation via @voltro/ai (lazy, OPTIONAL dependency). Uses a structured * classification. `threshold` (0..1) decides flagged from the model's max * category score. * * Two distinct failure modes — the distinction matters because a silent * false-pass is the worst error class for a security control: * - `@voltro/ai` NOT INSTALLED (import rejects) → a configuration error. * Warn ONCE (so "all content silently passes" is never silent), fail open. * - per-call model error / outage → fail open WITHOUT warning (a transient * outage must not block every write, and would spam the log). */ export declare const aiProvider: (opts?: AiProviderOptions) => ModerationProvider; declare interface AiProviderOptions { readonly threshold?: number; readonly categories?: ReadonlyArray; /** Importer seam for the optional `@voltro/ai` dependency — defaults to the * real lazy import. Tests inject a stub to drive the verdict logic AND the * missing-dependency branch deterministically (mirrors @voltro/ai's own * injectable gatewayProvider). */ readonly load?: () => Promise<{ readonly generateObject: typeof generateObject; }>; } /** Raised when a `block`-action rule flags the content — the write never * commits. Merged into every procedure's wire-error union (typed on the client). */ export declare class ContentRejected extends ContentRejected_base { } declare const ContentRejected_base: Schema.Class; readonly reason: Schema.String; }>, YieldableError>; /** A flagged-content item awaiting review (the dashboard queue). */ export declare interface FlaggedItem { readonly id: string; readonly tag: string; readonly categories: ReadonlyArray; readonly reason: string; readonly subjectId: string | null; readonly at: string; status: 'pending' | 'confirmed' | 'dismissed'; } /** Zero-dep denylist matcher. Case-insensitive; whole-word where the term has * word edges (see {@link termMatcher}). */ export declare const keywordProvider: (deny: ReadonlyArray) => ModerationProvider; /** Moderate arbitrary text in a handler (e.g. to redact before writing) — the * fine-grained escape the rpc interceptor can't do (it can't rewrite input). * Reads the configured provider off `ModerationService`; never throws. */ export declare const moderate: (text: string) => Effect.Effect; export declare const moderationPlugin: (options: ModerationPluginOptions) => VoltroPlugin; export declare interface ModerationPluginOptions { readonly provider: ModerationProvider; readonly rules: ReadonlyArray; /** Called when a `flag`-action rule (or any flagged content) is seen. */ readonly onFlag?: (info: { tag: string; verdict: ModerationVerdict; }) => void; /** * Namespace for this plugin's inspect endpoints. Default `moderation`. * * Set it when your app already publishes under that name — an exact tag * collision is fatal at codegen, and this is the way out. Orthogonal to * `name` below: `alias` REPLACES the namespace, `name` distinguishes two * installations within it. * * The dashboard panel follows: the plugin's inspect endpoints keep a mount * under its CANONICAL name alongside the aliased one, so aliasing does not * take the panel away. The one case it cannot cover is two installs of this * plugin — one canonical name, two panels — which get no shared mount at * all, on purpose. Read `inspectSlug` from `/_voltro/inspect/plugins` to * reach a specific install. */ readonly alias?: string; /** * Discriminator for a SECOND installation of this plugin, when one app runs * two (`@voltro/plugin-moderation#analytics`). Not a rename — for that use `alias`. */ readonly name?: string; } export declare type ModerationProvider = (text: string) => Effect.Effect; export declare interface ModerationRule { /** rpc tag (exact) or RegExp on the tag. */ readonly match: string | RegExp; /** Input field keys whose string values are concatenated + moderated. */ readonly fields: ReadonlyArray; /** `block` (default) fails the call; `flag` allows + records. */ readonly action?: 'block' | 'flag'; } /** `Context.Service` for the moderation service — `yield* ModerationService` in any * handler once `moderationPlugin(...)` is wired. The handler depends on the * Tag; the plugin owns the provider-backed implementation (no module-level * global). Mirrors `@voltro/plugin-mail`'s `MailService`. */ export declare class ModerationService extends ModerationService_base { } declare const ModerationService_base: Context.ServiceClass; /** The moderation runtime surface a handler reads to classify arbitrary text. * Backed by the configured provider; contributed once per app by * `moderationPlugin(...)` via `services`, mirroring `MailService` / * `StorageService`. */ export declare interface ModerationServiceShape { /** Classify text against the configured provider. Never fails. */ readonly moderate: (text: string) => Effect.Effect; } export declare interface ModerationVerdict { /** True when the content violates policy. */ readonly flagged: boolean; /** Matched categories (e.g. 'hate', 'sexual', 'self-harm', or a keyword). */ readonly categories?: ReadonlyArray; /** Highest category score 0..1 (AI providers). */ readonly score?: number; readonly reason?: string; } export { }