@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
70 lines • 3.31 kB
JavaScript
/** The role every A2A caller carries — what a tool names to be reachable by personal agents at all. */
export const PERSONAL_AGENT_ROLE = 'personal_agent';
export const REQUEST_PERMISSION_TOOL = 'request_permission';
/** The component the tool pushes into the stream; the A2A turn reads it back as a step-up. */
export const PERMISSION_COMPONENT = 'a2a.permission';
function inputSchema(scopeIds) {
const known = new Set(scopeIds);
return {
'~standard': {
version: 1,
vendor: 'adonis-agora-agent',
validate: (value) => {
const scopes = value?.scopes;
if (!Array.isArray(scopes) || scopes.length === 0) {
return { issues: [{ message: 'must be a non-empty array', path: ['scopes'] }] };
}
const unknownScope = scopes.find((id) => typeof id !== 'string' || !known.has(id));
if (unknownScope !== undefined) {
return {
issues: [{ message: `unknown scope ${String(unknownScope)}`, path: ['scopes'] }],
};
}
return { value: { scopes: [...new Set(scopes)] } };
},
jsonSchema: {
input: () => ({
type: 'object',
properties: {
scopes: {
type: 'array',
items: { type: 'string', enum: scopeIds },
minItems: 1,
description: 'The permissions this request needs and the user has not granted yet.',
},
},
required: ['scopes'],
additionalProperties: false,
}),
},
},
};
}
/**
* Register `request_permission`: the tool a personal agent's turn calls when what it was asked to do
* needs a permission the user has not delegated. It pushes the requested scopes into the stream and
* ENDS the turn; the A2A handler answers with a step-up task (PACT §5.5) carrying a consent link for
* exactly those scopes. The conversation stays open — the agent re-sends once the user approves.
*
* Only personal agents reach it ({@link PERSONAL_AGENT_ROLE}), and only scopes the deployment
* offers can be asked for.
*/
export function registerRequestPermissionTool(registry, scopes) {
const ids = Object.keys(scopes);
const catalog = ids.map((id) => `- ${id}: ${scopes[id]}`).join('\n');
registry.register({
name: REQUEST_PERMISSION_TOOL,
kind: 'read',
terminal: true,
roles: [PERSONAL_AGENT_ROLE],
description: `Ask the user to grant permissions you do not have yet. Call it when the request needs one of these and it is not among your current permissions — the user approves on their own account page and you continue afterwards:\n${catalog}`,
inputSchema: inputSchema(ids),
}, {
async execute(input, ctx) {
const { scopes: requested } = input;
await ctx.emitComponent?.(PERMISSION_COMPONENT, { scopes: requested });
return { requested, status: 'awaiting_user_approval' };
},
});
}
//# sourceMappingURL=permission-tool.js.map