Skip to main content
Provide a session_id on your agent and conversation history is saved and restored automatically — no database setup required.
The user returns with the same session_id; prior turns reload automatically from disk.
Upgrade if you rely on multi-process saves, gateway resume, or idempotent save_state() — fixes landed in PRs #1709, #1897, #1972, and #2102.

Quick Start

1

Start with a session_id

2

Resume from the CLI

History, model, and agent name are restored — not just the transcript.

Agent Session Persistence

Deterministic Resume

As of PR #2277, praisonai session resume is a first-class restore — not a transcript display. What is restored:
  • Chat history — full conversation messages
  • Model — the LLM used in the session (read from metadata["model"], falls back to metadata["llm"])
  • Agent name — the name of the agent that ran the session
New CLI options:
  • praisonai session resume <id> — restores state and shows a “Session Resumed” panel
  • praisonai session resume <id> "<prompt>" — restores state and continues with a new prompt
  • praisonai session resume <id> --transcript — opt-in to the old transcript-only view (panel title: “Session Transcript”)
Session lookup checks the project store first, then falls back to the global default store — so sessions created via praisonai run --continue or via the gateway/TUI are all reachable. For the full CLI reference, see Session Command. When context compaction is enabled, resume gets cheaper still: the compaction summary is persisted to the session and replayed on the next run, so --continue / session resume reconstructs a compacted working history (summary + retained tail) instead of the full raw transcript.
With execution=ExecutionConfig(context_compaction=True) and a bound session_id, resume automatically uses the compacted working history. Sessions without a checkpoint resume from raw messages exactly as before. See Compacted Session Resume.

How It Works

When you provide a session_id to an Agent:
  1. Automatic Persistence: Conversation history is automatically saved to disk after each message
  2. Automatic Restoration: When a new Agent is created with the same session_id, history is restored
  3. Zero Configuration: No database setup required - uses JSON files by default

Default Storage Location

Sessions are stored in: ~/.praisonai/sessions/{session_id}.json

Behavior Matrix

Session expiry / cleanup. This page covers the low-level session_id + DefaultSessionStore path used by Agent(memory={"session_id": ...}). If you instead use the high-level Session(...) wrapper, it supports session_ttl, is_expired(), time_to_expiry(), and close() — see Sessions & Remote Agents → Session Expiry & Cleanup.

In-Memory Memory (Default)

Even without session_id, the same Agent instance remembers previous messages:
In-memory memory is lost when the Agent instance is garbage collected or the process ends. Use session_id for persistence across processes.

Persistent Sessions

Basic Usage

Resuming Sessions

Session File Format

Sessions are stored as JSON files with automatic metadata tracking:

Session Metadata Fields

The following metadata is automatically populated after each assistant turn: These fields enable cost tracking and usage analytics across sessions.

How metadata is populated

After every assistant turn, praisonaiagents/agent/memory_mixin.py::_persist_session_stats() calls store.update_session_metadata(session_id, model=..., total_tokens=..., cost=..., source=..., agent_id=...). You normally don’t call this directly — but you can call it to record custom metadata on a session:

Idempotent saves

Session._save_agent_chat_histories() uses set_chat_history(session_id, messages) to atomically replace the persisted history rather than appending. This means:
  • Repeated session.save_state() calls do not duplicate messages.
  • The per-turn _persist_message() path and the _auto_save_session() flush share _auto_save_last_index, so each message is written exactly once.
Custom session stores that do not implement set_chat_history fall back to add_message() with a logged warning — those stores may produce duplicates on repeated save_state() calls until they add set_chat_history.

Multi-Process Safety

The session store is safe under concurrent multi-process and multi-instance use on both reads and writes:
  • Atomic writes — every mutator (add_message, set_agent_info, set_gateway_info, clear_session, update_session_metadata) reloads the session from disk inside FileLock, mutates, then atomically writes (temp file + os.replace). Concurrent writers cannot drop each other’s messages.
  • Fresh readsget_chat_history, get_session, and get_sessions_by_agent reload from disk under FileLock on every call and refresh the in-process cache. Two store instances pointing at the same session_dir will always see each other’s writes.
  • Cross-platform locksfcntl.flock on Unix/macOS, msvcrt.locking on Windows.
The praisonai session CLI has its own session store (praisonai.cli.session.UnifiedSessionStore, separate from praisonaiagents.session.DefaultSessionStore documented above). Both stores use the same cross-platform locking strategy as of PR #1837. UnifiedSessionStore now reloads and merges under exclusive lock — shared message-prefix merge + delta-based stats merge — as of PR #1885. Updated in PR #1892: UnifiedSessionStore.save() reloads under lock and merges concurrent writes (previously it overwrote with the in-process cache, dropping messages from a second process that wrote between load and save). UnifiedSessionStore.load() always reads from disk. The Windows code path locks the entire file (max(file_size, 1) bytes via msvcrt.locking) instead of only the first byte, matching Unix fcntl.flock semantics. Concurrent writers from a TUI + --interactive session, or from two terminals sharing ~/.praisonai/sessions/, no longer drop each other’s messages. See CLI Sessions for the CLI-side details.
Multiple processes (a gateway worker and a bot worker, several uvicorn workers, a CLI alongside a server) can safely share the same session_dir. Each call to get_chat_history returns the latest committed state on disk — there is no stale-cache window.
store.invalidate_cache(session_id) still exists for backwards compatibility, but since reads always reload from disk it is effectively a no-op on the read path. You no longer need to call it before get_chat_history / get_session.

Direct Session Store Access

For advanced use cases, you can access the session store directly:

Custom Session Directory

Using with DB Adapter

When a DB adapter is provided, it takes precedence over JSON persistence. The DbSessionAdapter now persists both messages and metadata to the conversation store, ensuring session metadata survives process restarts.
For DB-backed sessions, clear_session() and delete_session() now purge persisted messages from the database via the conversation store’s delete_messages() method, ensuring that cleared history does not reappear after restarts.When using the built-in DbSessionAdapter (via praisonai.db), both messages and metadata are automatically persisted to your database. The set_metadata() and get_metadata() methods now round-trip through the conversation store, so metadata survives process restarts without additional configuration. For complete examples, see the HostedAgent persistence guide.

Context Caching

For cost optimization with Anthropic models, use caching=True:
This caches the system prompt, reducing token costs for repeated conversations.

Bot Session Persistence

Bots use the same session store as agents. Each user gets a persistent session that survives bot restarts. Configure via bot.yaml:
Run your bot:

max_history Resolution

When a bot starts, it resolves max_history using this precedence ladder:

Bot Session Configuration

Configure these settings in your bot.yaml under each channel:
When compaction is enabled, max_history becomes a hard upper bound (max_history × 4) instead of the primary trim mechanism. See Bot Session Compaction for the full flow.
max_history at the channel level takes precedence over session.max_history. Use session.max_history for new configs — it is the preferred form.

Session Reset Policies

Without a store parameter, BotSessionManager falls back to in-memory-only mode for backward compatibility.

Bounded lock caches

Long-running bots keep concurrency safe without unbounded memory growth: Recreating agents per request is safe: locks no longer share stale id(agent) keys across unrelated users.

Session Store Protocol

All session stores implement SessionStoreProtocol — a lightweight interface that enables swapping backends:

Retention Policies

Long conversations stay safe — older turns are summarised and archived, not silently dropped.
Before this change, the 101st turn silently deleted the 1st on every write. Retention policies (Issue #2709) fix that: overflow beyond the active window is rolled up into one summary message and preserved raw in a durable archived_messages field.

The Three Policies

Built-in defaults from store.py: DEFAULT_RETENTION = "compact" and DEFAULT_MAX_MESSAGES = 100.

What Non-Destructive Means

Overflow becomes one summary message plus a raw archive — the active window stays small while the full record survives.

Choosing a Policy

Pick the policy that matches what you care about most.

Configuration

Configure retention three ways — from zero-code env vars up to the constructor. Level 1 — env vars (zero code):
Level 2 — constructor:
Level 3 — legacy behaviour (backward-compat trap):
Passing max_messages=N with N != 100 and no explicit retention keeps the legacy truncate behaviour — older turns are permanently dropped. To opt into non-destructive rollup, pass retention="compact" explicitly.

Environment Variables

Both env vars are read only when the global default store is first constructed (lazy singleton). Setting them mid-process has no effect.

Constructor Parameters

The archived_messages Field

Raw archived turns are preserved on disk in SessionData.archived_messages and loaded back into SessionData / ExtendedSessionData on subsequent reads — old session JSON without the field loads cleanly as an empty list. Once the archive crosses ARCHIVE_WARN_THRESHOLD (10_000 entries) the store logs a one-off warning (archived_messages has grown to N entries) so operators can spot runaway sessions.

Reads Return Full History

get_chat_history() no longer silently re-caps reads at the legacy 100-message tail — full stored history returns unless you pass max_messages explicitly.

Retention in Practice

A compact session summarises overflow on write and hands back summary + recent turns on resume.
Upgrading is safe. Old session JSON files load cleanly — a missing archived_messages defaults to []. Callers that pass max_messages=N keep the legacy truncate behaviour by default. To opt into non-destructive rollup, either omit max_messages (the store uses DEFAULT_MAX_MESSAGES=100 + retention="compact") or pass retention="compact" explicitly.

Retention Best Practices

Non-destructive rollup is the right default for --continue, resume, and Agent(session_id=...) flows — the earliest turns survive as a summary plus a raw archive.
When you want a hard cap on disk usage and don’t care about old turns, retention="truncate" hard-slices to the tail.
keep_all never trims — archived_messages and the active window grow unbounded otherwise.
A larger active_window keeps more live context per turn but raises token cost. It defaults to max_messages when unset.
The store logs once when the archive crosses ARCHIVE_WARN_THRESHOLD (10_000) so operators can spot runaway sessions.

Best Practices

Include user or context in the id: f"user-{user_id}-{conversation_type}".
Default max_messages is 100. Overflow is summarised and archived by default (retention="compact") — see Retention Policies.
Call store.delete_session() to remove stale sessions and purge DB rows when using a DB adapter.
Set caching=True on the agent to reduce token cost on repeated conversations.

API Reference

Agent Parameters

DefaultSessionStore Methods

When you use SqliteSessionStore instead of the default JSON store, get_by_gateway_session() and list_sessions_by_gateway_agent() become indexed lookups against the session_route table (Issue #2956) — routing latency stays flat as sessions accumulate. See Session Store → SqliteSessionStore.

Session Protocol

Build custom session backends

Bot Session Compaction

Summarise older bot turns instead of dropping them