Hermes telemetry
Stream Hermes Agent (Nous Research’s open-source agent harness) runs into Latitude as traces. After setup, each Hermes turn appears in your project’s Traces view with user prompts, model turns, tool calls and results, the tools the agent was offered, memory reads and writes, delegated subagents, token usage, cost, timing, and the real system prompt that reached the model.Prerequisites
- A Latitude account with a project
- Hermes Agent installed locally
pip(Hermes already runs on Python — the plugin uses only the standard library pluscertifi, which Hermes already ships)
Install
- In Latitude, copy your project slug from the project sidebar.
- Create or copy an API key from Settings → API Keys.
- Install the plugin:
- Enable it by adding
latitudeto the enabled-plugins list in~/.hermes/config.yaml, and turn on Hermes’s reasoning-delta forwarding so time-to-first-token is measurable:
stream_reasoning_deltas: true is required for time-to-first-token. It is Hermes’s own
setting, off by default, and without it TTFT is missing from most spans — see
Time to first token.hermes_agent.plugins entry point — there are no files to copy.
Enable via
config.yaml, not hermes plugins enable latitude. Hermes’s runtime loads
pip/entry-point plugins, but its hermes plugins list/enable/disable commands scan only
bundled and ~/.hermes/plugins/ directory plugins — so they report a pip-installed plugin as
“not installed or bundled” even though it loads fine (hermes-agent#23802).
The config.yaml entry above is the reliable way to turn it on.The plugin must be installed into the same Python that runs Hermes. The official installer
puts Hermes in its own venv (
~/.hermes/hermes-agent/venv) that ignores your shell’s Python, so
a plain pip install from another interpreter (system, pyenv, mise, …) won’t be discovered.
Install into Hermes’s venv instead:- Set your credentials in the environment, or add them to
~/.hermes/.env(Hermes loads it at startup):
The plugin sends to Latitude Cloud (
https://ingest.latitude.so) by default. If you
run a self-hosted or local Latitude, also set LATITUDE_BASE_URL to your ingest
origin only — for example http://localhost:3002 on a local dev stack — with no
/v1/traces suffix (the plugin appends it). The API key and project slug must belong
to that same instance.Upgrade
Upgrade with the same tool the plugin was installed with, into the same interpreter that runs Hermes:The official installer’s venv ships without
pip, so pip install -U fails there with
No module named pip — use the uv form. The plain pip form applies when you installed
Hermes yourself into an environment you manage.-U:
config.yaml and .env are per-profile. To read the version now on disk, ask the interpreter that
runs Hermes — importlib.metadata is always available, whereas pip show is not:
Restart Hermes
Python resolves entry-point plugins once, when the process starts. A running Hermes goes on executing the version it loaded, and the upgraded files sit on disk unread. A CLI session picks the new version up on its next run. A long-running process never does. The gateway (Slack, Discord, …) is that long-running process, and it is usually supervised, so it will not restart on its own. On macOS the official installer registers it with launchd — find the label, then restart it:kickstart -k stops the running instance first. Don’t use launchctl stop on its own: the job sets
KeepAlive, so it does come back, but ThrottleInterval holds it down for 30 seconds first. Under
any other supervisor, restart the service however you normally would.
Scheduled agents run inside the gateway. The cron ticker is part of that process rather than a
separate scheduler, so cron-driven sessions keep using the plugin version the gateway loaded. If
your agent only ever runs on a schedule, you still need the gateway restart.
Confirm the new version is the one running
The check above reads the disk, and a stale process is precisely the case where the disk and the running code disagree. For the running answer, send a message and open the new trace: every span carrieshermes.plugin.version in its metadata, and the resource carries service.version. Both
come from the code that is executing, so they settle it.
Verify
Run Hermes and send a message to your agent, then open your Latitude project and go to Traces. The new trace should appear within a few seconds. If nothing arrives, setLATITUDE_DEBUG=true in ~/.hermes/.env and run again to see the plugin’s logging: it logs every export with its HTTP status. (hermes plugins list does not show pip-installed plugins — see the install note — so it can’t be used to confirm the plugin is loaded.)
Structural-only telemetry
If you want trace structure without prompt, response, or tool content, set:Disable or uninstall
To pause telemetry without removing anything, set the environment variable in~/.hermes/.env:
latitude from plugins.enabled in ~/.hermes/config.yaml. (hermes plugins disable latitude doesn’t work for pip-installed plugins — see the install note.)
To remove the integration entirely, drop it from plugins.enabled and uninstall the package:
~/.hermes/.env credentials are left in place so a re-install is one step.)
Configuration
Every setting can be given in two places, and the environment wins:- an environment variable — in your shell or in
~/.hermes/.env; - a key under
plugins.entries.latitude.settingsin~/.hermes/config.yaml.
~/.hermes/config.yaml, ~/.hermes/.env) are the default profile’s; a named profile keeps its own pair under ~/.hermes/profiles/<name>/, and that is the one to edit when a profile is active.
Credentials and transport
Telemetry stays off until both an API key and a project are set.
Switches
Content
Privacy
Features
Identity and grouping
How it works
Hermes loads pip-installed plugins via thehermes_agent.plugins entry point and calls the module’s register(ctx), which subscribes to its lifecycle hooks: pre_api_request / post_api_request / api_request_error, pre_llm_call / post_llm_call, pre_tool_call / post_tool_call, on_stream_start / on_stream_delta / on_stream_end, on_session_start / on_session_end / on_session_reset / on_session_finalize, and subagent_start / subagent_stop.
Each Hermes turn becomes one trace:
gen_ai.*), so they render natively in the trace viewer. Spans are shipped as they finish, batched, and retried on transient ingest errors. The plugin is fail-open: a telemetry error never affects your agent, and it depends only on the Python standard library plus certifi, which Hermes already ships.
Running several agents in one project
Send every agent to the same project and tell them apart with tags. The plugin derives these automatically:
Add your own with
tags / metadata; yours are appended to the derived ones, never replacing them.
Recommended layout for several agents: give each agent its own Hermes profile (sessions, memory and credentials are then already isolated), set agent.name and agent.version in each profile’s config.yaml, and set service_name only if you want the Service column to read as the agent rather than the harness.
Comparing two versions of one agent. The version lands on two surfaces on purpose, because they answer different questions:
- The tag is a breakdown dimension, so one analytics query gives you a row per version:
queryAnalytics { stream: "sessions", metric: { kind: "avg", field: "cost" }, breakdown: "tag" }. It is also a session filter, so an experiment withtags contains 2.2.0against a baseline oftags contains 2.1.0gives you a two-variant score comparison. (If several agents in the project could share a version string, combine it with the agent-name tag.) - The metadata key
metadata.hermes.agent.versionis filter-only, and stays clean when you have dozens of versions and don’t want dozens of tags.
Memory
If your agent uses Hermes’s built-in memory, its reads and writes appear as memory operations on the Memory page and in each session’s memory footprint.- The store is
hermes/<profile>, with one record per store file:MEMORY.mdandUSER.md. - Memory enters the system prompt as a frozen snapshot at session start, so it is recorded as one read per session.
- A write is recorded after the
memorytool call succeeds, by reading the file back off disk — so the recorded body is exactly what landed. Latitude turns the sequence of bodies into a diff view and per-entry history. - Turn it off with
LATITUDE_HERMES_MEMORY=0, or keep the structure without the bodies withLATITUDE_HERMES_MEMORY_CONTENT=0. - If you have configured an external memory provider (Mem0, Supermemory, Honcho, …) in
config.yaml, the built-in files are no longer the live store and memory telemetry turns itself off.
Identified users
On a gateway platform (Slack, Discord, …) each turn carries the platform’s member id as the user, so per-user analytics and the Users page work. Hermes only exposes that id to plugins — no display name, no email — so that is what you see. When a platform uses an email address as its user id, the email is recorded too.Time to first token
isStreaming is set on every streaming call. Time-to-first-token needs one setting in Hermes’s own config, because Hermes decides which streamed tokens reach a plugin:
LATITUDE_HERMES_STREAM_TTFT=0 disables our subscription entirely if you would rather not pay for the per-token hook at all.
Token usage and cost
Hermes’s/usage and Latitude will not show the same numbers, and neither is wrong:
- Background reviews. A background review runs a full conversation loop on its own agent object in a separate thread. Latitude sees those calls;
/usagereads the main agent’s counters, so it doesn’t. Latitude = main loop + background reviews. - Reasoning tokens. Hermes’s “Output tokens” includes reasoning; Latitude shows output and reasoning as separate figures.
- Auxiliary calls. Approvals, context compaction and title generation don’t fire plugin hooks. Latitude recovers their usage from Hermes’s own per-task ledger at the end of each session, so session totals match
session_model_usagein~/.hermes/state.db— the arbiter if you ever want to check by hand. - Cost on a subscription route. On an OAuth/subscription route Hermes reports the call as included at $0, while Latitude shows the equivalent list price from its model catalog. The span records which regime it was in, so the figure is explainable rather than mysterious.
Captured data and privacy
By default Latitude receives what it needs to reconstruct a Hermes run: prompts, responses, the system prompt, tool definitions, tool input and output, memory record bodies, model metadata, token usage and timing. Secret redaction is on by default. Everything content-bearing goes through Hermes’s own secret redactor on the way out, with URL-credential redaction enabled — so an API key echoed by a terminal tool is masked before it leaves the machine. Turn it off withLATITUDE_HERMES_REDACT_SECRETS=0 if you’d rather see raw values. If the redactor can’t be loaded, the span says so (hermes.redaction.applied=false) rather than quietly claiming protection it didn’t apply.
Keeping a specific attribute local. LATITUDE_HERMES_REDACT_ATTRIBUTES replaces the whole value of any attribute you name — for example gen_ai.memory.records,gen_ai.tool.call.result, or a pattern like /^gen_ai\.tool\.call\./. The attribute is still sent, with its value masked, so you can see what the plugin exported.
Structure only. LATITUDE_NO_CONTENT=true drops every content-bearing attribute: you keep timing, model, token usage and run structure, and lose prompts, responses, tool I/O and memory bodies.
Telemetry runs for each turn until disabled or uninstalled. Disable it before working with material you do not want sent to Latitude.
Troubleshooting
No traces appear. Confirmlatitude is in plugins.enabled in ~/.hermes/config.yaml (hermes plugins list never shows a pip-installed plugin — see the install note), check that the API key and project slug are correct, then set LATITUDE_DEBUG=true and send a new message: the plugin logs each export and its HTTP status.
Traces arrive, but they look like an older version of the plugin. The running Hermes is still
executing the plugin version it loaded at startup — upgrading the package does not affect a live
process. Restart Hermes, and the gateway specifically if you run one; see Upgrade.
LATITUDE_DEBUG will not reveal this: the old version keeps exporting successfully, so the log
shows 200s throughout.
Need more diagnostics. Set LATITUDE_DEBUG=true in ~/.hermes/.env and trigger another run.
Traces show timing but no content. Structural-only mode is enabled. Remove LATITUDE_NO_CONTENT from ~/.hermes/.env.
A value looks masked. Secret redaction is on by default; LATITUDE_HERMES_REDACT_SECRETS=0 turns it off. A value that is exactly ****** was matched by LATITUDE_HERMES_REDACT_ATTRIBUTES.
Memory operations are missing. Check that the agent actually wrote memory in the session, that LATITUDE_HERMES_MEMORY isn’t 0, and that config.yaml doesn’t configure an external memory.provider — that turns built-in memory telemetry off.