Measure what each agent costs, as it happens

See what each of your agents costs you as it happens, including memory, CPU, tokens and spend, and the same for this machine. It records numbers only, never a message, prompt, file, tool argument or answer. You need macOS or Linux and the measure plugin, which lnk up agents installs. It's free.

lnk measure                 # this machine as a tree: its agents, their processes
lnk measure --agent main    # one agent, now and over the last day
lnk measure start           # keep watching in the background

See what your agents use

lnk measure shows the machine, each agent here and each of an agent's processes, with the memory each holds now, the CPU it has used since it started, what crossed its proxy, and its tokens, turns and spend over the last day. lnk agent list shows each agent's memory and the day's spend too, as the watcher last saw them (none while it isn't running).

Measured from outside

Each agent is measured from outside, process by process, whichever harness runs it, so OpenClaw, Hermes and DeepSeek Harness are measured as Link Harness is. Its tokens are counted from its model's answers as they pass its proxy (below).

How long it takes to start and stop is timed by lnk agent itself, which starts and stops every harness. Only Link Harness reports what a harness alone can count, which is a turn's cost, and the time until the model is asked and until the first word of the answer.

See one agent over a day

This shows every measure now and over the last day, the day an hour at a time, and the agent's processes. Over a day, memory is the most it held, CPU and network are what they grew by, tokens, turns and spend are their sum, and a turn's times are their median. History is what the watcher kept, so start it first.

lnk measure --agent main              # by name
lnk measure --agent main --pid 4567   # one of its processes

Keep watching

The watcher looks at the machine every 10 seconds. It keeps every look for a day and a summary of each minute for 30 days, in

lnk measure start           # in the background, as lnk agent start does too
lnk measure stop

~/.lnk/measure/, for the machine and each agent. An agent's processes are shown and sent but not kept.

Its settings

These are Link's settings, and you can change them to keep more or less:

lnk measure settings                          # yours, and those of Link where you set none
lnk measure settings --keep-fine 6h --keep-minutes 7d # less history, less disk
lnk measure settings --every 30s
lnk measure settings --processes              # the processes of each agent kept too: about seven times the disk

Send them to your observability tools

The watcher sends every look as OTLP over HTTP, as JSON, to

lnk measure export --url http://localhost:4318      # an OpenTelemetry collector
lnk measure export --url https://api.honeycomb.io --header-file honeycomb.headers  # a file of its x-honeycomb-team header
lnk measure export --off

<url>/v1/metrics, so an OpenTelemetry collector, Grafana, Honeycomb or your own reads them as they are.

Where it may send

The endpoint is https://, or http:// to this machine (localhost, 127.0.0.1, [::1]): a plain http:// endpoint anywhere else, such as a collector on another machine or in another container (http://otel-collector:4318), is refused, since your headers would cross the network in the clear. Give that collector a TLS receiver, or one on this machine to forward to it.

Each unit

Each unit is a resource, named by link.unit.id, link.unit.kind (machine, agent, sandbox or process; or relay, for what a relay sends of itself), link.unit.parent, link.unit.name, link.machine.id and, for an agent, link.owner.id, whose it is, and link.team.ids, the IDs of the teams it's in (an array of strings), when it's in one.

How a measure adds up

The last word of a measure's name says how it adds up to the unit above it. sum adds, max takes the largest, and own doesn't add at all.

What's measured

Each measure, by its name, with its unit. Turns, spend and a turn's times come from Link Harness alone, and another harness is measured from outside, its tokens included.

Memory and CPU

link.memory.used.sum and link.memory.peak.max, in bytes: in use now, and the most seen. link.cpu.used.sum, in core-seconds: used since it started.

GPU

link.gpu.used.sum and link.gpu.memory.sum, in GPU-seconds and bytes: GPU time used since the last look, and GPU memory in use, where there's a GPU that says (below).

Disk

link.disk.used.sum and link.disk.files.sum, in bytes and files: an agent's folders, looked at every 10 minutes. link.disk.read.sum and link.disk.written.sum, in bytes: read from and written to disk.

Network, processes and ports

link.network.in.sum and link.network.out.sum, in bytes: through an agent's proxy, or the machine's own network. link.processes.sum and link.ports.sum, a count: running, and TCP ports listened on.

Turns, tools and errors

link.turns.sum, link.tool_calls.sum and link.errors.sum, a count: turns answered, tools called, and what failed.

Fallbacks

link.fallbacks.off.sum, .window.sum and .overloaded.sum, a count: turns the agent's fallback model answered (lnk agent start --fallback), by why: its model was off, the turn's prompt was past its model's window, or its model's provider answered busy or failing (429 or a 5xx).

Tokens and model requests

link.tokens.input.sum, .output.sum, .cached.sum and .cache_written.sum, in tokens, as the provider counts them: the prompt (cached or not), the answer, and of the prompt, what was read from the provider's cache and what was written to it (Anthropic's, which it charges above the prompt's price). link.requests.model.sum and link.requests.unmetered.sum, a count: requests for a model's answer its proxy carried; of them, answers that gave no usage, whose tokens aren't counted.

Cost and a turn's times

link.cost.sum, in micro-dollars: what the provider said a turn cost, never estimated. link.time.to_model.own, .first_token.own and .turn.own, in seconds: from a message in to the model asked, to the answer's first piece out, to the whole answer.

A relay's

link.relay.connections.sum, a count of relay agent connections, including standbys; link.relay.requests.sum, public requests received by a relay; and link.relay.bytes.sum, encoded payload bytes carried between a relay and its agents. link.hooks.undelivered.sum, a relay's, never a machine's, counts webhooks it couldn't hand on through its tunnel (the relay).

Starts and stops

link.time.start_cold.own, .start_warm.own and .stop.own, in seconds: a start from cold (an agent's first since the machine started) or warm (any start after), until its harness answers, in the background or in a terminal; a stop, until none of its main processes is left, or for one in a terminal, lnk agent stop's until it has ended.

When they're timed

Starts and stops are timed for every harness, each time lnk agent starts or stops it (start, restart, stop, and lnk up): a start the system makes itself, as a machine starts or you log in, isn't timed. A stop that takes more than 10 seconds isn't kept.

GPUs

Where a machine has a GPU that says what it uses, the watcher reads it. Without a GPU, or with one that says nothing, no GPU measure is shown.

NVIDIA

nvidia-smi gives each GPU's use and memory, and each process's memory on them. A process's GPU time there isn't said, so only the machine's is.

Linux's own drivers and a Mac

For AMD, Intel and others: each card's use and memory in /sys/class/drm, and each process's time and memory on its GPU in /proc/<pid>/fdinfo, as the kernel counts them. On a Mac, ioreg gives the GPU's use and memory, the machine's only.

How GPU time adds up

The machine's GPU time over a look is how busy its GPUs were times the look's length, so two GPUs at half count as one. A process's is what its kernel says it grew by since the last look, and an agent's is its processes' added up.

Tokens

Every agent's tokens are counted as the provider counts them, whichever harness it runs. Its model's requests pass through its sandbox's proxy to the proxy's model route, served by the models plugin outside the sandbox, which adds the key, and reads the usage each answer gives as it passes it on, never holding it back or changing it.

Anthropic

usage in a whole answer; in a stream, message_start's and message_delta's. The prompt is its three parts: what wasn't cached, what was read from the cache (cached) and what was written to it (cache written), as Link Harness counts it.

OpenAI, OpenRouter and local runtimes

For OpenAI (Chat Completions and Responses), OpenRouter, and a local runtime's OpenAI-compatible API: usage, in a stream the last event's. Ollama's own API gives prompt_eval_count and eval_count.

A stream without usage

A Chat Completions stream gives its usage only when its request asks for it (stream_options.include_usage), and the proxy changes no request. An answer without one counts in link.requests.unmetered.sum, so a gap shows as one rather than as fewer tokens.

Where they're added up

The route adds each answer's counts to the agent's, a file of the measure plugin's in Link's settings (measure/tokens/<agent ID>.json), which the watcher reads.

An agent with no sandbox

An agent with no sandbox has no proxy, so its tokens are what its harness reports, which Link Harness does. A harness's own count of the tokens its route counts is never added too, so nothing is counted twice. Spend is never estimated from tokens: it's what Link Harness says the provider charged.

For scripts

Each measure is {"now", "day"}, its value now (null for what's counted per turn) and over the last day. The contracts page has the rest.

lnk measure --json          # at, units: id, kind, parent, name, owner, teams, measures
lnk measure --agent main --json     # and hours: its last day, an hour at a time

Where things live

The watcher's files are all under ~/.lnk/measure/, except what a harness reports of itself and what lnk agent timed. settings.toml there holds your settings, and your endpoint and its headers; watch.log is the watcher's log.

What the watcher kept

~/.lnk/measure/fine/ and minutes/ hold what the watcher kept, a file an hour and a file a day: <date>-<hour>.jsonl while it's written, compressed to .jsonl.zst (zstd) once its span is over.

Each file's lines

Each file is lines of JSON. Its first names its measures, {"measures": ["memory.used.sum", ...]} (link. left out); each line after is one look, {"t": <seconds since 1970>, "units": [...]}, each unit its id, and its values in the order the first line names ("v": [...], null for one left out). What's counted per turn is "e", each measure's values by its place in that order.

Values left out

A value is left out when it's zero, but a running total's: a total is kept, zero too, and left out only when it hasn't changed since the unit's last line in the file. So a day, read back or as the watcher keeps it, has no zero but a total's, and lnk measure --json shows the same either way.

A unit's names

A unit's kind, parent, name, owner, teams and s (when its totals began, in nanoseconds) are written on its first line in the file and when they change. A reader carries each forward from the unit's line before.

now.json and day.json

Beside them, now.json is the watcher's last look and day.json the last day as it summed it up, each minute, so lnk measure reads only the looks since rather than the whole day.

Outside ~/.lnk/measure/

In each harness's home, .lnk/measures.jsonl holds what it reports of itself, for the watcher to read: emptied once the watcher has read it all. In each agent's settings, ~/.lnk/agents/<name>/times.jsonl holds how long each of its starts and stops took.

More about it

Where the rest of what lnk measure does is written:

  • Every flag is in lnk measure --help.
  • Security has what's kept and sent, and what never is.
  • Decisions has why it works this way.
  • Troubleshooting has what to do when a number is missing.

Docs

  • Configuration: The files lnk measure keeps, and what each one holds
  • Security: What lnk measure reads, keeps and sends, and what it never does
  • Decisions: Why lnk measure works the way it does
  • Troubleshooting: What to do when lnk measure shows no history, can't watch, or sends nothing (Measure)