Skip to content
AgentStat

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, config and visible on existing ids are applied. Optional with a source.

sourceAgentSource

Events to plot, from createAgentStream(), useAgentStream(), connectSse() or your own source. History the source kept is replayed at its original times. Agents not in agents are added after them. See Event streams.

simulateDataboolean | { seed?: number }Default false

Demo data. Pass { seed } for a reproducible run. Never enable it in front of real telemetry.

maxHistoryPointsnumberDefault 100000

Memory 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.

windowSecondsnumber

Show the last N seconds on a wall-clock axis. Unset: up to the last 10 seconds of data, anchored to the newest sample.

tokenAxisMaxnumber

Fixed 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.

smoothingnumberDefault 0

Display-only smoothing of the line, in [0, 1). Health, anomalies and the tooltip always use raw data.

Layout and appearance

heightnumber | stringDefault 520

Number = pixels; any CSS length works. Width is always 100% of the container.

theme'light' | 'dark' | 'system'

'system' follows the OS. Unset: dark if styles.background is 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.

classNamestring

Class name on the root element.

styleReact.CSSProperties

Inline styles on the root element.

ariaLabelstring

Accessible name of the chart. Defaults to a description of the roster.

Anomalies and health

anomalyDetectionbooleanDefault false

Detect 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) => void

Once when an anomaly starts, and once more if it escalates to critical.

onAnomalyEnd(agentId: string, anomaly: Anomaly) => void

Once 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) => void

When the score moves by a point or more, or hallucinationRisk or latencyTrend changes. See Health score.

Interaction

onPointClick(agentId: string, point: AgentDataPoint) => void

A sample was clicked, or activated with Enter during keyboard inspection.

Deprecated

onSpikeClickDeprecated(agentId: string, point: AgentDataPoint) => void

Use onPointClick. It fires for any point, not only spikes.

widthDeprecatednumber

Not 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: AnomalyConfig
  • createAgent builds an agent with zeroed values. Omit color to use the theme palette.
  • demoAgents is a three-agent roster for simulateData.
  • useAgentStream returns the same stream for the component's lifetime.
  • detectAnomalies runs 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) => void for useChat. Options: clientTools, countTokens.
  • createAiSdkTap(sink, agentId, options?) returns (part) => void for streamText parts 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 one streamEvents (v2) call, with end(error?).
  • tapLangChainStream(sink, events, options) consumes a whole streamEvents stream.
  • 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 string agentId replaces 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 an AgentSource for 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.