# Agent activity

Who is working right now: each agent, what it is doing in plain words, and its state in words beside the dot, so state is never colour alone. An agent that isn't working says only its state ("Done", "Standing by"); nothing is made up for it to be doing. Agents stay in the order you give. For background jobs as one pill, use TaskTray. An agent with `lastHeardAt` that has been silent past `staleAfter`, or whose host is offline, isn't shown as running: it says "No update since 10:42" or that its host is offline.

- Registry name: `agent-activity` (`@candor/agent-activity`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/agent-activity

## Install

```bash
npx candor-ui add agent-activity
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/agent-activity`.
It brings in: agent-avatar, locale, relative-time, run-heartbeat, status, status-dot, work, utils.

## Example

```tsx
"use client"

import { AgentActivity } from "@/components/agent-activity"
import type { AgentActivityEntry } from "@/lib/work"

const minutesAgo = (m: number) => new Date(Date.now() - m * 60_000).toISOString()

const agents: AgentActivityEntry[] = [
  { agent: { id: "lead", name: "Lead", role: "lead" }, doing: "Talking with you", status: "running", statusLabel: "Working" },
  { agent: { id: "writer", name: "Writer", role: "dev" }, doing: "Drafting section 2, the pricing table", status: "running", statusLabel: "Working", since: minutesAgo(4) },
  // Its runner can't be reached: it isn't shown as working.
  { agent: { id: "indexer", name: "Indexer", role: "data" }, doing: "Re-indexing the catalogue", status: "running", lastHeardAt: minutesAgo(6), hostOffline: { host: "Runner 3", since: minutesAgo(5) } },
  { agent: { id: "checker", name: "Checker", role: "qa" }, status: "queued", statusLabel: "Waiting" },
  { agent: { id: "planner", name: "Planner", role: "architect" }, status: "succeeded", statusLabel: "Done" },
  // Not working: no "doing" line is made up for it.
  { agent: { id: "tester", name: "Tester", role: "qa" }, status: "idle", statusLabel: "Standing by" },
]

export default function Example() {
  return <AgentActivity className="max-w-sm" agents={agents} />
}
```

## <AgentActivity>

Props:

- `agents: AgentActivityEntry[]`: One entry per agent: who, what it's doing now, its status and your word for it.
- `label?: string | undefined`: The list's accessible name. Defaults to "Agents".
- `staleAfter?: number | undefined` (default `STALE_AFTER`): How long a running agent may be silent before it's no longer called running, in milliseconds. Defaults to two minutes.
- `className?: string | undefined`

## Strings (`agentActivity`)

The fixed words, English by default. Override any of them under `agentActivity` in `<LocaleProvider strings={{ agentActivity: { … } }}>`; the table is exported as `agentActivityStrings`.

- `label`: Agents
- `none`: No agents yet.
- `since`: since {time}

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
