@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
72 lines • 4.9 kB
TypeScript
import type { HttpContext } from '@adonisjs/core/http';
import type { ActorResolver } from '../spi/actor-resolver.js';
import type { AccessDeniedOption, AgentDashboardAuthorize, AgentDashboardUnauthenticatedHook } from './define_config.js';
/** The outcome of the dashboard access gate: proceed, or deny with an HTTP status + message. */
export type DashboardGateVerdict = {
ok: true;
} | {
ok: false;
status: 401 | 403;
error: string;
};
/**
* Decide whether a request may reach the dashboard, WITHOUT touching the response — so it is unit
* testable free of the AdonisJS router/app: resolve the actor through the agent config's resolver
* (missing/failed → `401`), then run the optional `authorize` gate over the resolved actor
* (denied/threw → `403`).
*
* This gates the SPA **shell**, and only the shell. It is NOT what decides whether the console's data
* is readable, and it is no longer the last line of defence it once had to be:
*
* - Whether the console mounts at all is decided earlier, by {@link decideDashboardMount} — the
* routes are not registered unless the agent config carries a `governanceAuthorize` gate AND its
* `governanceQueries` read-model exists. So this function never runs for a console that could only
* 404 its own panels.
* - Whether the DATA is readable is decided by the agent's own `governanceAuthorize` on the
* `/agent/governance/*` routes, not here. Omitting `authorize` while a gate is configured leaves
* the shell open to any resolved actor but exposes no cross-actor data.
*
* Omitting BOTH the resolver and `authorize` would expose the shell to any request; the provider is
* expected to short-circuit on a missing resolver via the `401` here.
*
* `debug` controls whether a THROWN error's `.message` reaches the client, for the two per-request
* failure paths (the resolver rejecting a caller, `authorize` throwing): the resolver's job is
* exactly to reject unauthenticated/unauthorized callers, and its message is meant for the developer
* wiring the config (e.g. `AuthActorResolver`'s "no authenticated user on ctx.auth.user...") — not
* for whoever is making the request, which in production is an untrusted, possibly anonymous,
* possibly adversarial caller. Default `false` (generic `'unauthorized'`/`'forbidden'`, matching
* `@adonis-agora/durable`'s dashboard convention of a uniform message on every credential failure);
* the provider passes `!app.inProduction` so local/dev boots keep the diagnostic detail. The "no
* actor resolver configured" branch is unaffected — that is a static config error, not a per-request
* one, identical for every caller, and useful for the operator to see even in production.
*
* `onUnauthenticated`, when set, runs on EITHER failure-to-resolve branch (no resolver configured, or
* `resolve(ctx)` threw) before this returns its verdict — it never sees a fabricated actor, since
* there isn't one. The provider (not this function) is what actually decides whether to skip its
* default JSON write in favour of whatever the hook did to `ctx.response` (redirect, custom render);
* this function only guarantees the hook runs before the verdict is handed back.
*/
export declare function evaluateDashboardGate(ctx: HttpContext, actorResolver: ActorResolver | undefined, authorize?: AgentDashboardAuthorize, debug?: boolean, onUnauthenticated?: AgentDashboardUnauthenticatedHook): Promise<DashboardGateVerdict>;
/** What {@link answerDashboardDenial} needs to serve (or delegate) the access-denied page. */
export interface DashboardDenialOptions {
/** The console's mount (`/agent/dashboard`) — reported to a custom renderer as `basePath`. */
basePath: string;
/** The host's `dashboard.accessDenied` option — tweak the built-in page, or render it. */
accessDenied?: AccessDeniedOption | undefined;
}
/**
* Write a refused {@link DashboardGateVerdict} to the response — shared by BOTH dashboard providers
* (embedded and standalone) so they refuse identically. A browser is what hits these routes (the
* SPA shell and its assets), so the body is the built-in access-denied page (or the host's
* `accessDenied` customisation of it), not the JSON the governance API answers with.
*
* Stands down when the request is already answered: `onUnauthenticated`/`authorize` (or the
* renderer) writing a redirect keeps it — the `location`-header rule the providers have always
* honoured. The verdict's `error` is shown on the page as a developer detail only when it is more
* than the generic word (i.e. the `debug` message outside production, or the static "no actor
* resolver configured" operator hint).
*/
export declare function answerDashboardDenial(ctx: HttpContext, verdict: Extract<DashboardGateVerdict, {
ok: false;
}>, options: DashboardDenialOptions): Promise<void>;
//# sourceMappingURL=gate.d.ts.map