API reference
Everything here is checked against the published types of @dan-build/agentstat 0.6.0.
AgentStat props
Data
agentsAgent[]Default[]The roster: which agents exist and in what order. Changes to
name,color,configandvisibleon existing ids are applied. Optional with asource.sourceAgentSourceEvents to plot, from
createAgentStream(),useAgentStream(),connectSse()or your own source. History the source kept is replayed at its original times. Agents not inagentsare added after them. See Event streams.simulateDataboolean | { seed?: number }DefaultfalseDemo data. Pass
{ seed }for a reproducible run. Never enable it in front of real telemetry.maxHistoryPointsnumberDefault100000Memory cap per agent and series. Retention is time-based (1.5× the visible window); this only bounds very high update rates.
What's plotted
metric'auto' | 'progress' | 'tokens' | 'both'Default'auto''auto'plots progress once every visible agent reports it, otherwise token rate.'both'puts progress on the left axis and token rate on the right.windowSecondsnumberShow the last N seconds on a wall-clock axis. Unset: up to the last 10 seconds of data, anchored to the newest sample.
tokenAxisMaxnumberFixed top of the token-rate axis. By default it rises at once for a new high and eases down only after the peak stays well below it.
referenceLine{ value: number; label?: string; color?: string }A dashed horizontal line against the left axis.
smoothingnumberDefault0Display-only smoothing of the line, in
[0, 1). Health, anomalies and the tooltip always use raw data.
Layout and appearance
heightnumber | stringDefault520Number = pixels; any CSS length works. Width is always 100% of the container.
theme'light' | 'dark' | 'system''system'follows the OS. Unset: dark ifstyles.backgroundis dark, else light. See Theming.styles{ background?: string; borderColor?: string; textColor?: string; gridColor?: string }Color overrides. Any CSS color, including
var(--token).panel'auto' | 'always' | 'never'Default'auto'The per-agent panel (rate, health, status).
'auto'hides it on charts under 560 × 340 px.classNamestringClass name on the root element.
styleReact.CSSPropertiesInline styles on the root element.
ariaLabelstringAccessible name of the chart. Defaults to a description of the roster.
Anomalies and health
anomalyDetectionbooleanDefaultfalseDetect stalls, spikes and thrashing: draws markers, announces them and fires
onAnomaly. See Anomaly detection.anomalyConfigPartial<AnomalyConfig>Threshold overrides, merged over
DEFAULT_ANOMALY_CONFIG.onAnomaly(agentId: string, anomaly: Anomaly) => voidOnce when an anomaly starts, and once more if it escalates to critical.
onAnomalyEnd(agentId: string, anomaly: Anomaly) => voidOnce when an anomaly ends (5 s after it was last seen, when detection is switched off, or when the agent is removed), with its last reading.
onHealthChange(agentId: string, health: HealthMetrics) => voidWhen the score moves by a point or more, or
hallucinationRiskorlatencyTrendchanges. See Health score.
Interaction
onPointClick(agentId: string, point: AgentDataPoint) => voidA sample was clicked, or activated with Enter during keyboard inspection.
Deprecated
onSpikeClickDeprecated(agentId: string, point: AgentDataPoint) => voidUse
onPointClick. It fires for any point, not only spikes.widthDeprecatednumberNot read. The chart is always as wide as its container.
Ref
interface AgentStatRef {
/** Report a partial sample; omitted fields keep their previous value. */
updateAgent(id: string, sample: AgentSample): void;
/** Positional form, kept for compatibility. */
updateAgent(
id: string,
tokensRate: number,
progress: number,
status: AgentStatus,
): void;
/** Forward a raw stream event; use this or updateAgent's tokensRate, not both. */
push(id: string, event: AgentEvent): void;
/** Token count, TTFT, inter-token latency and open tool, from push events. */
getStats: (id: string) => AgentStreamStats | undefined;
getHealth: (id: string) => HealthMetrics | undefined;
/** Anomalies active now (empty with detection off). */
getAnomalies: (id: string) => Anomaly[];
/** Values drawn at the live tip; progressKnown is false until progress is reported. */
getLiveMetrics: (id: string) => {
tokensRate: number;
progress: number;
status: AgentStatus;
progressKnown: boolean;
} | undefined;
}Methods for an id the chart doesn't know return undefined (or []). The ref is null until the chart mounts, so call it as ref.current?.….
Helpers
createAgent(id: string, name: string, color?: string): Agent
demoAgents: Agent[]
createAgentStream(): AgentStream
useAgentStream(): AgentStream
detectAnomalies(tokens, changes, currentStatus, now, cfg?): Anomaly[]
DEFAULT_ANOMALY_CONFIG: AnomalyConfigcreateAgentbuilds an agent with zeroed values. Omitcolorto use the theme palette.demoAgentsis a three-agent roster forsimulateData.useAgentStreamreturns the same stream for the component's lifetime.detectAnomaliesruns the detectors on plain arrays. See Anomaly detection.
Types
Agents and samples
type AgentStatus = 'active' | 'stuck' | 'thinking' | 'waiting' | 'complete' | 'hallucinating';
type ChartMetric = 'auto' | 'progress' | 'tokens' | 'both';
type Agent = {
id: string;
name: string;
/** Any CSS color. Omit to use the theme palette. */
color?: string;
/** @deprecated Not read. */
data?: AgentDataPoint[];
current: {
tokensRate: number;
progress: number;
status: AgentStatus;
latencyMs?: number;
confidenceScore?: number;
errorCount?: number;
};
config?: {
expectedTokensPerSec?: [number, number];
/** @deprecated Not read. */
maxHallucinationThreshold?: number;
};
/** Initial visibility (default true). */
visible?: boolean;
};
type AgentSample = {
tokensRate?: number;
progress?: number; // 0–100
status?: AgentStatus;
latencyMs?: number; // enables latencyTrend
confidenceScore?: number; // 0–1, enables hallucinationRisk
errorCount?: number;
};
type AgentDataPoint = {
time: number; // performance.now() clock
tokensRate: number;
progress: number;
status: AgentStatus;
latencyMs?: number;
confidenceScore?: number;
errorCount?: number;
};Events
type AgentEvent =
| { type: 'start'; time?: number }
| {
type: 'tokens';
count?: number;
total?: number;
since?: number;
time?: number;
}
| { type: 'tool-start'; name?: string; time?: number }
| { type: 'tool-end'; name?: string; error?: boolean; time?: number }
| { type: 'error'; time?: number }
| { type: 'finish'; time?: number };
type AgentStreamStats = {
totalTokens: number;
ttftMs: number | null;
interTokenMs: number | null;
tool: string | null; // innermost open tool call ('' if unnamed)
statusMs: number; // time in the current status
};Sources and streams
type AgentStreamMessage = { seq: number } & (
| { type: 'event'; id: string; event: AgentEvent }
| {
type: 'agent';
id: string;
name?: string;
color?: string;
config?: Agent['config'];
}
| { type: 'remove'; id: string }
);
interface AgentSource {
subscribe(
listener: (m: AgentStreamMessage) => void,
after?: number,
): () => void;
}
interface AgentStream extends AgentSource {
push(agentId: string, event: AgentEvent): void;
describe(
agentId: string,
info: { name?: string; color?: string; config?: Agent['config'] },
): void;
remove(agentId: string): void;
}Health and anomalies
type HealthMetrics = {
score: number;
tokenEfficiency: number;
stability: number;
hallucinationRisk: number | null;
latencyTrend: 'improving' | 'stable' | 'degrading' | null;
};
type AnomalyKind = 'stall' | 'spike' | 'thrash';
type AnomalySeverity = 'warning' | 'critical';
interface Anomaly {
kind: AnomalyKind;
severity: AnomalySeverity;
t: number; // performance.now() clock
message: string; // e.g. "stalled 8s while active"
value: number; // rate for stall/spike, count for thrash
baseline?: number; // spikes: the usual level compared with
}
interface AnomalyConfig {
stallRateThreshold: number;
stallDurationMs: number;
thinkingStallMs: number;
waitingStallMs: number;
spikeRatio: number;
spikeMinIncrease: number;
spikeMinSamples: number;
spikeBaselineMs: number;
spikeMinBaselineRate: number;
spikeMaxEpisodeMs: number;
thrashChangeCount: number;
thrashWindowMs: number;
thrashBenignStatuses: AgentStatus[];
}The defaults and what each field means are on Anomaly detection.
Telemetry
type AgentTelemetry = {
id: string;
name: string;
status: AgentStatus;
statusMs: number;
tool: string | null;
tokensRate: number;
totalTokens: number;
ttftMs: number | null;
health: number | null;
anomalies: Anomaly[];
history: { status: AgentStatus; fromMs: number; toMs: number }[];
};
interface TelemetryOptions {
agents?: Agent[];
anomalyDetection?: boolean; // default true
anomalyConfig?: Partial<AnomalyConfig>;
historySeconds?: number; // default 300
onAnomaly?: (agentId: string, anomaly: Anomaly) => void;
onAnomalyEnd?: (agentId: string, anomaly: Anomaly) => void;
onHealthChange?: (agentId: string, health: HealthMetrics) => void;
}
type TelemetryComponentProps = {
theme?: 'light' | 'dark' | 'system'; // default 'system'
className?: string;
style?: React.CSSProperties;
};The hook and components are described on Without the chart.
Entry points
The main entry is client-only. The other entries have no dependencies and run anywhere.
@dan-build/agentstat
import {
AgentStat, createAgent, demoAgents,
createAgentStream, useAgentStream,
useAgentTelemetry, AgentStatusBadge, AnomalyBanner, AgentRoster, AgentTimeline,
useAnomalyAnnouncer, timelineBars,
detectAnomalies, DEFAULT_ANOMALY_CONFIG,
type AgentStatProps, type AgentStatRef,
type Agent, type AgentDataPoint, type AgentSample, type AgentStatus, type ChartMetric,
type AgentEvent, type AgentStreamStats,
type AgentSource, type AgentStream, type AgentStreamMessage,
type HealthMetrics, type Anomaly, type AnomalyKind, type AnomalySeverity, type AnomalyConfig,
type AgentTelemetry, type TelemetryOptions, type TelemetryComponentProps,
} from '@dan-build/agentstat';@dan-build/agentstat/ai-sdk
import {
createChatTracker, createAiSdkTap, tapAiSdkStream,
type ChatTrackerOptions, type ChatMessage, type AiSdkPart,
type AdapterOptions, type EventSink, type AgentEvent,
} from '@dan-build/agentstat/ai-sdk';createChatTracker(sink, agentId, options?)returns(messages, status) => voidforuseChat. Options:clientTools,countTokens.createAiSdkTap(sink, agentId, options?)returns(part) => voidforstreamTextparts or UI message chunks.tapAiSdkStream(sink, agentId, parts, options?)consumes a whole stream; resolves when it ends.
@dan-build/agentstat/langchain
import {
createLangChainCallbacks, createLangChainTap, tapLangChainStream, bySubgraph,
type LangChainOptions, type AgentIdFor, type LangChainRun,
type LangChainCallbackHandler, type LangChainStreamEvent, type LangChainTap,
type AdapterOptions, type EventSink,
} from '@dan-build/agentstat/langchain';createLangChainCallbacks(sink, { agentId, countTokens? })returns a callback handler.createLangChainTap(sink, options)returns a tap for onestreamEvents(v2) call, withend(error?).tapLangChainStream(sink, events, options)consumes a wholestreamEventsstream.bySubgraph(fallback)routes runs to sub-agents.
@dan-build/agentstat/otel
import {
createOtelProcessor,
type OtelOptions, type OtelSpan, type OtelSpanInfo, type AgentStatSpanProcessor,
type AdapterOptions, type EventSink, type AgentEvent,
} from '@dan-build/agentstat/otel';createOtelProcessor(sink, { agentId?, countTokens? })returns a span processor. A stringagentIdreplaces the'agent'fallback; a function decides every span.
@dan-build/agentstat/ag-ui
import {
createAgUiTap,
type AgUiEvent, type AdapterOptions, type EventSink,
} from '@dan-build/agentstat/ag-ui';createAgUiTap(sink, agentId, options?)returns(event) => void.
@dan-build/agentstat/sse
import {
createSseHandler, connectSse, createAgentStream,
type SseHandlerOptions, type SseSourceOptions,
type AgentSource, type AgentStream, type AgentStreamMessage,
type Agent, type AgentEvent,
} from '@dan-build/agentstat/sse';createSseHandler(source | (request) => source, { heartbeatMs?, maxQueued? })returns a Fetch-API handler(request) => Promise<Response>. Defaults: 15,000 ms and 1,000 messages.connectSse(url, { withCredentials? })returns anAgentSourcefor the browser.createAgentStream()is the same stream as the main entry's, importable from server code.
AdapterOptions is { countTokens?: (text: string) => number } (default: characters ÷ 4). EventSink is anything with push(agentId, event), such as a stream.