@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
342 lines • 14.4 kB
TypeScript
import type { GenuiStats } from '../genui-telemetry.js';
import type { Latency, RunFailureCodeRow } from '../run-metrics.js';
/**
* A read-model over the persisted agent data (usage ⋈ pricing, tool calls, threads) for the
* governance surfaces — the standalone `-dashboard` SPA and the `-telescope` "Agent" tab both
* consume this ONE interface, so cost/usage aggregation lives in a single place.
*
* Separate from {@link AgentStore} on purpose: that SPI owns the write/thread path, this owns the
* read/analytics path. A store adapter implements both. Consumers inject via
* `AGENT_GOVERNANCE_QUERIES`.
*
* Live activity (in-flight runs, streaming tool calls, delegations, forbidden attempts) is NOT here
* — that comes off the `aviary:agent:*` diagnostics channel. This interface is the durable, restart-
* surviving history.
*/
import type { CursorPage, CursorParams } from '../pagination.js';
/** Inclusive UTC day range, each `YYYY-MM-DD`. */
export interface GovernanceRange {
fromDay: string;
toDay: string;
}
/** Spend + token totals for one model over a range. */
export interface ModelSpendRow {
modelId: string;
requests: number;
inputTokens: number;
outputTokens: number;
costUsd: number;
}
/** Spend + token totals for one acting ref (user/tenant) over a range. */
export interface ActorSpendRow {
actorRef: string;
/** Display label for `actorRef`, when an `actorDirectory` is bound and knows one. See {@link ActorSpendRow.actorRef}. */
actorLabel?: string;
requests: number;
totalTokens: number;
costUsd: number;
}
/** One point on the daily usage/cost trend. */
export interface UsageTrendPoint {
day: string;
totalTokens: number;
costUsd: number;
}
/** A recent tool-call for the activity feed. */
export interface ToolCallActivityRow {
toolCallId: string;
toolName: string;
toolType: string;
status: string;
threadId: string;
createdAt: string;
}
/** A recent thread with rolled-up activity. */
export interface ThreadActivityRow {
threadId: string;
title: string;
actorRef: string;
/** Display label for `actorRef`, when an `actorDirectory` is bound and knows one. */
actorLabel?: string;
messageCount: number;
totalTokens: number;
lastActivityAt: string;
}
/** A run's lifecycle status as the read-model surfaces it. */
export type RunStatus = 'running' | 'completed' | 'failed' | 'cancelled';
/**
* Filters + cursor for {@link AgentGovernanceQueries.listRuns}. All optional; absent = no constraint.
*
* The pagination half is {@link CursorParams} — the ecosystem-wide `{ after, first }` shape mirroring
* `@adonis-agora/filter`'s `CursorParams`. Forward-only: no `before`/`last`.
*/
export interface ListRunsFilter extends CursorParams {
/** Exact `actor_ref`. */
actor?: string;
/** Exact `agent_name`. */
agent?: string;
status?: RunStatus;
/** Inclusive UTC-day lower bound on `started_at`, `YYYY-MM-DD`. */
from?: string;
/** Inclusive UTC-day upper bound on `started_at`, `YYYY-MM-DD`. */
to?: string;
/**
* Opaque cursor from a prior page's {@link ListRunsResult.nextCursor}; omit for the first page.
* (Inherited from {@link CursorParams}, restated for the doc site.)
*/
after?: string;
/** Page size; defaults to 50, clamped to 200 by the adapter. (Inherited from {@link CursorParams}.) */
first?: number;
}
/** A run row for the governance list + detail. Newest-first in {@link ListRunsResult}. */
export interface RunSummaryRow {
runId: string;
threadId: string;
actorRef: string;
/** Display label for `actorRef`, when an `actorDirectory` is bound and knows one. */
actorLabel?: string;
tenantRef: string | null;
agentName: string | null;
/**
* The run that delegated this one, or `null` for a turn a person started. Without it a delegation's
* spend reads as an orphan turn on every surface built over these rows.
*/
parentRunId: string | null;
status: string;
/** ISO timestamp. */
startedAt: string;
/** ISO timestamp, `null` while still running. */
finishedAt: string | null;
/** `finishedAt - startedAt` in ms, `null` while still running. */
durationMs: number | null;
stepCount: number;
inputTokens: number;
outputTokens: number;
costUsd: number | null;
error: string | null;
durable: boolean;
/**
* Turn start → first text frame, for a completed run that streamed text. Optional: an adapter that
* predates it omits it; `null` when the run didn't record one.
*/
ttftMs?: number | null;
/** A failed run's stable stream error code (`quota_exceeded`, `run_failed`…); optional, as `ttftMs`. */
errorCode?: string | null;
/** The completed run's last model; optional, as `ttftMs`. */
modelId?: string | null;
}
/**
* One page of {@link AgentGovernanceQueries.listRuns}, newest-first, with an opaque forward cursor.
*
* This is exactly {@link CursorPage} of {@link RunSummaryRow}: the rows are under `items` (not
* `runs`), `nextCursor` goes back as {@link ListRunsFilter.after}, and — the read-model being
* forward-only — `prevCursor` is always `null` and `hasPrev` always `false`.
*/
export type ListRunsResult = CursorPage<RunSummaryRow>;
/** A message belonging to a run, for {@link RunDetail}. */
export interface RunMessageRow {
id: string;
role: string;
content: string;
createdAt: string;
}
/** A tool call belonging to a run, for {@link RunDetail} (and the approvals subset). */
export interface RunToolCallRow {
toolCallId: string;
toolName: string;
toolType: string;
status: string;
input: unknown;
output: unknown;
error: string | null;
executionMs: number | null;
createdAt: string;
}
/** A usage ledger row belonging to a run, for {@link RunDetail}. */
export interface RunUsageRow {
modelId: string;
purpose: string;
inputTokens: number;
outputTokens: number;
costUsd: number | null;
createdAt: string;
}
/** The full trace of one run: the run row plus its messages, tool calls, pending approvals and usage. */
export interface RunDetail {
run: RunSummaryRow;
messages: RunMessageRow[];
toolCalls: RunToolCallRow[];
/** The subset of `toolCalls` still `pending_approval`. */
approvals: RunToolCallRow[];
usage: RunUsageRow[];
}
/** Filters for {@link AgentGovernanceQueries.pendingApprovals}. */
export interface PendingApprovalsFilter {
/** Restrict to a single acting ref (the owning run's `actor_ref`). */
actor?: string;
/** Cap the inbox; defaults to 50, clamped to 200 by the adapter. */
limit?: number;
}
/** One tool call awaiting a HITL decision, for the cross-thread approvals inbox (oldest first). */
export interface PendingApprovalRow {
toolCallId: string;
toolName: string;
input: unknown;
threadId: string;
/** The run this call belongs to, for a trace deep-link; `null` for a call recorded before run tracking. */
runId: string | null;
/** Who asked — the owning run's (or thread's) actor. */
actorRef: string;
/** Display label for `actorRef`, when an `actorDirectory` is bound and knows one. */
actorLabel?: string;
/** ISO timestamp the call was recorded (requested). */
requestedAt: string;
}
/** Inclusive UTC-day range for the run/tool aggregates; each bound `YYYY-MM-DD`, both optional. */
export interface ToolStatsRange {
/** Lower bound on the row's `created_at`/`started_at`; omit for no lower bound. */
from?: string;
/** Upper bound; omit for no upper bound. */
to?: string;
}
/** Per-tool call/failure/rejection/latency rollup over a range, highest call count first. */
export interface PerToolStatRow {
toolName: string;
toolType: string;
calls: number;
failed: number;
rejected: number;
/** Mean `executionMs` across calls that recorded one; `null` when none did. */
avgDurationMs: number | null;
/**
* Nearest-rank median / 95th percentile of the same `executionMs` samples; `null` when none did.
* Optional: an adapter that predates them omits them.
*/
p50ExecutionMs?: number | null;
p95ExecutionMs?: number | null;
}
/** Run count + failure count for one agent, part of {@link RunReliability.byAgent}. */
export interface RunAgentBreakdownRow {
/** `"—"`-free; a run recorded before agent tracking (or with no agent set) is grouped as `null`. */
agentName: string | null;
runs: number;
failed: number;
/** completed / runs for this agent, 0 when runs = 0. */
successRate: number;
}
/** One day's run volume + failure count, part of {@link RunReliability.trend}. */
export interface RunTrendPoint {
day: string;
runs: number;
failed: number;
}
/** Aggregated run reliability over a range — success / failure / cancel rates. */
export interface RunReliability {
runs: number;
completed: number;
failed: number;
cancelled: number;
running: number;
/** completed / runs, 0 when runs = 0. */
successRate: number;
/** failed / runs, 0 when runs = 0. */
failureRate: number;
/** cancelled / runs, 0 when runs = 0. */
cancelRate: number;
/** Mean duration of settled (completed/failed/cancelled) runs in ms; `null` when none settled. */
avgDurationMs: number | null;
/**
* Run/failure counts grouped by `agentName`, highest call count first. Optional: an adapter that
* predates this breakdown may omit it; the dashboard renders an empty section when absent.
*/
byAgent?: RunAgentBreakdownRow[];
/** Daily run/failure counts over the same range, oldest first. Optional, same reason as {@link byAgent}. */
trend?: RunTrendPoint[];
/**
* Time to first token and turn duration of the range's completed turns — a person's turns only, a
* delegation's child run is part of its parent's. Optional, same reason as {@link byAgent}.
*/
latency?: {
ttft: Latency;
duration: Latency;
};
/**
* Failed runs by their stream error code, most frequent first; a failure recorded before codes
* were is `'unknown'`. Optional, same reason as {@link byAgent}.
*/
failuresByCode?: RunFailureCodeRow[];
}
/** Thread metadata + LIFETIME usage rollup (not range-scoped) for the thread governance drill-down. */
export interface GovernanceThreadDetail {
threadId: string;
title: string;
actorRef: string;
/** Display label for `actorRef`, when an `actorDirectory` is bound and knows one. */
actorLabel?: string;
createdAt: string;
updatedAt: string;
/** `true` when the thread has been soft-deleted (the record is still readable here). */
deleted: boolean;
usage: {
totalTokens: number;
/** `null` when the thread has no priced usage at all (not the same as a $0 total). */
costUsd: number | null;
runCount: number;
messageCount: number;
};
/** Newest-first, capped by the adapter (mirrors {@link ListRunsResult} page sizing). */
runs: RunSummaryRow[];
/** Newest-first, capped by the adapter. */
messages: RunMessageRow[];
}
/**
* The governance read-model.
*
* Cost per row: a provider-reported `costUsd` always wins. Otherwise it is the same cache-aware
* estimate {@link import('./pricing-store.js').estimateCost} computes against the model's current
* pricing row — the UNCACHED input (`inputTokens - cacheWriteTokens - cacheReadTokens`) at
* `inputPricePer1m`, each cache bucket at its own per-1M rate (falling back to the input rate when
* unpriced), plus output at `outputPricePer1m`, each divided by 1e6.
*
* A model with no pricing row contributes 0 to the ROLLUPS ({@link ModelSpendRow},
* {@link ActorSpendRow}, {@link UsageTrendPoint}, whose `costUsd` is non-nullable) while its tokens
* still count. The per-row ledger keeps the distinction: {@link RunSummaryRow.costUsd} and
* {@link RunUsageRow.costUsd} stay `null` when nothing priced them.
*
* The `actorLabel` on every row carrying an `actorRef` is filled by the HTTP layer from the
* configured `actorDirectory` — an adapter never sets it.
*/
export interface AgentGovernanceQueries {
spendByModel(range: GovernanceRange): Promise<ModelSpendRow[]>;
spendByActor(range: GovernanceRange): Promise<ActorSpendRow[]>;
usageTrend(range: GovernanceRange): Promise<UsageTrendPoint[]>;
recentToolCalls(limit: number): Promise<ToolCallActivityRow[]>;
recentThreads(limit: number): Promise<ThreadActivityRow[]>;
/**
* Filterable, cursor-paginated run list, newest-first. Pagination is the ecosystem's forward-only
* cursor interface: `{ after, first }` in, {@link CursorPage} out (`items` / `nextCursor`, with
* `prevCursor: null` and `hasPrev: false`).
*/
listRuns(filter?: ListRunsFilter): Promise<ListRunsResult>;
/** The full trace of one run (run + messages + tool calls + approvals + usage), or `null` if unknown. */
runDetail(runId: string): Promise<RunDetail | null>;
/** Tool calls sitting `pending_approval`, oldest first (an inbox drains from the back). */
pendingApprovals(filter?: PendingApprovalsFilter): Promise<PendingApprovalRow[]>;
/** Per-tool call/failure/rejection/latency rollup over the range, highest call count first. */
perToolStats(range?: ToolStatsRange): Promise<PerToolStatRow[]>;
/** Success/failure/cancel rates + mean settled duration over the range. */
runReliability(range?: ToolStatsRange): Promise<RunReliability>;
/**
* The thread governance drill-down: metadata + lifetime usage rollup + recent runs/messages, or
* `null` when unknown. Optional — an adapter that predates this method leaves the route unmounted
* with a `501` rather than the dashboard breaking on a missing function.
*/
threadDetail?(threadId: string): Promise<GovernanceThreadDetail | null>;
/**
* OPTIONAL: generative-UI telemetry over the range — first-try success per tool and model, common
* errors, UI cost per turn and per day, time to first and complete frame (the dashboard's
* "Generative UI" page). Absent → `GET <governance>/genui` answers `501`.
*/
genuiStats?(range: GovernanceRange): Promise<GenuiStats>;
}
//# sourceMappingURL=governance-queries.d.ts.map