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 |
File kinds
One .sd file holds one cut of one source. The header’s kind says which, and the records of
each kind carry a characteristic set of fields.
kind |
Collected from | Its records | What refers to it |
|---|---|---|---|
transcript |
one execution stream: the main stream or a child agent’s own | the conversation itself: inputs, calls in fragments, results, injections, resets. stream on the header is main or the agent id. |
almost every node of a round |
agent_meta |
what the runtime recorded about a child agent when it started it | one record, from: runtime, with child and label, and a data part |
the child’s stream node, for its label |
journal |
a workflow run’s journal | one record per event, from: runtime, with child and batch; a child_result flag on the record that returns a child’s value |
agent.output nodes and ends_with relations |
workflow_manifest |
a workflow run’s manifest | one record with batch and label, and a data part |
the run’s name |
workflow_script |
the program a workflow ran | one record whose part is unknown: the source is a program, not data |
nothing; kept because it is part of the session |
Files of every kind are bound by the rounds’ input_digest, so a session travels or archives as
all of them. A session’s agent_meta, workflow_manifest and workflow_script records carry
no time, so those files have no record time range.
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. |
model |
the provider model the call ran on, as the runtime named it, on the records of a call. What a token count is reported under. Empty on records landed before it was kept. |
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.