Quick Start
How It Works
Sessions live at~/.praisonai/sessions/*.json — one JSON file per session. The default DefaultSessionStore search is a dependency-free substring/keyword scan with no setup. The index hop above appears only when a SqliteSessionStore is active; the default flow reads the JSON files directly.
Picking the right shape:
Which store should I use?
Configuration Options
session_search Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
query | str | "" | Free-text query for discovery mode |
session_id | str | "" | Session to read in scroll mode |
around_message_id | str | "" | Anchor message index (as string) for scroll mode |
limit | int | 5 | Maximum sessions/results to return |
window | int | 5 | Messages to include around a hit/anchor |
Return Shapes
- Discovery
- Scroll
- Browse
Returned when The
query is set:bookends key is present only when the session has more than BOOKEND_SIZE (2) user+assistant messages — mirroring SessionHit.as_dict().Scoring
Discovery mode scores hits using keyword matching:| Match type | Score added |
|---|---|
| Exact substring match | +2 per message |
| Per-term keyword match | +1 per term per message |
updated_at descending). Scores are heuristic — treat them as relative rankings, not probabilities.
Snippets are centred on the first match and trimmed to ~120 characters with … ellipses.
Anchored, Demoted, Deduped Results
Every discovery hit — from either store — carries anchored context, demotes noisy automated runs, and collapses continuations of the same conversation.- Bookends:
{"opening": [...], "closing": [...]}— the first and last 2 user+assistant messages of the session, so the agent sees goal → match → resolution in one call. - Automated demotion: sessions tagged as automated/scheduled or whose message rate exceeds ~60 msg/hour have their score multiplied by
0.25. - Lineage dedup: reset/compacted continuations sharing
lineage_id/root_session_id/thread_idcollapse to the single best-scoring hit.
Automated-session heuristics
| Signal | Trigger |
|---|---|
data["source"] or data["metadata"]["source"] | one of scheduled, automated, cron, system, batch |
data["metadata"]["automated"] | True |
| Message rate | > 60 messages / hour over the session span (needs ≥ 4 timestamped messages) |
0.25 (constant AUTOMATED_DEMOTION).
Bookends
Bookends return the first / last 2 user+assistant messages (BOOKEND_SIZE = 2). They are omitted when the session has ≤ 2 conversational messages, to avoid duplicating messages already in the context window.
Lineage keys
| Key | Read from | Note |
|---|---|---|
lineage_id | data or metadata | Explicit chain identifier |
root_session_id | data or metadata | Root of a continuation chain |
thread_id | data or metadata | Thread identifier |
parent_session_id is intentionally not a lineage key — it points at an immediate parent, so sibling sessions forked from one parent stay distinct instead of suppressing each other.
Common Patterns
Gateway assistant recalling a past decision
”What was I working on?” at the start of a new session
Programmatic access via the store directly
Best Practices
Keep window small for fast scans
Keep window small for fast scans
Use
window=3 to window=5 for discovery. Widen only when you need more surrounding context after finding a hit — use scroll mode for that.Treat scores as relative rankings
Treat scores as relative rankings
Scores are heuristic keyword counts, not probability-calibrated relevance scores. A score of 4.0 beats 2.0 but says nothing absolute. Always let the agent reason about the returned snippets.
Scope sessions per user in multi-user deployments
Scope sessions per user in multi-user deployments
The default search is per-store, not per-user. In multi-user deployments, prefix
session_id values with a user identifier (e.g. user_42_session_xyz) and search within those sessions explicitly.Scale-out: swap in SqliteSessionStore
Scale-out: swap in SqliteSessionStore
For long-lived gateway bots with thousands of sessions, swap in
SqliteSessionStore — a stdlib sqlite3 + FTS5 index turns recall into a bounded MATCH ... ORDER BY bm25 lookup instead of a full-directory scan. It transparently falls back to the substring scan if sqlite3 or FTS5 is unavailable, so nothing breaks. See Session Store.Advanced: SearchableSessionStoreProtocol
The default store implements SearchableSessionStoreProtocol, a separate runtime_checkable protocol that adds search to any session store backend.
Protocol Methods
| Method | Signature | Description |
|---|---|---|
search | search(query, *, limit=5, window=5) -> List[SessionHit] | Keyword scan across all stored sessions |
window | window(session_id, around_message_id=None, *, window=5) -> List[Dict] | ±N messages around an anchor; uses most recent if anchor omitted |
recent | recent(*, limit=10) -> List[SessionSummary] | Most recently updated sessions |
SessionHit Fields
| Field | Type | Default | Description |
|---|---|---|---|
session_id | str | required | The session that matched |
title | str | "" | Agent name or first user message snippet |
when | Optional[str] | None | updated_at or created_at timestamp |
snippet | str | "" | Short snippet centred on the first match |
score | float | 0.0 | Match score (higher = better) |
anchor_index | int | -1 | Index of the best-matching message |
messages | List[Dict] | [] | Context window around the hit |
bookends | Dict[str, List[Dict]] | {} | {"opening": [...], "closing": [...]} — first/last user+assistant turns; empty when the session has ≤ BOOKEND_SIZE conversational messages |
SessionSummary Fields
| Field | Type | Default | Description |
|---|---|---|---|
session_id | str | required | Session identifier |
title | str | "" | Agent name or session id |
when | Optional[str] | None | Last update timestamp |
message_count | int | 0 | Number of messages in the session |
SessionStoreProtocol (the core persistence contract) is unchanged and backward compatible. SearchableSessionStoreProtocol is an additive, separately runtime-checkable protocol.Related
Bot Default Tools
Where session_search fits in the opt-in tool list for bots
Session Store
How sessions are persisted in ~/.praisonai/sessions/
Memory
Distilled long-term memory (different from raw session transcripts)
Knowledge
RAG over documents (also different from session recall)

