@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
119 lines • 7.16 kB
TypeScript
import type { HttpContext } from '@adonisjs/core/http';
import type { AgentGovernanceAuthorize } from '../governance-gate.js';
import type { Actor } from '../types.js';
import type { AccessDeniedOption as GenericAccessDeniedOption, AccessDeniedRenderer as GenericAccessDeniedRenderer } from './access_denied_page.js';
/**
* The function form of {@link AgentDashboardConfig.accessDenied}: render (or answer) a refused
* page navigation yourself. Receives the refusal ({@link AccessDeniedInfo}) and the AdonisJS
* {@link HttpContext}. Return an HTML string to have it served; answer the request yourself (a
* redirect, most commonly) and return nothing to make the provider stand down; return nothing
* WITHOUT answering and the built-in page is served.
*/
export type AccessDeniedRenderer = GenericAccessDeniedRenderer<HttpContext>;
/** `accessDenied` in either form — an options object for the built-in page, or a renderer. */
export type AccessDeniedOption = GenericAccessDeniedOption<HttpContext>;
/**
* An extra authorization gate for the dashboard, run AFTER the agent config's `actorResolver` has
* resolved the caller. Return `false` (or throw) to deny — the request gets `403`. The governance
* read-model the SPA serves spans EVERY actor's spend/usage, so most apps restrict the console beyond
* mere authentication, e.g. `(actor) => actor.roles?.includes('ADMIN') ?? false`.
*/
export type AgentDashboardAuthorize = (actor: Actor, ctx: HttpContext) => boolean | Promise<boolean>;
/**
* Runs when the agent config's `actorResolver` itself rejects the caller — no resolver configured, or
* `resolve(ctx)` threw (e.g. `AuthActorResolver` over an anonymous `ctx.auth`) — so there is no
* resolved {@link Actor} to hand to {@link AgentDashboardAuthorize}. Deliberately ctx-only, never a
* fabricated actor: the resolver's whole contract is "never invent an identity", and this hook must
* not undermine that.
*
* Called BEFORE the gate writes its default `401 { error }` JSON. To show something other than that
* JSON — a redirect to the app's own login page, most commonly — set a response header inside this
* hook (typically via `ctx.response.redirect(...)`); the gate detects the `location` header and skips
* its own write. Anything else (return normally without touching the response) falls through to the
* default JSON, so this is safe to omit or to use only for side effects (e.g. logging).
*/
export type AgentDashboardUnauthenticatedHook = (ctx: HttpContext) => void | Promise<void>;
/**
* Optional `config('agent').dashboard` block. The dashboard reuses the agent config's `path` and
* `actorResolver` (so it sits behind the SAME actor gating as the governance routes); this block
* toggles it on/off, optionally overrides the mount path, and optionally adds an `authorize` gate.
*
* Shared by BOTH the embedded dashboard provider (`@adonis-agora/agent/dashboard_provider`, wired
* automatically when you `node ace configure @adonis-agora/agent`) and the standalone
* `@adonis-agora/agent-dashboard` package's provider — one config block controls whichever one is
* registered.
*/
export interface AgentDashboardConfig {
/**
* Mount the SPA. Default `true` — set `false` to keep the routes off entirely. Note `true` is
* necessary but not sufficient: the console also needs the agent's `governanceAuthorize` gate, see
* {@link decideDashboardMount}.
*/
enabled?: boolean;
/** Override the mount path. Default `<agentPath>/dashboard`. */
path?: string;
/**
* Extra authorization run after the actor resolves. Return `false` to deny (`403`). Omit to allow
* any resolved actor (the default) — note this is only an EXTRA gate on the SPA shell; whether the
* console mounts at all, and whether its data is readable, is decided by the agent config's
* `governanceAuthorize`.
*/
authorize?: AgentDashboardAuthorize;
/**
* Runs when the actor resolver itself rejects the caller (no resolver, or `resolve(ctx)` threw) —
* the one denial `authorize` never sees, since there is no resolved actor to hand it. Set a
* response header inside (e.g. `ctx.response.redirect('/login')`) to replace the default
* `401 { error }` JSON with a custom page/redirect. See {@link AgentDashboardUnauthenticatedHook}.
*/
onUnauthenticated?: AgentDashboardUnauthenticatedHook;
/**
* What a BROWSER sees when the gate refuses — the SPA shell or its assets (the console has no
* JSON routes of its own; its data comes from the agent's `/agent/governance/*` routes, which are
* unaffected). Omit it for the built-in page — a dark card in the console's own visual language
* with the status, a sentence explaining the refusal and a "Back to app" link. Pass an object to
* tweak that page (`brand`, `title`, `message`, `homeHref`, `accent`, …), or a function to
* render/answer it yourself — see {@link AccessDeniedRenderer}. A redirect written by
* `onUnauthenticated` or `authorize` still wins: the provider never overwrites a `location` header.
*/
accessDenied?: AccessDeniedOption;
}
export interface ResolvedAgentDashboardConfig {
enabled: boolean;
path?: string;
authorize?: AgentDashboardAuthorize;
onUnauthenticated?: AgentDashboardUnauthenticatedHook;
accessDenied?: AccessDeniedOption;
}
/** Fill defaults for the optional dashboard config block. */
export declare function resolveDashboardConfig(config: AgentDashboardConfig | undefined): ResolvedAgentDashboardConfig;
/** Whether the provider registers the console's routes, and (when it refuses) what to say about it. */
export type DashboardMountDecision = {
mount: true;
} | {
mount: false;
reason: 'disabled';
} | {
mount: false;
reason: 'no-governance-gate';
warning: string;
} | {
mount: false;
reason: 'no-governance-queries';
warning: string;
};
/**
* Decide whether to register the console's routes at all — the router-free core of the provider's
* mount check, extracted so it can be unit tested without booting an app.
*
* The console is a pure consumer of the agent's cross-actor `/agent/governance/*` read routes, and
* those routes are not mounted unless the agent config carries a `governanceAuthorize` gate. Serving
* a shell that can only 404 its own data is worse than not serving it: the failure shows up as seven
* dead panels at click time with nothing anywhere explaining why. So the console follows the same
* fail-closed-by-omission rule one layer down — no gate, no routes — and says so at boot.
*
* This costs nothing that still works: every app affected is an app whose console is ALREADY dead in
* every view but Quota. `dashboard.authorize` is deliberately NOT the trigger; it gates the shell,
* not the data, and an app can set it and still have a console with nothing to render.
*/
export declare function decideDashboardMount(enabled: boolean, governanceAuthorize: AgentGovernanceAuthorize | undefined, governanceQueries?: unknown): DashboardMountDecision;
//# sourceMappingURL=define_config.d.ts.map