@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
230 lines • 8.86 kB
TypeScript
import type { GenuiErrorCategory } from './genui/sandbox-validate.js';
import { type Latency } from './run-metrics.js';
/**
* Generative-UI telemetry: one row per UI tool call (`ui__render`, `ui__sandbox`, `ui__show_*`,
* `ui__show`), recorded by the loop through `AgentStore.recordGenuiCalls` and read back by the
* dashboard's "Generative UI" page through `AgentGovernanceQueries.genuiStats`.
*
* PRIVACY: a row is metadata — names, counts, timings, an error's category and a scrubbed message.
* The generated HTML/JSX and props are stored only when the tool was built with
* `telemetry: { samplePayloads: true }` (off by default).
*/
import type { AgentStore, CostSource } from './spi/agent-store.js';
import type { GovernanceRange } from './spi/governance-queries.js';
import type { SinkWriter } from './spi/token-stream-sink.js';
import type { GenuiToolMarker } from './types.js';
export type { GenuiErrorCategory } from './genui/sandbox-validate.js';
/**
* How a call fared: `first_try` — accepted on the turn's first UI attempt; `retried` — accepted
* after one or more refused attempts; `failed` — refused (the model was handed the error).
*/
export type GenuiValidity = 'first_try' | 'retried' | 'failed';
/** One UI tool call, as the loop records it. */
export interface RecordGenuiCallInput {
/** The tool call's id — the row's key (a replayed record overwrites, never duplicates). */
toolCallId: string;
runId: string;
threadId: string;
actorRef: string;
agentName?: string;
/** The model step (0-based) that wrote the call. */
step: number;
toolName: string;
/** The turn's channel (`web`, `whatsapp`, …). */
channel: string;
mode: GenuiToolMarker['mode'];
modelId: string;
/** 1 + the UI calls refused since the turn's last accepted one. */
attempt: number;
outcome: 'ok' | 'error';
validity: GenuiValidity;
errorCategory?: GenuiErrorCategory;
/** The error, with quoted values replaced by `"…"` (verbatim with `samplePayloads`). At most 300 chars. */
errorMessage?: string;
/**
* The call's share of the step that wrote it — the step's tokens and cost split by the characters
* each tool call's arguments (and the step's text) took.
*/
inputTokens: number;
outputTokens: number;
costUsd?: number;
costSource?: CostSource;
/** Turn start → the call's first `ui` frame (a streaming preview or the pushed component), in ms. */
firstFrameMs?: number;
/** Turn start → the call's final (non-partial) `ui` frame, in ms. Absent for a refused call. */
completeFrameMs?: number;
/** Size of the call's arguments, as JSON. */
payloadBytes: number;
/** The call's arguments as JSON — ONLY with `telemetry.samplePayloads`. */
payload?: string;
}
/** A stored {@link RecordGenuiCallInput}. */
export interface GenuiCallRow extends RecordGenuiCallInput {
createdAt: Date;
}
/** First-try success for one tool and model. */
export interface GenuiToolModelRow {
toolName: string;
modelId: string;
mode: string;
calls: number;
/** Turns whose first UI attempt used this tool. */
firstAttempts: number;
firstTrySuccesses: number;
/** `firstTrySuccesses / firstAttempts`; null when there is none. */
firstTryRate: number | null;
retried: number;
failed: number;
costUsd: number | null;
}
export interface GenuiErrorRow {
category: string;
/** The (scrubbed) message, as recorded. */
message: string;
toolName: string;
count: number;
lastAt: string;
}
export interface GenuiDayRow {
day: string;
calls: number;
turns: number;
inputTokens: number;
outputTokens: number;
costUsd: number | null;
firstTryRate: number | null;
}
/** Time to a UI tool's first and complete frame — the same distribution run latency reports. */
export type GenuiLatency = Latency;
/** What the dashboard's Generative UI page shows. */
export interface GenuiStats {
totals: {
calls: number;
turns: number;
firstAttempts: number;
firstTrySuccesses: number;
firstTryRate: number | null;
retried: number;
failed: number;
inputTokens: number;
outputTokens: number;
costUsd: number | null;
};
byToolModel: GenuiToolModelRow[];
/** Most frequent first, at most 20. */
errors: GenuiErrorRow[];
byDay: GenuiDayRow[];
/** UI cost per turn (turns with at least one UI call). */
costPerTurn: {
turns: number;
avgUsd: number | null;
p50Usd: number | null;
p95Usd: number | null;
};
/**
* Per turn: turn start → its first UI frame, and → the first complete UI frame of an accepted call.
*/
latency: {
firstFrame: GenuiLatency;
complete: GenuiLatency;
};
}
/** Which kind of mistake a UI tool's error message is (what the genui tools and the registry throw). */
export declare function classifyGenuiError(message: string): GenuiErrorCategory;
/** An error message without the values it quotes (a schema's "received 'Jane'"), at most 300 chars. */
export declare function scrubGenuiError(message: string): string;
/** Rows created within the (inclusive, UTC) day range. */
export declare function genuiRowsInRange<T extends {
createdAt: Date;
}>(rows: readonly T[], range: GovernanceRange): T[];
/** {@link GenuiStats} from the rows of a range — the same numbers whichever store holds them. */
export declare function summarizeGenuiCalls(rows: readonly GenuiCallRow[]): GenuiStats;
/** When each tool call's first and final `ui` frames went out on the turn's stream. */
export interface GenuiFrameTimes {
first?: number;
complete?: number;
}
/**
* Wraps the turn's stream writer to note when each tool call's UI frames (`component`) are written —
* what the user sees, so the time to first frame counts a streaming preview, and a frame a gate
* held back counts when it was released.
*/
export declare function observeGenuiFrames(writer: SinkWriter, now?: () => number): {
writer: SinkWriter;
times: Map<string, GenuiFrameTimes>;
};
/** What the loop knows about one step, to turn its UI calls into rows. */
export interface GenuiStepFacts {
runId: string;
threadId: string;
actorRef: string;
agentName?: string;
step: number;
channel: string;
modelId: string;
/** When the turn started; absent → no latency is recorded (an engine that cannot tell). */
startedAt?: number;
/** The step's model usage and cost (the whole step: shared out by characters written). */
usage: {
inputTokens: number;
outputTokens: number;
};
costUsd: number | null;
costSource?: CostSource;
text: string;
calls: readonly {
id: string;
name: string;
input: unknown;
}[];
/** Each call's result: an `error` when it was refused or failed. */
results: readonly {
id: string;
error?: string;
output?: unknown;
}[];
/** A call's genui marker, or undefined for a tool that is not a UI tool. */
markerOf(name: string): GenuiToolMarker | undefined;
frames: Map<string, GenuiFrameTimes>;
/** When the step's results settled (a call whose frames went out unobserved completes here). */
now: number;
}
/**
* Tracks the turn's UI attempts across steps: a refused UI call followed by another is a retry,
* whichever UI tool the second one uses.
*/
export declare class GenuiTurnTelemetry {
private refusedSinceAccepted;
rows(facts: GenuiStepFacts): RecordGenuiCallInput[];
}
/** One UI call made outside the agent loop, for {@link GenuiRunRecorder.track}. */
export interface GenuiOutOfLoopCall {
store: Pick<AgentStore, 'recordGenuiCalls'>;
/** The tool's `spec.genui`; not a UI tool (undefined) or telemetry off → nothing is recorded. */
marker: GenuiToolMarker | undefined;
runId: string;
threadId: string;
actorRef: string;
agentName?: string;
channel: string;
modelId: string;
call: {
id: string;
name: string;
input: unknown;
};
}
/**
* Genui telemetry for an engine whose tools are called OUTSIDE the agent loop (OpenCode's MCP
* endpoint, which its code mode calls): one row per UI call, attempts counted per run. There the
* model's step is not split per call (tokens 0, no cost) and the frames are not timed (no latency).
* Recording is best-effort and never changes what the call returns or throws.
*/
export declare class GenuiRunRecorder {
private readonly maxRuns;
private readonly runs;
constructor(maxRuns?: number);
track<T>(input: GenuiOutOfLoopCall, invoke: () => Promise<T>): Promise<T>;
private turn;
}
//# sourceMappingURL=genui-telemetry.d.ts.map