Session Data
Session Data is what was actually in a conversation, in one shape regardless of which agent
produced it. A landed file has the extension .sd. Its first line is a header carrying everything
constant for the file. Every line after it is one source record, converted, with its content broken
into parts named for what they are: a message, a thought, a call, its result.
The conversion happens once, while the source is read, and never again. A runtime’s vocabulary
stops at its adapter. A reader is handed parts, so there is no runtime shape for it to reach into.
A dialect that meets something it cannot describe keeps the bytes verbatim in an unknown part
rather than guessing or dropping them.
The format is public. Package pkg/sessiondata defines it, and a third-party adapter produces it.
The schema is sd/1.
Header
{"h":1,"schema":"sd/1","seq":2,"at":"2026-09-01T12:56:03.1818Z","kind":"transcript",
"adapter":"claude-code-local/0.1.0","dialect":"claude-code/1",
"src":"-Users-me-github-skywalking/0438c73b-….jsonl","session":"0438c73b-…","stream":"main"}
| Field | Meaning |
|---|---|
h |
envelope version, 1 |
schema |
sd/1 |
seq |
the landed sequence number, monotonic per session |
at |
when the file was collected |
kind |
what it was collected from: transcript, agent_meta, journal, workflow_manifest, workflow_script, provider_body. otlp_log and otlp_span are reserved for a push transport. |
adapter |
how the records were acquired, with its contract version |
dialect |
whose schema they were read as. A push receiver and a local reader for one runtime share a dialect and nothing else. |
src |
the source, relative to the adapter’s root |
session, stream, batch |
the session, the execution stream (main or an agent id), and the group of children a workflow run started |
Record
{"ord":12,"off":25086,"sha":"4fed1b624096","bytes":1524,
"id":"c8b85d73-…","parent":"a5f0a42d-…","call":"msg_011Cdm…",
"from":"agent","time":"2026-08-06T16:57:22.652Z","flags":["finished"],
"usage":{"in":2,"out":331,"cache_read":20729,"cache_write":19709},
"parts":[{"k":"call","id":"toolu_015jKz…","name":"Bash","data":{"command":"make check"},
"state":"available","bytes":181}]}
Two halves. The record level carries provenance and the identifiers structure is built from, in role names. The part level carries what the content is.
| Field | Meaning |
|---|---|
ord, off, sha, bytes |
where the record was in the source: line number, byte offset, digest of the source bytes and their size. The bytes themselves are not kept; the digest stays so provenance is provable, and a record that claims a source it did not come from is detectable. |
id, parent |
the record’s own identity and its containment parent |
call |
the provider call this record is a fragment of |
run |
the agent loop it belongs to, one per trigger |
continues |
on a reset boundary only, the record the new context resumes from |
tool, child, batch, started_by |
joins the runtime states outside the content: the call a sidecar answers, the child stream a record names, the group it belongs to, the stream that started this one |
label |
a name the runtime gave something. The only naming evidence in the data. |
from |
who produced it: agent, external, runtime. A record’s type is not what a record is; most records that look like a person are a tool answering. |
time, trigger, flags |
when, what started the loop, and states such as finished |
usage |
what the provider reported: input, output, cache read and cache write tokens. Meaningful only where the call finished. |
parts |
the content |
dropped |
what the conversion chose to leave out, with its size and the reason |
Empty identifiers are common and mean the runtime supplied none. Nothing is inferred to fill them.
Parts
| Kind | Is | Carries |
|---|---|---|
text |
readable text | text |
reasoning |
the model’s own reasoning | text when the runtime kept it |
call |
a request to run something | id, name, data |
result |
what a call returned | of, text or data, failed |
media |
an image or a document | media, data |
data |
structure that is not prose: a record the runtime keeps for itself, a manifest | data |
unknown |
content the dialect could not describe | the raw bytes in data |
Every part carries state and bytes. state is one of available, truncated, redacted,
omitted or unavailable, and bytes is the size of the original even when the part holds less.
A reader is always told how much of the original it has. One tool result in the measured corpus
was 1.1 MB, and the share of a session that is tool output ran from 17% to 84%, so a reader is
routinely shown part of something and has to know it.
failed is absent, not false, when the runtime said nothing. Most results carry no error flag,
and reading that as success would report something the runtime never claimed.
The set of kinds is small because the thing described is small. Measured across a corpus of 3,032 Claude Code files, six content shapes exist and four cover 99.99% of records.
Dropped
"dropped":[{"what":"reasoning signature","bytes":588,"why":"a provider verifies it; a reader cannot read it"}]
dropped exists so a loss is stated rather than silent. It lists only what the dialect understood
and chose not to carry. Everything it did not understand travels as an unknown part, so a later
version of the dialect can interpret it without collecting again.