Trajectory observability
A query on its own says little about whether a search was any good. Jevlin carries enough of the agent's context with each search that the platform can group one task's searches and show what the agent was doing when it asked.
The vocabulary
- Session: one conversation in an agent. A subagent has a session of its own, and from Claude Code, Codex and opencode it also carries its parent's.
- Turn: one exchange in a session: what you asked, what the agent did, what it concluded.
- Call: one search inside a turn.
- Window: which context window of the session, counted up at each compaction.
- Seq: a call counter within the session.
- Trace: the small record a search carries with these ids and the assistant text before it.
- Trajectory: the session read back in order, turn by turn: the agent's prompts, its searches, what came back, and what it concluded, with the nested structure flattened so you read it as the path the agent took.
The trace
| field | carries |
|---|---|
harness | Which agent: claude-code, codex, cursor, and so on. |
session_id, turn_id, call_id | The agent's ids, hashed with SHA-256 before they leave the machine. |
parent_session_id | On a subagent's search only: the hashed session of the agent that started it. |
window | Which context window of the session, after compactions. |
seq | A call counter. |
history | The assistant text just before the search, scrubbed of secrets, last 32 KiB. Codex and Hermes send none. |
host_meta | Agent-specific metadata. |
The real ids never leave the machine; the router sees only the hashes, so it can group searches without learning which conversation they came from. The trace lists every field and every scrub rule.
Lineage files
The hooks keep a lineage file per workspace, and in Cursor per conversation, under ~/.jevlin/sessions. Each search reads it to learn which session, window and turn it belongs to, so the searches of one task line up even across context compactions and subagents. Only the agent that wrote a file reads it, so a second agent in a subdirectory never sends the first one's narration. Pi reads its ids back from the session Pi itself saved, so a resumed session keeps its place.
Without a hook, a search still carries a per-shell identity, hashed with a key that never leaves state/, so even a bare terminal's searches group together.
What the platform shows
Each search appears on platform.jevlin.ai twice, at two zoom levels.
Traces shows one search. The query, the tier, the provider the router chose, and a receipt per provider: whether it answered, timed out or is still pending, how long it took, how many results it returned, and what it cost. Totals give the results, the unique pages, the wall time and the cost of the search.
Trajectories shows one session. One row per session lists what the agent asked and what came back, with the turn count, the search count, latency, cost and when it was last active. Open one and you read the session as a path: each turn, the assistant text that led to each search as its input, the pages that came back as its output, and the searches, cost, latency and turn id as attributes. A search that found nothing, or a provider that degraded, is marked on the row.
That is the whole point of carrying the trace: a list of query strings tells you what was asked, and a trajectory tells you why, and whether the answer moved the task along.
The turn end
The trace carries what the assistant wrote before a search. With [miner] turn_end = true, the agent's end-of-turn hook also sends what the turn concluded: the final message, what you asked, the tools called by name, duration and outcome, the model and the token counts. Only for turns in which a search was served, and never a tool's input or output. See The turn end.
Turning it off
export JEVLIN_TRACE=off
Sends no trace and no turn end. Searches work the same without them, and are metered the same. See What leaves the machine.