@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
179 lines • 7.94 kB
TypeScript
import type { CompanyClient, McpConnection, StoredKey } from '../poppy-client/index.js';
import { AgentIdentity, OAuthError, PoppyError, ResourceAuthError } from '../poppy-client/index.js';
import type { Actor } from '../types.js';
import { type PoppyCompanyPolicy, type PoppyConnectionsConfig, type PoppyConnectionsTexts } from './config.js';
import type { PoppyConnectionRecord, PoppyConnectionStore, PoppyPendingConnect } from './store.js';
/** A Company outside the allow-list. The model is told plainly, and nothing is fetched. */
export declare class PoppyCompanyNotAllowedError extends Error {
readonly domain: string;
constructor(domain: string);
}
/** One connection as the app and the model see it — never a token. */
export interface PoppyConnectionView {
domain: string;
organization: string;
status: PoppyConnectionRecord['status'];
scope: string;
connectedSince: number | null;
}
/** What a connect page shows. */
export type PoppyConnectPageData = {
kind: 'expired';
} | {
kind: 'connected';
organization: string;
} | {
kind: 'open';
id: string;
organization: string;
direct: boolean;
device: boolean;
mediated: {
name: string;
label: string;
secret: boolean;
}[] | null;
};
/** What `ask`/`request` bring back from the Company's agent. */
export interface PoppyCompanyReply {
conversationId: string;
replies: {
text: string;
data?: Record<string, unknown>;
}[];
/** The Company said the token lacks something (§7.11). */
authorization?: {
error: string;
scope?: string;
};
status: string;
}
export interface PoppyConnectionsOptions {
config: PoppyConnectionsConfig;
store: PoppyConnectionStore;
secret: string;
production: boolean;
/** Called once a connect completes — the provider decides the parked `connect` call. */
onConnected?: (pending: PoppyPendingConnect) => Promise<void>;
log?: (line: string) => void;
}
/**
* This app's agent acting on other Poppy Companies for its users: the agent identity, the
* connections (sealed at rest), the connect flows and the calls the tools make.
*
* @experimental Tracks Personal Agent Protocol Draft 0.1 — will change as the spec evolves.
*/
export declare class PoppyConnections {
#private;
readonly texts: PoppyConnectionsTexts;
readonly path: string;
readonly baseUrl: string;
readonly insecureDev: boolean;
constructor(options: PoppyConnectionsOptions);
/** How long a connect link works, in ms (`connectTtlSeconds`, default 15 minutes). */
get connectTtlMs(): number;
/** The agent's display name (its `client_name`). */
get identityName(): string;
/** The app's agent identity (`client_id` = `{baseUrl}/{path}/agent.json`). */
identity(): Promise<AgentIdentity>;
ownerOf(actor: Actor): string;
/** The policy for `domain`, or `null` when it is not allow-listed (default: nothing is). */
policy(domain: string): PoppyCompanyPolicy | null;
/** The allow-listed Companies (when the allow-list is a list or a map). */
allowedCompanies(): {
domain: string;
label?: string;
}[];
/**
* The allow-listed domain the model meant by `company`. A model often names a company rather than
* its domain ("MeuProntoo", "meuprontoo.com" for `localhost:3841`): when `company` is not itself
* allowed, it resolves to the ONE allowed company whose label, or the organization of the
* owner's connection to it, it names. Ambiguous or unknown → `company` as given (then refused).
*/
resolveCompany(owner: string, company: string): Promise<string>;
/**
* The organization an allow-listed company names itself in its `poppy.json` (cached ten
* minutes), or `undefined` when it cannot be reached — no owner, no sign-in needed.
*/
organizationOf(domain: string): Promise<string | undefined>;
/** Run `fn` with this owner's client at `domain`, serialized per owner. */
withCompany<T>(owner: string, domain: string, fn: (client: CompanyClient, policy: PoppyCompanyPolicy) => Promise<T>): Promise<T>;
list(owner: string): Promise<PoppyConnectionView[]>;
/** The connection at `domain`, without contacting it. */
connection(owner: string, domain: string): Promise<PoppyConnectionView | null>;
/** Sign out at the Company (revokes the Account Token, §4.9) and mark the connection revoked. */
disconnect(owner: string, domain: string): Promise<boolean>;
/**
* Start a connect: the link the person opens (web or WhatsApp — it is a URL), bound to the parked
* `connect` call so completing it resumes the conversation. `id` makes a replayed call land on the
* same link.
*/
startConnect(input: {
actor: Actor;
domain: string;
scope?: string;
threadId?: string;
runId?: string;
toolCallId?: string;
id?: string;
}): Promise<{
id: string;
url: string;
organization: string;
scope: string;
}>;
/** The connect started by one tool call (`runId`, `toolCallId`), if any. */
pendingFor(runId: string, toolCallId: string): Promise<PoppyPendingConnect | null>;
/**
* The connect id of one tool call (`runId:toolCallId`): a replayed call reaches the same link.
* Keyed with the app secret — the link is a bearer capability, so it must not be derivable.
*/
connectIdFor(key: string): string;
connectUrl(id: string): string;
page(id: string): Promise<PoppyConnectPageData>;
/** Direct Sign-In (§4.5): the authorization URL to send the browser to. */
directUrl(id: string): Promise<string | null>;
/** The Direct Sign-In redirect back to `redirect_uri`. */
directCallback(params: URLSearchParams): Promise<{
ok: boolean;
organization: string | null;
}>;
/**
* Mediated Sign-In (§4.7) from this app's own page: the credentials go from the person's browser
* to this server and straight to the Company — never stored, logged, or shown to the model.
*/
mediated(id: string, credentials: Record<string, string>): Promise<{
status: 'connected' | 'code_required' | 'failed' | 'expired';
sentTo?: string;
}>;
mediatedCode(id: string, code: string): Promise<{
status: 'connected' | 'code_required' | 'failed' | 'expired';
sentTo?: string;
}>;
/** Device Sign-In (§4.6): the code and the Company page where the person approves it. */
deviceStart(id: string): Promise<{
userCode: string;
verificationUri: string;
} | null>;
/** The device code already started for this connect, if any. */
deviceInfo(id: string): Promise<{
userCode: string;
verificationUri: string;
} | null>;
/** One device-code exchange, when the person says they approved. */
deviceCheck(id: string): Promise<'connected' | 'pending' | 'failed' | 'expired'>;
/** The Company's MCP server, with a Bearer token for it (§6). Closed by the caller. */
mcp(owner: string, domain: string): Promise<McpConnection>;
/**
* One exchange with the Company's agent (§7): `act: false` narrows the token to the read scopes
* (`policy.readScopes`, default `poppy:read`), so a question can never make the Company act;
* `act: true` uses the whole grant — the tools gate that behind the person's confirmation.
*/
ask(owner: string, domain: string, text: string, options: {
act: boolean;
}): Promise<PoppyCompanyReply>;
}
/** Private EC JWKs (or poppy-client `StoredKey`s, or a JSON string of either) → `StoredKey[]`. */
export declare function normalizeKeys(input: PoppyConnectionsConfig['identity']['keys']): Promise<StoredKey[]>;
export { OAuthError, PoppyError, ResourceAuthError };
//# sourceMappingURL=service.d.ts.map