UNPKG

@adonis-agora/agent

Version:

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

56 lines • 2.74 kB
import { defaultScopeResolver } from '../skills.js'; import { PERSONAL_AGENT_ROLE } from './permission-tool.js'; /** The role a delegated scope becomes. Namespaced, so a scope can never pose as an app role. */ export const SCOPE_ROLE_PREFIX = 'scope:'; export function scopeRole(scope) { return `${SCOPE_ROLE_PREFIX}${scope}`; } function isPersonalAgent(actor) { return (actor.roles ?? []).includes(PERSONAL_AGENT_ROLE); } /** * Wrap a {@link RolesPolicy} so personal agents reach only what was written for them. * * An A2A actor carries {@link PERSONAL_AGENT_ROLE}, plus one {@link scopeRole} per delegated scope. * For it, a tool must DECLARE one of those roles, and the wrapped policy must agree too. A tool with * no `roles` is unreachable: under the default policy "no roles" means "anyone", and an app's * existing tools were never written with an outside caller in mind. Every other actor is decided by * the wrapped policy alone, unchanged. */ export function personalAgentGate(inner) { return { async can(actor, tool) { if (isPersonalAgent(actor)) { const roles = actor.roles ?? []; if (!(tool.roles ?? []).some((role) => roles.includes(role))) return false; } return inner.can(actor, tool); }, /** * A personal agent is OFFERED every tool written for personal agents — `personal_agent`, or a * `scope:` role even when that scope was not delegated. Running it still needs {@link can}; the * denied call is what the A2A turn turns into a permission request (step-up), so the model asks * for a permission by doing what it would do anyway, not by remembering a separate tool. */ async canOffer(actor, tool) { if (!isPersonalAgent(actor)) { return inner.canOffer ? inner.canOffer(actor, tool) : inner.can(actor, tool); } return (tool.roles ?? []).some((role) => role === PERSONAL_AGENT_ROLE || role.startsWith(SCOPE_ROLE_PREFIX)); }, }; } /** * Wrap the {@link ScopeResolver} of memory or skills so a personal agent resolves NO scope. Under * delegation its actor id is the account's — without this it would read the account's memories * (and its tenant's) and could write new ones, a standing instruction to the user's own assistant. * Built-ins like `remember` come from config, not the registry, so the tool gate never sees them. */ export function personalAgentScopes(inner) { const resolver = inner ?? defaultScopeResolver; return { resolve: (ctx) => (isPersonalAgent(ctx.actor) ? [] : resolver.resolve(ctx)), }; } //# sourceMappingURL=gate.js.map