How Oda stores chats

DataMind Oda keeps every chat as a lasting record on your computer: a log file that grows as the chat works. This page describes that storage for the people who build, ship or debug Oda itself: the files, the events inside them, and how older logs are brought forward. The user-facing view of the same files is on Chat logs on disk, and the rules that keep older logs readable are on Chat log format and compatibility.

Where a chat's history lives

The event envelope

Every event in a log carries the same small envelope:

PartWhat it carries
typeWhat the event records
seqThe event's position in the log
timeWhen the event happened
dataThe event's payload
ignorable (optional)Marks the event as safe to skip when a reader does not know its type
surfaceOp, sourceEventSeqs (conditional)For a surface event: the history change it applies and the recorded events it is built from
Note

The messages a user meets when a log cannot be read are collected on Opening older chats.

What a chat records as it works

A chat's record grows as the work happens, and nothing already written is rewritten.

Where a stored message came from

Reading the record from outside

Bringing older logs forward

An older log is converted while the chat is used: opening it only reads it, and the current-format copy is written when the chat next records something new. That story is on Opening older chats. For a whole root at once, maintainers use a one-time migration command, pnpm run migrate:sessions-to-v4.

Questions, answered

What does an event in a log look like?

Every event carries type, seq, time and data, plus an optional ignorable marker and, for a surface event, the conditional surfaceOp and sourceEventSeqs fields. The table above says what each part carries.

Why do some recorded events never reach the model's history?

By design: an event is either a surface event, which produces the history a model reads when the chat continues, or a log-only event, which is kept for the record but never enters that history.

What can a stored message's source be?

One of a reserved set: you, the model, a tool, the system prompt, a checkpoint saved when a chat is shortened, a generated chat title, the tool registry, runtime context, a plugin, an automatic review, or a compaction output. A new kind cannot reuse a reserved name, and an unknown kind is preserved rather than dropped.

Why does a log record a model call that returned nothing?

So the record stays complete: every successful call is recorded, even one with no content or one that stopped at the output limit. An empty answer is kept out of the history derived for the model, so it never misleads a later turn.

What does the log record while a turn runs?

The whole path of the turn: the prompt admission, the running state, the turn start, the assembled system prompt, each step start, the request with its system and user messages, the answer and its tool calls and results, then the step end, the turn end and the idle state.

What do other programs read to replay a chat?

The session event stream, which carries the record for replay. Coordination while work runs, such as queue and status, steering and errors, travels on a separate live surface.

How are the chat-reading tools authorised?

They are a read-only set, available only where a deployment enables it; each answer is authorised from the chat that asked for it, and the set never exposes its internal paging cursors.

Why does the one-time upgrade refuse to run?

Because the build it ships in does not write the current log format. Run it from a build whose writer is current (this one-time command requires a V4 Session writer); nothing is converted otherwise.

What happens if a log changes while the upgrade runs?

That log is deferred and retried once after the first pass, so the run never converts it mid-change.

Does the upgrade call a model?

No. The run uses no model and no API key, and its text log and final summary are written privately in a temporary folder.