@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
222 lines • 11.5 kB
TypeScript
import type { ActionProposal, ActionProposalMutationResult, AgentCatalogEntry, AgentClientConfig, ChatQueueState, MessageAttachment, MessageFeedback, ModelCatalogView, QuotaReport, SkillCatalogEntry, ThreadDetail, ThreadSummary, ToolCatalogEntry } from '../../index.js';
import type { AgentBackend, AttachmentUploadStrategy, ChatStreamRequest, ChatStreamResponse, MessageFeedbackInput, QueuedMessageUpdate, QueuedSendResult, ResumeStreamRequest, ThreadPatch, UploadAttachmentOptions } from './backend.js';
import { type AgentRequestError, type ErrorAnswer, type HttpErrorListener } from './http-error.js';
export type { ThreadPatch } from './backend.js';
export type { AgentRequestError, HttpErrorListener } from './http-error.js';
/**
* Thrown by {@link AgentClient} on a non-2xx response. Carries the HTTP `status` so callers can
* branch (e.g. 403 → "not your thread", 429 → quota) instead of string-matching a generic Error,
* and what the server said: `message` is the body's `message` when it sent one (so a hook that
* shows `error.message` shows the server's words), `code` its machine-readable `code`, and `body`
* the whole answer, parsed when it is JSON.
*/
export declare class AgentHttpError extends Error implements AgentRequestError {
readonly status: number;
readonly method: string;
readonly path: string;
/** The answer's body: parsed JSON, else its text; `undefined` when empty. */
readonly body: unknown;
/** The body's `code` (`quota_exceeded`, …), when the server sent one. */
readonly code: string | undefined;
constructor(status: number, method: string, path: string, statusText: string, answer?: ErrorAnswer);
/** Read a refused `response`'s body into an error. */
static from(response: Response, method: string, path: string): Promise<AgentHttpError>;
}
export interface CancelResult {
aborted: boolean;
}
export interface OkResult {
ok: boolean;
}
export interface AgentClientOptions {
/**
* The server's origin, e.g. `https://api.example.com`. Defaults to `''` (same origin). The
* agent's route prefix is {@link AgentClientOptions.path}, not part of this.
*/
baseUrl?: string;
/**
* The agent's route prefix — `AgentModule`'s `path`, with any global prefix in front
* (`'api/agent'`). Leading/trailing slashes are optional. Defaults to `'agent'`.
*/
path?: string;
/** Static headers merged into every request. */
headers?: Record<string, string>;
/**
* Resolved per request — for short-lived bearer tokens, or a CSRF header read from a cookie
* (`{ 'X-XSRF-TOKEN': readCookie('XSRF-TOKEN') }`), which has to be read at request time because
* the server may rotate it.
*/
getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
/**
* Forwarded to fetch so cookie auth works. Same-origin requests send cookies by default; set
* `'include'` when the API lives on another origin (and have it answer with credentialed CORS).
*/
credentials?: RequestCredentials;
/** Injectable for tests / non-browser runtimes. */
fetch?: typeof fetch;
/**
* Called with every error answer (an {@link AgentHttpError}, or a `MediaUploadError` from a
* `mediaAttachments()` upload) right before it is thrown — the place for app-wide reactions such
* as "401 → sign in again". The error still reaches the caller. Not called for the `404` a
* resume answers when nothing is streaming, which is an answer, not a failure.
*/
onHttpError?: HttpErrorListener;
/** Attachment uploads. */
attachments?: {
/**
* How `uploadAttachment` uploads. Omitted → `POST <path>/attachments` (multipart). Pass
* `mediaAttachments()` from `@adonis-agora/agent/react/media` for resumable uploads through
* @adonis-agora/media, or your own {@link AttachmentUploadStrategy}.
*/
upload?: AttachmentUploadStrategy;
};
}
/** `'/api/agent'` from `'api/agent'`, `'/api/agent/'`, …; `''` for an empty path. */
export declare function normalizeAgentPath(path: string | undefined): string;
/**
* Framework-agnostic REST client for the Adonis agent endpoints — the default {@link AgentBackend}.
* Used by `useAgentChat`, but standalone-usable (vanilla fetch, no React).
*/
export declare class AgentClient implements AgentBackend {
private readonly options;
constructor(options?: AgentClientOptions);
/** `POST <path>/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
openChatStream(request: ChatStreamRequest): Promise<ChatStreamResponse>;
/**
* `POST <path>/chat` for a message that should wait in the thread's queue — the body carries
* `mode: 'queue'` (or `'interrupt'`), which the server always answers with `202` JSON.
*/
enqueueMessage(request: ChatStreamRequest): Promise<QueuedSendResult>;
/** `GET <path>/threads/:id/queue` — the thread's waiting messages, and whether it drains. */
getQueue(threadId: string): Promise<ChatQueueState>;
/** `PATCH <path>/queue/:messageId` — change a waiting message's text/attachments, or move it. */
updateQueuedMessage(messageId: string, update: QueuedMessageUpdate): Promise<ChatQueueState>;
/** `DELETE <path>/queue/:messageId`. */
removeQueuedMessage(messageId: string): Promise<ChatQueueState>;
/**
* `POST <path>/queue/:messageId/interrupt` — run a waiting message now, cancelling the running
* turn for it. `runId` when nothing was running and it started; else `interrupting`.
*/
interruptQueuedMessage(messageId: string): Promise<ChatQueueState & {
runId?: string;
interrupting?: string;
}>;
/** `DELETE <path>/threads/:id/queue` — drop every waiting message. */
clearQueue(threadId: string): Promise<ChatQueueState>;
/** `POST <path>/threads/:id/queue/resume` — lift a pause; `runId` when the head started. */
resumeQueue(threadId: string): Promise<ChatQueueState & {
runId?: string;
}>;
/**
* `GET <path>/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
* streaming under that id (404).
*/
resumeChatStream(request: ResumeStreamRequest): Promise<ChatStreamResponse | null>;
/**
* Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
* the stored rating.
*/
setMessageFeedback(messageId: string, input: MessageFeedbackInput): Promise<{
feedback: MessageFeedback | null;
}>;
listThreads(): Promise<ThreadSummary[]>;
/**
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
* call, that the model is offered, so what a user can type after a `/` and what the agent can
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
* one conversation; omitted, the server reads it as a brand-new thread.
*/
listSkills(threadId?: string): Promise<SkillCatalogEntry[]>;
/**
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
*/
listTools(agent?: string): Promise<ToolCatalogEntry[]>;
getThread(id: string): Promise<ThreadDetail>;
deleteThread(id: string): Promise<void>;
forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
/** General `PATCH <path>/threads/:threadId` — title and/or the thread's pinned default agent. */
updateThread(id: string, patch: ThreadPatch): Promise<OkResult>;
/**
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
* mirrors the backend's `POST <path>/attachments`. The returned {@link MessageAttachment} is
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
*/
uploadAttachment(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
private uploadWithProgress;
promoteThread(id: string): Promise<OkResult>;
truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
/** `GET <path>/models?agent=` — the models this caller may pick, grouped by provider. */
listModels(agent?: string): Promise<ModelCatalogView>;
/** `GET <path>/agents` — the registered agents, the default one flagged. */
listAgents(): Promise<AgentCatalogEntry[]>;
/** `GET <path>/config` — attachment limits and upload mode, and which features are on. */
getConfig(): Promise<AgentClientConfig>;
/** `GET <path>/quota` — the caller's budget windows and the one blocking sends, if any. */
getQuota(): Promise<QuotaReport>;
cancelStream(runId: string): Promise<CancelResult>;
/**
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
* the decision came through (the server records `'web'` when omitted).
*/
listActionProposals({ threadId }: {
threadId: string;
}): Promise<ActionProposal[]>;
approveActionProposal({ threadId, proposalId, remember, }: {
threadId: string;
proposalId: string;
remember?: boolean;
}): Promise<ActionProposalMutationResult>;
rejectActionProposal({ threadId, proposalId, reason, }: {
threadId: string;
proposalId: string;
reason?: string;
}): Promise<ActionProposalMutationResult>;
approveToolCall(input: {
toolCallId: string;
remember?: boolean;
via?: string;
}): Promise<void>;
rejectToolCall(input: {
toolCallId: string;
reason?: string;
via?: string;
}): Promise<void>;
/**
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
* out takes the pre-picked default the request carried, resolved server-side against the request
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
* `via` names the surface the answer came through (the server records `'web'` when omitted).
*/
answerToolCall(input: {
toolCallId: string;
answers?: Record<string, string[]>;
via?: string;
}): Promise<void>;
/**
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
* values a confirmation would, and persists differently on purpose — only one of them is
* evidence the user chose them.
*/
skipToolCall(input: {
toolCallId: string;
via?: string;
}): Promise<void>;
private fetchImpl;
/** This client's connection, for an {@link AttachmentUploadStrategy}. */
private connection;
private baseUrl;
private agentPath;
/** Origin + agent path: what every route hangs off. */
private root;
private resolveHeaders;
private credentials;
private request;
private requestResponse;
/** The error for a refused `response`, already reported to `onHttpError`. */
private failure;
private handleResponse;
}
//# sourceMappingURL=client.d.ts.map