UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

153 lines • 8.26 kB
import type { StandardSchemaV1 } from '@standard-schema/spec'; import type { RolesPolicy } from './spi/roles-policy.js'; import type { AiToolCtx, ToolDescribeScope, ToolHandler, ToolInputPreview, ToolInputPreviewScope, ToolPreflightResult } from './spi/tool.js'; import type { Actor, ToolDefinition, ToolSpec } from './types.js'; /** Thrown when an actor invokes a tool their role is not allowed. */ export declare class ToolForbiddenError extends Error { readonly toolName: string; constructor(toolName: string); } /** * Thrown when a registered tool is invoked while this deployment has it turned off (`enabled` / * `isEnabled()`). Distinct from {@link ToolForbiddenError}, which is about the actor, and from * {@link ToolNotFoundError}, which is about a name nobody registered — an operator reading a log * needs to tell "you flipped the flag" apart from "that tool does not exist in this build". * * Reachable in normal operation, not just from a forged call: a HITL `action` approved before the * flag was turned off runs its tool afterwards. */ export declare class ToolDisabledError extends Error { readonly toolName: string; constructor(toolName: string); } /** Thrown when a tool is invoked that was never registered. */ export declare class ToolNotFoundError extends Error { readonly toolName: string; constructor(toolName: string); } /** Thrown when a tool's input fails its Standard Schema validation. */ export declare class ToolInputInvalidError extends Error { readonly toolName: string; readonly issues: readonly StandardSchemaV1.Issue[]; constructor(toolName: string, issues: readonly StandardSchemaV1.Issue[]); } export declare class ToolPreflightDeniedError extends Error { readonly toolName: string; readonly reason: string; constructor(toolName: string, reason: string); } /** Trusted preparation or a fresh schema parse changed the approved JSON input. */ export declare class ToolInputDriftError extends Error { readonly toolName: string; constructor(toolName: string); } /** * Holds every registered tool and gates invocation. * * Note: `definitionsFor` returns NEUTRAL definitions (no `execute`). The agent loop runs * each tool itself as a (durable) step — so even read tools are not auto-executed by the * model. `action` tools additionally require HITL approval before the loop runs them. */ export declare class ToolRegistry { private readonly entries; register(spec: ToolSpec, handler: ToolHandler): void; /** * Drop a tool, so it is neither offered to the model nor invocable. Returns whether there was one. * * For a tool whose EXISTENCE is not decided here: an MCP server may stop exporting a tool between * one `tools/list` and the next, and leaving the old spec registered would go on offering the * model a tool whose next call fails on somebody else's machine. Whoever registered a name is * responsible for deciding when it is gone — nothing in the registry itself expires. */ unregister(name: string): boolean; has(name: string): boolean; hasPresentation(name: string): boolean; /** Used by a journaled already-completed preflight; never executes the domain handler. */ presentResult(name: string, output: unknown, ctx: AiToolCtx): Promise<void>; /** * How a streamed call of `name` is previewed while its input arrives (`ToolHandler.previewInput`), * or `undefined`. A preview that fails to build is no preview: it never fails the turn. */ previewInput(name: string, scope: ToolInputPreviewScope): Promise<ToolInputPreview | undefined>; spec(name: string): ToolSpec | undefined; allSpecs(): ToolSpec[]; /** * The tools to offer the model for this actor+agent, after the four filter layers: what this * agent pinned, what this deployment has enabled, what this actor's role allows, and what each * tool's own `canUse` allows this actor. * * Every layer only ever removes tools, so no arrangement of them can widen what a turn reaches. * The allow-list goes FIRST, even though it is the narrowest statement: it is a pure name-set * match, while each of the three below it may be a round trip — an authz service, a feature-flag * store, an entitlement table — and this runs once per model step. */ definitionsFor(actor: Actor, policy: RolesPolicy, allowedTools?: string[], scope?: Omit<ToolDescribeScope, 'actor'>): Promise<ToolDefinition[]>; /** * The specs {@link definitionsFor} offers the model, whole — the same four gates (allow-list, * enabled, role, `canUse`) in the same order. For a surface that lists what an actor can reach * (`GET <path>/tools`), which must never disagree with what the model is actually shown. */ visibleSpecs(actor: Actor, policy: RolesPolicy, allowedTools?: string[]): Promise<ToolSpec[]>; private visibleEntries; /** * Run a tool. Re-checks that the tool is enabled, that the role allows it and that the tool's own * `canUse` admits the actor (defense-in-depth — a call can reach here from a replayed durable step * or an approval granted before the flag moved, neither of which went through `definitionsFor` * again) and re-parses the input. With `options.allowedTools` (the turn's persona allow-list) a * tool off that list is refused last, the same way the offer narrowed it. A tool whose * `describe()` answers `available: false` for the call's scope is refused as not registered. */ private validated; prepare(name: string, input: unknown, ctx: AiToolCtx, policy: RolesPolicy, options?: PrepareOptions): Promise<ToolPreflightResult>; /** Expose the single schema parse; snapshotInput opts proposal callers into immutable JSON. */ prepareValidated(name: string, input: unknown, ctx: AiToolCtx, policy: RolesPolicy, options?: PrepareOptions): Promise<ToolPreparationResult>; invoke(name: string, input: unknown, ctx: AiToolCtx, policy: RolesPolicy, options?: InvokeOptions): Promise<unknown>; } /** Per-call narrowing for {@link ToolRegistry.invoke}. */ export interface InvokeOptions { /** * Only these tool names may run — the turn's persona allow-list. Checked after `enabled`, the roles * policy and `canUse`. Undefined → no such check (every caller predating personas). */ allowedTools?: readonly string[]; /** Trusted normalized JSON input. Own-property presence requires comparison even if undefined. */ approvedInput?: unknown; } export interface PrepareOptions extends InvokeOptions { /** Preserve a detached normalized JSON snapshot and refuse mutation by the prepare hook. */ snapshotInput?: boolean; } export interface ToolPreparationResult { input: unknown; preflight: ToolPreflightResult; } /** * Default gate: one of the actor's roles must be in `spec.roles` (else the default roles). An empty * list means no restriction — the default: a tool is callable by whoever the actor resolver resolved, * an anonymous visitor included. `action` tools still park on approval regardless. */ export declare class DefaultRolesPolicy implements RolesPolicy { private readonly defaultRoles; private readonly emptyRoles; constructor(defaultRoles?: string[], options?: RolesPolicyOptions); can(actor: Actor, tool: ToolSpec): boolean; } /** * What an empty roles list means to {@link DefaultRolesPolicy}: `'allow'` (the default) — no * restriction; `'deny'` — nobody. */ export type EmptyRoles = 'allow' | 'deny'; export interface RolesPolicyOptions { /** What an empty roles list means. Default `'allow'`. */ emptyRoles?: EmptyRoles; } /** * The closed gate — {@link DefaultRolesPolicy} with `emptyRoles: 'deny'`: the actor needs a role the * tool declares (else one of the default roles). A tool with no `roles` and no default roles, or with * an explicitly empty list, reaches nobody. For apps where an empty list means "no one" — a computed * `roles` that can come out empty, a multi-tenant MCP surface. */ export declare class ClosedRolesPolicy extends DefaultRolesPolicy { constructor(defaultRoles?: string[]); } //# sourceMappingURL=tool-registry.d.ts.map