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 backgroundSee 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).
What only Link Harness reports
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 processesKeep 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 diskSend 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 timeWhere 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 measurekeeps, and what each one holds - Security: What
lnk measurereads, keeps and sends, and what it never does - Decisions: Why
lnk measureworks the way it does - Troubleshooting: What to do when
lnk measureshows no history, can't watch, or sends nothing (Measure)