Skip to main content
Every conversation is an append-only transcript you can group into projects, search full-text, fork from any turn, or trim a message at a time.

Quick Start

1

Group into a project

Right-click a conversation and choose Move to project. An in-app text prompt appears — type a project name (or leave it blank to remove the chat from its current project) and press OK. Press Esc or click outside to cancel without changing anything. If the prompt never appears on an older build, see Troubleshooting.
2

Search everything

Press ⌘K and type — the app searches every conversation’s text and jumps to the first match.
3

Fork from a turn

Fork any turn to branch a new conversation without changing the original.
The Move to project prompt routes on what you type:

How It Works

Transcripts are append-only JSON on disk, written with a write-then-rename pattern so a crash mid-write can never truncate an existing chat. Transcripts live at ~/Library/Application Support/PraisonAI/chats/ as one JSON file per conversation.
A transcript is written only after a run completes — never per token — so a chat app never burns through an SSD’s write endurance.

Reopening a chat restores history

The transcript you see on screen and the history the model reads are two different things — reopening a chat now aligns them.
  • The transcript on disk is what the sidebar renders.
  • The agent’s in-memory chat_history is what the model actually sees.
  • Reopen a chat after a restart, a settings change, or toggling tools, and the transcript is replayed into the fresh agent so the model has the same context you can read on screen.
  • Blank or whitespace-only assistant messages (left behind by failed turns) are skipped, so the model is never taught that silence is a valid answer.
  • A mid-session agent already holds those turns and is not replayed over — nothing is duplicated.
Replay happens only when the cached agent has no history — reopening mid-session does not replay, so nothing is duplicated.

Actions

A corrupt transcript is surfaced in the sidebar as “(unreadable)” rather than hidden — a chat you can see is recoverable; one silently omitted looks like data loss. “Corrupt” now includes any file in chats/ that is not a JSON object — for example, the JSON array the app’s own Export produces. Such a file used to drop /chats, /projects, and /search; it now surfaces as an (unreadable) row like any other broken transcript. When a chat like this can’t be deleted for the same on-disk reasons, see Deleting a conversation below. See Data & Privacy for the on-disk shape and Troubleshooting for recovery.

Deleting a conversation

The app now tells the truth about a delete: the screen follows what the engine actually did on disk, not the click. Answering 200 OK for every case was how a chat closed on screen and reappeared on refresh — the sidebar delete and Clear-all now check the engine’s answer first.
  • The sidebar delete follows the server’s answer. If the engine refuses the delete — a read-only data dir, a permission-changed folder, or a synced folder mid-conflict — the row stays, the open transcript stays, and a toast reads “Could not delete that conversation.” On success, the transcript is cleared only if the deleted chat was the active one; the sidebar refreshes either way.
  • Clear-all tolerates partial failure. Every conversation that couldn’t be deleted is counted, and a toast reads “Could not delete N conversation(s).” The active transcript is blanked when its own delete succeeded, even if another one failed — the transcript on screen follows disk state, not the batch result.
The raw response codes behind these paths — 200, 400, 500 — are in Engine API.
⌘K search delegates to the library’s SqliteSessionStore (FTS5/bm25) over your on-disk chats instead of a substring scan.
  • Ranked by FTS5/bm25 relevance instead of directory order.
  • Each result carries a snippet centred on the match.
  • The index is rebuilt in-memory per query, so an edited chat is never served stale.
  • Falls back to the old substring scan if SqliteSessionStore is unavailable, so search never breaks.
Desktop chats persist their id under id, not session_id, so the engine backfills each chat’s id from its filename before indexing — every chat is searchable and addressable independently.
The same engine powers the terminal — see Session Search.

Best Practices

A fork copies the transcript up to a chosen turn into a new conversation and leaves the original untouched — branch an idea without losing the thread.
Move related conversations into a project so the sidebar stays legible as chats accumulate.
With auto_title on, the first message becomes the chat title. Leave it on and rename only the few that need it.

Data & Privacy

Where transcripts live and how to export them

Chat & Streaming

Per-turn actions like Fork and Delete