Loading the documentation…
DataMind Oda keeps each chat's lasting record as a session log file on your computer. This page covers the format of that log: its header, the events inside it, and the rules Oda follows when reading and writing it. It is written for people who inspect, support or process chat logs. Where the files live and how Oda keeps them is covered in Session logs.
A log holds everything the model saw in the chat, in the order it happened:
The first line is the header. It names the chat and how it started, and Oda can read it on its own to decide how to treat the file before reading anything else. The header carries:
The number of inherited events is kept with the log, not among the records a reader replays.
Before trusting a header, Oda checks its fields:
id must be a string;session metadata createdAt must be a non-negative safe integer;cwd must be absolute.A log written by a different version of Oda is converted or refused when it opens; see Opening older chats.
Every message carries a role:
| Role | What it carries |
|---|---|
user | messages from you and other human input |
model | Oda's answers, including the reasoning that came with them |
tool | a tool result, as a message of its own |
developer | notes about which tools the model had |
Message content comes in a few kinds: text, reasoning, image, file, tool-call and tool-result. Images are held as attachments in PNG, JPEG, WebP or GIF; each image record carries its media type, its size and its dimensions.
Two details matter when you process a log:
Oda stores each value in a log exactly as it was, and refuses a value it cannot store that way instead of storing an approximation. This covers:
undefined members, and arrays with empty slots;The same test runs on the chat's metadata, refused with session metadata must be losslessly JSON-serializable. When a value is refused, nothing from that batch is stored. For the messages a failed check can show, see Session log problems.
These version numbers name the log format, not a release of DataMind Oda. They count one series: a lower number in an older record or release note is an earlier step of this same series.
A change can stay in a released version only in three cases, each with exact conditions:
Every other kind of change starts a new version, for example: adding or removing a kind of record a log can carry; removing a property or changing its type; adding a required property or changing an existing property's optionality; changing a union's variants, an index signature, or a tuple's length; and changing the source compatibility policy.
Every type the format can reach is fingerprinted with a SHA-256 digest that covers every referenced field, and a change is reviewed by comparing the fingerprints before and after.
A change that looks cosmetic, such as reordering tuple elements, can still count; renaming a type alias does not.
A log is read in pieces, not all at once. Saved events keep their order and their sequence numbers, and reading a log never changes it. A read asks for two things:
Both are whole numbers of zero or more; anything else is refused with read offset must be a non-negative safe integer, got <v> or read length must be a non-negative safe integer, got <v>, depending on which value is wrong. By default a read starts at the beginning and returns the rest of the log. A read that starts at or past the end returns nothing; it is not an error.
A later read never shows an older state than an earlier one.
Oda also keeps small current values built from a chat's log, so it can present the chat's state without replaying every event. These values are called read models.
-1.Everything the model saw in the chat: the messages, the tool calls and their results, the files and images, and the settings each request ran with, all in order. The header at the top names the chat and how it started.
Each message carries one of four roles: user, model, tool or developer. They say where the message came from; the table above lists what each role carries.
PNG, JPEG, WebP and GIF. An image is stored with its media type, its size and its dimensions, and a reference to the attachment that holds it.
So a reader sees every result exactly where it belongs in the conversation. The result message keeps the id of the call it answers and is marked when the call failed. Logs from older formats are converted as they open, and their results also become messages of their own.
Values that JSON cannot keep exactly: negative zero, infinity and NaN, undefined members, empty array slots, hidden or symbol-named members, cycles, and instances of custom classes. Oda refuses them rather than storing a changed value.
Ask for a starting position and a length. The position is the sequence number of the first event to return, and the length is how many events to return. By default a read returns the log from its start, and a position at or past the end returns nothing.
Every change except three: an optional property added within an event's body, a new event payload version that keeps every earlier variant, and a source kind added only as an attribution. Adding or removing a kind of record, removing a property or changing its type or optionality, or giving a retired field data again all start a new version.
The numbers count one series, the log format's own versions, which have moved one step at a time from v0 to v4. Not every step was carried by a published release, so an older record can name a lower number of this same series.
It is the sequence number of the last event that the snapshot's values account for, so you can tell how current they are. A chat with no events yet reports the position -1.
They can lag behind the chat, but they are never wrong: when a saved checkpoint no longer fits the log, Oda rebuilds the state from the log instead of serving it.