Quick answer
OpenCode keeps every session and its messages in the local database. Use the session switcher in the TUI for recent work, or query the `session` and `message` tables for anything older. `opencode debug paths db` prints the file to open with `sqlite3`.
Recent work is one keystroke away
Inside a project, OpenCode remembers the conversations you started there. The session switcher lists them with a title, a timestamp, and enough context to recognise the thread. Selecting one restores the transcript, including tool calls and their output, so you can continue where you stopped.
Two limits show up quickly. The switcher is built for choosing a conversation, not for auditing a month of them, and it only knows about sessions the current server can see. If you run a standalone server or move between machines, the list you see depends on which data directory that server opened.
Titles are generated by a small model when a session starts. That is convenient and occasionally misleading: a title written after the first message may describe the wrong problem if the conversation changed direction. Search the message text, not the title, when you are hunting for a specific fix.
The database holds the rest
Sessions are stored as rows, with messages pointing back at the session they belong to. Column names have drifted between releases, so inspect the schema before you write a query that you plan to keep.
DB=$(opencode debug paths db)
sqlite3 -readonly "$DB" ".tables"
sqlite3 -readonly "$DB" "SELECT id, title, datetime(time_created/1000,'unixepoch','localtime') FROM session ORDER BY time_created DESC LIMIT 20;"Timestamps are usually stored as milliseconds since the epoch, which is why the conversion divides by 1000. If a date looks like the year 1970 or 57000, you divided when you should not have, or the column already holds a formatted string.
- Filter by project directory or working directory to separate repositories.
- Join messages to sessions on the session identifier, not on the title.
- Order by the creation timestamp rather than by row id, which is not guaranteed to match.
- Select message role so you can exclude user turns from token totals.
What a session record will not tell you
A session is a container, not an invoice. It records what was said and which model answered, and usually how many tokens each answer used. It does not know your provider's contract, whether a discount applied, or what a subscription already covered. Cost fields are estimates written by the client.
Context compaction is the other gap. When a long session is summarised to fit the window, the transcript keeps the summary and the token record keeps growing, so re-reading the early messages will not explain the later counts. That is normal, not corruption.
For day-to-day use, the TUI is enough. For anything involving money or a postmortem, query the database or open UsageAtlas, which lists sessions with tokens and cost across 7, 30, or 90 days and needs no account. The token usage guide explains the fields you will see on each message.
Questions, answered
How long does OpenCode keep session history?
Until you delete it. There is no automatic expiry in the local database, though a reinstall or a manual cleanup can remove sessions. Back up the database if the history matters.
Can I search old OpenCode conversations?
Yes, by querying the message text in SQLite with a LIKE or full-text search, depending on your build. The TUI switcher is not a search tool, so use the database for older threads.
Where did my session go after switching machines?
Sessions are local to the data directory on each machine. Copying the database across, or enabling a sync feature if your build offers one, is what moves them.