import type { Message, ToolDefinition } from '../types/messages.js'; import type { Principal } from '../types/principal.js'; import type { Store } from '../types/store.js'; import { type A2aCardKeyResolver, type A2aCardSigner } from './a2a-signing.js'; import { type ProtocolGraph, type ProtocolInterrupt } from './shared.js'; export type { ProtocolGraph, ProtocolInterrupt, ProtocolResult, ProtocolRunOptions } from './shared.js'; /** The protocol version this module speaks. */ export declare const A2A_PROTOCOL_VERSION = "1.0"; /** Who a message is from. */ export type A2aRole = 'ROLE_USER' | 'ROLE_AGENT'; /** Where a task stands. */ export type A2aTaskState = 'TASK_STATE_SUBMITTED' | 'TASK_STATE_WORKING' | 'TASK_STATE_INPUT_REQUIRED' | 'TASK_STATE_AUTH_REQUIRED' | 'TASK_STATE_COMPLETED' | 'TASK_STATE_FAILED' | 'TASK_STATE_CANCELED' | 'TASK_STATE_REJECTED'; /** One part of a message or an artifact: text, structured data, or a file by URL or bytes. */ export interface A2aPart { /** Text. */ text?: string; /** Structured data. */ data?: unknown; /** A file, by URL. */ url?: string; /** A file, as base64 bytes. */ raw?: string; /** The part's media type. */ mediaType?: string; /** A file's name. */ filename?: string; /** Anything else. */ metadata?: Record; } /** A message between agents. */ export interface A2aMessage { /** Its id. */ messageId: string; /** Who it is from. */ role: A2aRole; /** What it says. */ parts: A2aPart[]; /** The conversation it belongs to. */ contextId?: string; /** The task it continues, when it answers one. */ taskId?: string; /** Anything else. */ metadata?: Record; } /** What a task produced. */ export interface A2aArtifact { /** Its id. */ artifactId: string; /** Its name. */ name?: string; /** Its content. */ parts: A2aPart[]; } /** A unit of work an agent was asked to do. */ export interface A2aTask { /** Its id. */ id: string; /** The conversation it belongs to. */ contextId: string; /** Where it stands, with the agent's latest message. */ status: { state: A2aTaskState; message?: A2aMessage; timestamp?: string; }; /** What it produced. */ artifacts?: A2aArtifact[]; /** The messages exchanged, when asked for. */ history?: A2aMessage[]; /** Anything else. */ metadata?: Record; } /** One of the OAuth 2.0 flows an `oauth2SecurityScheme` offers: exactly one is set. */ export type A2aOAuthFlows = { authorizationCode: { authorizationUrl: string; tokenUrl: string; refreshUrl?: string; scopes: Record; pkceRequired?: boolean; }; } | { clientCredentials: { tokenUrl: string; refreshUrl?: string; scopes: Record; }; } | { deviceCode: { deviceAuthorizationUrl: string; tokenUrl: string; refreshUrl?: string; scopes: Record; }; } | { implicit: { authorizationUrl?: string; refreshUrl?: string; scopes?: Record; }; } | { password: { tokenUrl?: string; refreshUrl?: string; scopes?: Record; }; }; /** How a caller authenticates with an agent, as its card names it: exactly one scheme is set. */ export type A2aSecurityScheme = { apiKeySecurityScheme: { description?: string; location: 'query' | 'header' | 'cookie'; name: string; }; } | { httpAuthSecurityScheme: { description?: string; scheme: string; bearerFormat?: string; }; } | { oauth2SecurityScheme: { description?: string; flows: A2aOAuthFlows; oauth2MetadataUrl?: string; }; } | { openIdConnectSecurityScheme: { description?: string; openIdConnectUrl: string; }; } | { mtlsSecurityScheme: { description?: string; }; }; /** The schemes a call needs, by the names the card gives them, each with the scopes it needs. */ export interface A2aSecurityRequirement { /** Each scheme's name, with its scopes. */ schemes: Record; } /** A JSON Web Signature over an agent card, as the card carries it. */ export interface A2aCardSignature { /** The protected header: base64url-encoded JSON. */ protected: string; /** The signature, base64url-encoded. */ signature: string; /** The unprotected header's values. */ header?: Record; } /** A skill an agent card advertises. */ export interface A2aSkill { /** Its id. */ id: string; /** Its name. */ name: string; /** What it does. */ description: string; /** Labels for discovery. */ tags: string[]; /** Example requests. */ examples?: string[]; /** The schemes a call of this skill needs, beyond the card's. */ securityRequirements?: A2aSecurityRequirement[]; } /** What an agent publishes about itself at `/.well-known/agent-card.json`. */ export interface A2aAgentCard { /** Its name. */ name: string; /** What it does. */ description: string; /** Its version. */ version: string; /** Who provides it. */ provider?: { organization: string; url?: string; }; /** Where it is reached, and how. */ supportedInterfaces: Array<{ url: string; protocolBinding: string; protocolVersion: string; tenant?: string; }>; /** What it supports. */ capabilities: { streaming: boolean; pushNotifications: boolean; extendedAgentCard: boolean; }; /** Media types it accepts. */ defaultInputModes: string[]; /** Media types it produces. */ defaultOutputModes: string[]; /** What it can do. */ skills: A2aSkill[]; /** How a caller authenticates, each scheme by a name `securityRequirements` use. */ securitySchemes?: Record; /** The schemes a call needs. */ securityRequirements?: A2aSecurityRequirement[]; /** Signatures over the card, as given: a signature must be over the card as it is served. */ signatures?: A2aCardSignature[]; } /** A streamed event: a task, a status change, or a piece of an artifact. */ export type A2aStreamEvent = { task: A2aTask; } | { message: A2aMessage; } | { statusUpdate: { taskId: string; contextId: string; status: A2aTask['status']; }; } | { artifactUpdate: { taskId: string; contextId: string; artifact: A2aArtifact; append?: boolean; lastChunk?: boolean; }; }; /** A2A's error codes, beside JSON-RPC's own. */ export declare const A2A_ERRORS: { readonly taskNotFound: -32001; readonly taskNotCancelable: -32002; readonly pushNotificationNotSupported: -32003; readonly unsupportedOperation: -32004; readonly extendedCardNotConfigured: -32007; readonly versionNotSupported: -32009; /** * This package's own, from the range JSON-RPC leaves to servers: the caller may not act for the * tenant it named. HTTP 403, and `PERMISSION_DENIED` over gRPC. */ readonly permissionDenied: -32099; }; /** Raised by a client when an agent answers with an error, on any binding, as A2A's code. */ export declare class A2aError extends Error { /** The error's JSON-RPC code: on HTTP+JSON and gRPC, the code of its `ErrorInfo` reason. */ readonly code: number; /** The error's data, when the agent sent any. */ readonly data?: unknown | undefined; constructor( /** The error's JSON-RPC code: on HTTP+JSON and gRPC, the code of its `ErrorInfo` reason. */ code: number, message: string, /** The error's data, when the agent sent any. */ data?: unknown | undefined); } /** Where a task's updates are pushed: a webhook the calling agent gave. */ export interface A2aPushNotificationConfig { /** Its id, unique within the task. Given one when it has none. */ id?: string; /** Where each update is POSTed. */ url: string; /** Sent back as the `X-A2A-Notification-Token` header, so the receiver knows the update is its own. */ token?: string; /** Sent as the `Authorization` header of each delivery, ` `, such as a bearer token. */ authentication?: { scheme: string; credentials?: string; }; } /** A task as a store keeps it: the task, its messages, the questions it waits on, and its webhooks. */ export interface A2aTaskRecord { /** The task. */ task: A2aTask; /** Every message of the task, the caller's and the agent's. */ history: A2aMessage[]; /** The questions the paused run asked, which the next message answers. */ interrupts?: ProtocolInterrupt[]; /** Where its updates are pushed. */ push?: A2aPushNotificationConfig[]; /** ISO-8601 time it was last written. */ updatedAt: string; /** * Who is running the task, and until when, while it works. The replica running it renews the lease; * once it lapses, another replica's `recover()` may take the task over. */ lease?: A2aTaskLease; /** What the working run was asked to do, so a replica that takes it over can carry it on. */ run?: A2aTaskRun; /** Bumped by every claim of the lease, so two replicas claiming one task cannot both win. */ version?: number; /** The tenant it belongs to, when the handler serves several: no other tenant's request sees it. */ tenant?: string; } /** A replica's hold on a working task. */ export interface A2aTaskLease { /** The replica holding it. */ owner: string; /** ISO-8601 time it lapses unless renewed. */ expiresAt: string; } /** A working run, as kept with its task: started by a message, or answering the task's questions. */ export interface A2aTaskRun { /** `invoke` for a run a message started, `resume` for one answering a paused task's questions. */ kind: 'invoke' | 'resume'; /** The message that started it. */ message: A2aMessage; /** The answers it resumed with, by interrupt id. */ answers?: Record; /** Who it runs for. */ principal?: Principal; } /** Which tasks `A2aTaskStore.list()` returns. */ export interface A2aTaskQuery { /** Only this conversation's. */ contextId?: string; /** Only those in this state. */ state?: A2aTaskState; /** Only this tenant's. A store that serves several tenants must honour it. */ tenant?: string; /** Most returned. */ limit: number; /** Skipped first, for paging. */ offset: number; } /** * Where an A2A agent keeps its tasks and conversations. A store outside the process lets a restart, or * another replica, pick a task up: answer a task's question, read it, list it, or cancel it. */ export interface A2aTaskStore { /** A task's record. */ get(taskId: string): Promise | A2aTaskRecord | undefined; /** Writes a task's record. */ put(record: A2aTaskRecord): Promise | void; /** Tasks, most recently written first, and how many match when the store knows. */ list(query: A2aTaskQuery): Promise<{ records: A2aTaskRecord[]; total?: number; }> | { records: A2aTaskRecord[]; total?: number; }; /** A conversation's transcript, as the agent last left it. */ conversation(contextId: string): Promise | Message[] | undefined; /** Writes a conversation's transcript. */ saveConversation(contextId: string, messages: Message[]): Promise | void; /** * Takes, or renews, the lease of a working task, or of one submitted and not yet started, in one * step: when the task has no lease, its lease has lapsed by `lease.now`, or `lease.owner` already * holds it. Resolves to the record as claimed, * or `undefined` when another replica holds it. Optional; without it, a claim is a read and a write, * and two replicas recovering at once may both take one task. */ claim?(taskId: string, lease: A2aTaskLease & { now: string; }): Promise | A2aTaskRecord | undefined; } /** Tasks and conversations in this process: the default, for one replica. */ export declare class MemoryA2aTaskStore implements A2aTaskStore { private readonly tasks; private readonly conversations; private readonly max; constructor(options?: { maxTasks?: number; }); /** A task's record, as a copy. */ get(taskId: string): A2aTaskRecord | undefined; /** Writes a task's record, dropping the least recently written beyond `maxTasks`. */ put(record: A2aTaskRecord): void; /** Tasks, most recently written first, with how many match. */ list(query: A2aTaskQuery): { records: A2aTaskRecord[]; total: number; }; /** A conversation's transcript. */ conversation(contextId: string): Message[] | undefined; /** Writes a conversation's transcript. */ saveConversation(contextId: string, messages: Message[]): void; /** Takes or renews a working task's lease, in one step. */ claim(taskId: string, lease: A2aTaskLease & { now: string; }): A2aTaskRecord | undefined; } /** * Tasks and conversations in any long-term `Store`: the memory, Redis, Postgres, or SQLite store, so * every replica sees one set. Listing filters on the task's fields in the store; it does not count * all matches, so `total` is unknown. */ export declare function storeA2aTasks(store: Store, options?: { namespace?: readonly string[]; }): A2aTaskStore; /** Options for pushing task updates to the webhooks callers give. */ export interface A2aPushOptions { /** * Signs every delivery with an \`x-nexus-signature\` header, as the server's operation webhooks are, * so a receiver checks it with \`verifyOperationWebhook()\` from \`nexus-ai-pro/operations\`. */ secret?: string; /** * Allows webhooks on loopback, private, and link-local addresses, and plain \`http\`. Off by default, * so a caller cannot make the agent reach into its own network. Turn it on for tests, or when every * caller is trusted. */ allowPrivateNetworks?: boolean; /** Replaces \`fetch\` for deliveries: a client with its own address checks, a proxy, or a test. */ fetch?: typeof globalThis.fetch; /** How long one delivery may take, in milliseconds. Defaults to 5 seconds. */ timeoutMs?: number; /** * Pushes the answer's text as it is written too, each piece as an `artifactUpdate`, in order with * the task's changes of state: so a caller that cannot hold a stream open still sees the answer * arrive. `everyMs` gathers the pieces written within that many milliseconds into one delivery. * Off by default: only changes of state are pushed. */ text?: boolean | { everyMs?: number; }; } /** How one handler serves several tenants, each request routed by the `tenant` it names. */ export interface A2aTenancyOptions { /** * Whether a caller may act for the tenant its request names. Defaults to its principal's `tenantId` * being that tenant, so a caller nobody authenticated may act for none. */ authorize?: (tenant: string, principal: Principal | undefined) => boolean | Promise; /** Whether every request must name a tenant. Defaults to true. */ required?: boolean; } /** Options for `a2aHandler()`. */ export interface A2aServerOptions { /** The card's content: the endpoint `url` is where this handler is mounted. */ card: Omit & { url: string; defaultInputModes?: string[]; defaultOutputModes?: string[]; }; /** The graph input for a task. Defaults to `{ messages }`: the context's conversation and the new message. */ input?: (message: A2aMessage, history: Message[]) => unknown; /** * The answer a message gives a paused task's question. Defaults to its first data part's value, * such as `{ approved: true }` for a tool approval, or else its text. Text answering an approval is * read as words: yes, ok, allow, approve, or confirm allows, and anything else refuses. */ answer?: (message: A2aMessage, interrupt: ProtocolInterrupt) => unknown; /** Who the task is for, from the request: the place to authenticate the calling agent. */ principal?: (request: Request) => Principal | undefined | Promise; /** * Where tasks and conversations are kept. Defaults to memory, for one replica; \`storeA2aTasks()\` * keeps them in a shared store. A task resumed elsewhere needs the graph's own checkpointer to be * shared too. */ store?: A2aTaskStore; /** Most tasks and contexts the default memory store keeps. Defaults to 10,000 each. */ maxTasks?: number; /** Pushes each task's updates to the webhooks callers give. Without it, push notifications are not offered. */ pushNotifications?: A2aPushOptions; /** * The extended card an authenticated caller may read with `GetExtendedAgentCard`: more skills, or * anything else this caller may see. Without it, there is no extended card. */ extendedCard?: (principal: Principal) => Partial | Promise>; /** * Signs every card this handler serves, its own, each tenant's, and the extended card, with each key * given, as protocol 1.0 signs a card: a JWS over its canonical form. A card's own `signatures` are * kept, and these follow them. */ sign?: A2aCardSigner | readonly A2aCardSigner[]; /** How often `SubscribeToTask` reads a task another replica runs, in milliseconds. Defaults to 1 second. */ pollMs?: number; /** * The lease a replica holds on each task it runs: renewed every third of `ms` (30 seconds by * default), and taken over by another replica's `recover()` once it lapses. `owner` names this * replica; it defaults to a random id. */ lease?: { ms?: number; owner?: string; }; /** * Runs `recover()` every this many milliseconds, so a task a stopped replica was working on is taken * over without a caller asking. Off by default; `close()` stops it. */ recoverEveryMs?: number; /** Receives push delivery failures, which never fail a task. */ onError?: (error: unknown) => void; /** * The bindings this handler answers, as its card lists them: `JSONRPC`, POSTed to the card's `url`, * and `HTTP+JSON`, its routes under that URL, such as `/message:send`. Defaults to both. */ bindings?: ReadonlyArray<'JSONRPC' | 'HTTP+JSON'>; /** * More interfaces the card lists, after this handler's own: the gRPC service * `nexus-ai-pro/protocols/a2a/grpc` serves over this handler, as `{ url: 'agents.example.com:443', * protocolBinding: 'GRPC' }`, or a binding of your own. */ interfaces?: ReadonlyArray<{ url: string; protocolBinding: string; protocolVersion?: string; tenant?: string; }>; /** * Serves several tenants from this one handler, each request routed by the tenant it names: in its * params over JSON-RPC, as its path's first segment over HTTP+JSON (`/{tenant}/message:send`), and in * its message over gRPC. Each tenant's tasks and conversations are its own, and its runs carry the * tenant on their principal, for its credentials and budget. A caller that may not act for the tenant * it names is refused with `A2A_ERRORS.permissionDenied`. `true` takes the defaults. Off by default: * a request's tenant is not read. */ tenants?: A2aTenancyOptions | boolean; } /** The binding a call arrived on. */ export type A2aBinding = 'JSONRPC' | 'HTTP+JSON' | 'GRPC' | (string & {}); /** Who a call is for, and what carries it. */ export interface A2aCallContext { /** The caller, as `A2aServerOptions.principal` read them. */ principal?: Principal; /** Stops a stream when the caller goes away. */ signal?: AbortSignal; /** * The binding, which shapes a push config's answer: JSON-RPC's carries the config both flat, as * protocol 1.0 has it, and as the `pushNotificationConfig` the drafts had. Defaults to `JSONRPC`. */ binding?: A2aBinding; } /** What one A2A call answers, whatever binding carries it: a result, an error, or a stream of events. */ export type A2aCallOutcome = { result: unknown; } | { error: { code: number; message: string; }; } | { events: ReadableStream; }; /** An A2A error, as each binding carries it: its HTTP status, its gRPC status, and its reason. */ export interface A2aErrorStatus { /** The HTTP status of the HTTP+JSON binding. */ httpStatus: number; /** The gRPC status code's name, such as `NOT_FOUND`. */ grpcStatus: string; /** The `google.rpc.ErrorInfo` reason, such as `TASK_NOT_FOUND`. */ reason: string; } /** How an error code is carried on the HTTP+JSON and gRPC bindings, as protocol 1.0 maps them. */ export declare function a2aErrorStatus(code: number): A2aErrorStatus; /** The error code of an `ErrorInfo` reason, the way back: `TASK_NOT_FOUND` is -32001. */ export declare function a2aErrorCode(reason: string | undefined, fallback?: number): number; /** What one `recover()` did. */ export interface A2aRecoveryReport { /** Tasks whose lease had lapsed, taken over and carried on here. */ tookOver: string[]; /** Tasks another replica claimed first, or that finished meanwhile. */ skipped: number; } /** * An A2A agent's HTTP handler, with what keeps its tasks alive across replicas: `recover()` takes over * the working tasks whose replica stopped renewing their lease, and `close()` stops the recovery timer. */ export interface A2aHandler { /** Answers one HTTP request: `GET` the card, a JSON-RPC `POST`, or an HTTP+JSON route under the card's URL. */ (request: Request): Promise; /** This replica's name in the leases it holds. */ readonly owner: string; /** * Takes over working tasks whose lease lapsed, up to `limit` (10), and carries each on: from the * graph's last checkpoint, by answering its questions again when the run was a resume that never * reached the graph, or from the start when the run left no checkpoint. Resolves once they are * claimed, not finished. */ recover(options?: { limit?: number; }): Promise; /** Stops the recovery timer `recoverEveryMs` started. Tasks running here carry on. */ close(): void; /** * Runs one method by its name, with its params as protocol 1.0's JSON has them: what every binding * does, and so how another binding is served over the same tasks, as the gRPC service is. */ call(method: string, params: Record, context?: A2aCallContext): Promise; /** The card this handler serves. */ readonly card: A2aAgentCard; /** Who a request is for, as `A2aServerOptions.principal` reads it: for a binding of your own to call with. */ authenticate(request: Request): Promise; } /** * An HTTP handler that serves a graph as an A2A agent: `GET` the card, `POST` JSON-RPC, or call an * HTTP+JSON route. Mount it at the card's `url` and every path under it; it answers the well-known card * path under the same origin too. Its `call()` serves any other binding, as `a2aGrpcService()` does. * * ```ts * const handler = a2aHandler(agent, { * card: { name: 'Researcher', description: 'Answers research questions', version: '1.0.0', url: 'https://agents.example.com/a2a', skills: [] }, * }); * ``` */ export declare function a2aHandler(graph: ProtocolGraph, options: A2aServerOptions): A2aHandler; /** Options for `a2aClient()`. */ export interface A2aClientOptions { /** The agent: its card's URL, or its endpoint for the binding, whose origin serves the card. */ url: string; /** Replaces the global `fetch`, for authentication, a proxy, or tests. */ fetch?: typeof globalThis.fetch; /** Headers added to every request, such as an authorization header. */ headers?: Record; /** * The binding to call the agent over: `JSONRPC`, the default, or `HTTP+JSON`. Given the card's URL, * the client calls the interface the card lists for it. gRPC has its own client, `a2aGrpcClient()`, * on `nexus-ai-pro/protocols/a2a/grpc`. */ binding?: 'JSONRPC' | 'HTTP+JSON'; /** * The tenant every request names. Defaults to the `tenant` of the interface the card lists for the * binding, as protocol 1.0 requires a client to send it. */ tenant?: string; /** * Checks the agent's card before the first call: a signature on it must hold against the public key * this gives for the signature's key id, or every call fails with `A2aCardSignatureError`. Given an * endpoint rather than the card, the card is read from the endpoint's origin to check it. */ verifyCard?: A2aCardKeyResolver; } /** What to send: text, and optionally data, a conversation, or the paused task it answers. */ export interface A2aSendOptions { /** The conversation to continue. */ contextId?: string; /** The paused task this message answers. */ taskId?: string; /** Structured data sent beside the text, such as `{ approved: true }`. */ data?: unknown; /** A webhook for this task's updates, when the agent pushes them. */ push?: A2aPushNotificationConfig; /** Returns the task while it runs, instead of when it is done. */ returnImmediately?: boolean; /** Cancels the request. */ signal?: AbortSignal; } /** Which tasks `A2aClient.listTasks()` asks for. */ export interface A2aListTasksOptions { /** Only this conversation's. */ contextId?: string; /** Only those in this state. */ status?: A2aTaskState; /** Tasks per page, up to 100. Defaults to 50. */ pageSize?: number; /** The `nextPageToken` of the page before. */ pageToken?: string; /** Messages of each task's history to include. */ historyLength?: number; } /** A page of tasks. */ export interface A2aTaskPage { /** The tasks. */ tasks: A2aTask[]; /** The token for the next page, or empty when this is the last. */ nextPageToken: string; /** The page size used. */ pageSize: number; /** How many tasks match, when the agent's store counts them. */ totalSize?: number; } /** A client for one A2A agent. */ export interface A2aClient { /** The agent's card, fetched once. */ card(): Promise; /** The card for an authenticated caller, as `GetExtendedAgentCard` returns it. */ extendedCard(): Promise; /** Sends a message and waits for the task it starts or continues. */ send(text: string, options?: A2aSendOptions): Promise; /** Sends a message and reads the task's events as they arrive. */ stream(text: string, options?: A2aSendOptions): AsyncGenerator; /** Reads a task. */ getTask(taskId: string, historyLength?: number): Promise; /** Lists tasks, a page at a time. */ listTasks(options?: A2aListTasksOptions): Promise; /** Follows a task that is still running, from now until it finishes or waits. */ subscribe(taskId: string, options?: { signal?: AbortSignal; }): AsyncGenerator; /** Cancels a task. */ cancel(taskId: string): Promise; /** Adds a webhook for a task's updates. */ setPushNotification(taskId: string, config: A2aPushNotificationConfig): Promise; /** A task's webhooks. */ listPushNotifications(taskId: string): Promise; /** Removes one of a task's webhooks. */ deletePushNotification(taskId: string, configId: string): Promise; } /** A client for an A2A agent, by its card or endpoint URL. */ export declare function a2aClient(options: A2aClientOptions): A2aClient; /** Options for `a2aTool()`. */ export interface A2aToolOptions extends A2aClientOptions { /** The tool's name, as the model calls it. */ name: string; /** What the remote agent does, written for the model that decides when to call it. */ description: string; } /** * A remote A2A agent as a tool another agent can call. Each call is a task, and the remote agent's * answer is the result; a remote task that needs input reports its question, which the model can * answer by calling again with `taskId`. The tool declares the host it reaches, as * `network:`, so a permission policy grants or denies it like any other. */ export declare function a2aTool(options: A2aToolOptions): ToolDefinition; export { type A2aCardKeyResolver, type A2aCryptoKey, type A2aJsonWebKey, A2aCardSignatureError, type A2aCardSigner, type A2aSignatureAlgorithm, canonicalA2aCard, signA2aCard, verifyA2aCard, } from './a2a-signing.js';