The short answer

The native-session path has two separate jobs. Discovery locates provider-owned transcript files. Parsing then turns a selected file into AgentSession: session metadata plus normalized SessionEvents. In v1.0.31, the parser dispatches Gemini to its JSON parser, Cursor to a JSONL parser that can include subagent transcripts, and Claude/Codex through the JSONL path. Provider-specific syntax stays inside the parser; the consumer receives one model.

This is intentionally not an OpenTelemetry data model and not an eBPF event model. The crate documentation calls agent-session a local IR and explicitly excludes OTLP export, UI rendering, database schema, and eBPF capture. Consuming applications can map the IR into SQLite, OpenTelemetry, reports, or other outputs; the parser itself does not pretend a provider transcript already contains host-level evidence it never recorded.

What the common IR actually preserves

IR objectExamples of preserved fieldsWhy it matters
AgentSessionagent type, session/display/conversation IDs, source path, start/end time, model, aggregate usage, cwd, duration, last-message timeKeeps provider identity and session provenance around the normalized events.
UserPromptprompt index, timestamp, text hash, full authorized text, preview, semantic tag, task pathLets consumers refer to one user turn without reducing it to a display string.
ToolEventtool name/category, command/effect, process chain, status, paths, domains, call ID, active skill and task pathSeparates a tool invocation from the files or destinations the native record attributes to it.
LlmResponseprompt index, model, source completion ID, text/hash/preview, token components, response phase, skill and task pathPreserves response identity and source lifecycle information when the provider records it.
PlanStepstep text and statusCarries the latest source-recorded coding plan without inferring an unseen planner state.

Normalization is not the same as erasing provider identity

AgentSession.agent_type remains part of every parsed session, and the object retains the native source path plus provider-derived session and conversation identifiers. That is important because two fields with the same normalized name can have different source semantics. A real Codex thread ID, for example, is stronger conversation evidence than a value guessed from a filename or response identifier.

The crate’s OTel-alignment note makes this rule explicit: names such as agent_type,conversation_id, and aggregate usage are chosen when they fit, butconversation_id is left unset when the native log has no real session/thread identifier. A common schema is useful only if “missing” still means “the source did not establish this,” rather than “the parser invented a plausible value.”

Prompt indexes are the local join key for interaction events

Prompts, tool events, and model responses all carry a prompt index. That gives downstream analysis a bounded way to group activity around the user turn that was active when the source recorded it. The prompt object also has a stable prompt_key() built from the index and text hash, so a consumer can identify a prompt without using raw prompt text as the key.

This is different from claiming perfect causal tracing. A native transcript records the relationships its agent exposes. The IR can preserve those relationships and make them comparable, but it cannot reconstruct an unrecorded scheduler decision or prove that every child process and filesystem effect belongs to a tool call. Independent system capture remains a separate evidence source for those questions.

Tool paths carry access semantics instead of becoming a flat filename list

The common ToolPath object stores the path together with an access operation. v1.0.31 documents normalized values including read, write, create, delete,rename_from, and rename. A rename can also carry its source path on the destination record. This avoids a common normalization mistake where “the session mentioned file X” is treated as if it proved a read or a write.

ToolEvent separately preserves path groups, domains, command information, status, and a call ID when available. Consumers can therefore ask narrow questions—what a tool reported touching, whether it succeeded, or which domain it named—without collapsing those fields into one generic activity string. These are still native-agent observations; they are not substitutes for host-level file or socket events.

Response identity exists so split source records can be merged without guessing

LlmResponse.source_id is explicitly described as the source-native completion identity used to merge split JSONL records. The normalized response can also carry response_phase values such as commentary, final answer, or assistant message when the source records a lifecycle. Those details matter for agents that emit one logical response across several native records.

Token fields stay decomposed as input, output, cache, and total counts. The helper used by profiling consumers drops zero or out-of-range components, uses the surviving validated components when any remain, and falls back to a bounded total estimate when none survives. If no usable component or total evidence exists, the profiling layer can mark the weight as unknown instead of turning missing usage into a fabricated zero-cost response.

One IR lets multiple consumers share parser semantics

The payoff is visible in agentpprof: its session layer aliases the sharedUserPrompt, ToolEvent, and LlmResponse types fromagent-session instead of defining another provider-normalization schema. The product can then build different projections—session listings, semantic flamegraphs, reports, or UI analysis—on top of the same native parsing contract.

That boundary also makes parser fixes more reusable. If a provider changes how it records a tool path or model response, the compatibility work belongs in the provider parser and common IR mapping. A consumer should not need four copies of “what does this Codex/Claude/Gemini/Cursor record mean for a file write?” just to render four different views.

Live process matching is adjacent to the IR, not embedded in transcript parsing

The library also exposes process-tree-to-session matching and PID-to-session lookup. Its documentation describes matching with real path evidence, sticky bindings, and a recent-working-directory fallback. Keeping this beside the transcript parser is useful because AgentSight can associate a live process with a native session without changing the session schema into a process-tracing schema.

The distinction is important for evidence interpretation. A parsed transcript answers “what did the native agent record?” Process matching answers “which live process tree most plausibly belongs to this session?” eBPF and other host sensors answer yet another question: “what did that process tree do at the operating-system boundary?” AgentSight can correlate those layers, but the common IR does not make them interchangeable.

What the session IR deliberately does not promise

A normalized transcript is not a packet capture, a complete syscall trace, a billing ledger, or a proof of policy compliance. Provider logs can omit token details, tool effects, conversation identifiers, or lifecycle events. A parser can faithfully preserve what exists and leave gaps explicit; it cannot recover information the source never wrote.

This is why AgentSight benefits from keeping native-session and independent system evidence as separate layers. Use the normalized IR for provider-visible intent, prompts, responses, tool records, plans, and usage. Add system capture when the investigation needs child processes, host file operations, network destinations, or other effects outside the transcript boundary.

Where to go next

For the step before normalization—where AgentSight looks for local session files and how it bounds discovery—see how AgentSight discovers local agent sessions. For the step after native usage enters reporting, see how overlapping token observations are reconciled. And for the independent host-observation boundary, read system-boundary observability.