UNPKG

@adonis-agora/agent

Version:

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

103 lines • 5.15 kB
import type { AgentService } from '../agent-service.js'; import type { StreamFrame } from '../spi/token-stream-sink.js'; import type { Actor, PageContext } from '../types.js'; /** * What an A2A turn does with an `action` call that parks for approval: * - `'approve-delegated'` (default) — approve it when the caller's delegation grants one of the * tool's roles: the user approved that scope on their own account page, which IS the consent the * in-app approval stands for. Anything else is rejected. An independent proposal * (`actionApprovalMode: 'independent'`) is decided the same way once the run ends, and the turn * waits for it to execute, so the reply never claims an action that did not happen. * - `'reject'` — reject every action; personal agents only read. */ export type A2aActionPolicy = 'approve-delegated' | 'reject'; export interface A2aTurnInput { actor: Actor; text: string; /** The registered agent that answers. Omitted → the service's default. */ agentName?: string; /** Continue this thread; omitted → a new (transient) one. */ threadId?: string; /** Scopes the caller's delegation grants; `null` without a delegation. */ delegatedScopes: string[] | null; actions: A2aActionPolicy; /** Give up (and cancel the run) after this long. */ timeoutMs: number; /** Called once the run started, with the thread it runs in — before its frames are read. */ onStarted?: (threadId: string, runId: string) => Promise<void>; /** The page context the turn runs with. Default `{ channel: 'a2a' }`. */ pageContext?: PageContext; /** What approvals and answers are recorded as coming through. Default `'a2a'`. */ via?: string; /** Each piece of the reply's text as it streams — for a surface that relays it live. */ onText?: (delta: string) => void | Promise<void>; /** Each component frame other than a permission request, as it arrives. */ onComponent?: (frame: Extract<StreamFrame, { t: 'component'; }>) => void | Promise<void>; /** * Execute, in this process, what of the run waits for a worker (`AgentService.drive`) while the * turn is read — for a durable runner whose dispatcher only persists a started run. */ drive?: (runId: string) => Promise<void>; } export interface A2aTurnAction { tool: string; argsHash: string; /** The delegated scopes that authorized it. */ scopes: string[]; } /** * An independent proposal that executed during the turn: the app's own summary of it (the * confirmation it would have shown the person) and what the tool returned. */ export interface A2aTurnOutcome { tool: string; /** * `succeeded` — approved and executed; `failed` — approved but did not run (`error` says why); * `rejected` — the policy refused it (no delegated scope, or `actions: 'reject'`). */ status: 'succeeded' | 'failed' | 'rejected'; summary: { title: string; detail?: string; }; result?: unknown; /** Why a `failed` action did not run. */ error?: string; } export interface A2aTurnResult { threadId: string; text: string; /** Scopes the agent asked the user for (`request_permission`); `null` when it asked for none. */ permission: string[] | null; /** Tools that RAN under a delegated scope (reads and approved actions) — what a receipt lists. */ actions: A2aTurnAction[]; /** Approved proposals that executed — the reply carries each as a data part. */ outcomes: A2aTurnOutcome[]; /** Why the run failed, when it did. */ error: string | null; } /** * What runs an A2A turn: start it, read its frames, settle what it parks on. `AgentService` (the * agent provider's) is one; an app with its own runtime — a `runAgentLoop` built per request, say — * writes an adapter with the same shape (`defineA2aConfig({ service })`). */ export interface A2aTurnService extends Pick<AgentService, 'chat' | 'subscribe' | 'approve' | 'reject' | 'skip' | 'cancel'>, Partial<Pick<AgentService, 'decideActionProposal' | 'listActionProposals'>> { /** * The roles a tool declares — what an approval and a receipt match the delegated scopes against. * Omitted → the shared `ToolRegistry`. An app whose registry is built per turn answers it here. */ toolRoles?(toolName: string): string[] | undefined; } /** * Run one A2A turn to completion and read its outcome off the stream. A2A's `message:send` is * synchronous (PACT §4.2), so the run's frames are consumed here until the stream ends: text is the * reply, an approval is decided on the spot by {@link A2aActionPolicy}, a question set is skipped * (there is no human on this side to answer it — the agent proceeds on its own assumptions), and a * `request_permission` call becomes a step-up. */ export declare function runA2aTurn(service: A2aTurnService, /** The roles a tool declares (see {@link A2aTurnService.toolRoles}). */ toolRoles: (toolName: string) => string[] | undefined, input: A2aTurnInput): Promise<A2aTurnResult>; //# sourceMappingURL=turn.d.ts.map