Search Sessions

Ranked full-text search across past session transcripts from the terminal

Search every stored session transcript from the terminal โ€” ranked by relevance, with a snippet centred on each match.

Diagram

Ranked Session Search

๐Ÿ’ฌ Query

๐Ÿ—ƒ๏ธ SqliteSessionStore

๐ŸŽฏ FTS5 + bm25

๐Ÿงน Cross-store dedup

๐Ÿ“Š Ranked results + snippets

Quick Start

An agent recalls past sessions through the session_search tool โ€” the CLI reaches the same engine from your terminal.

python
from praisonaiagents import Agent

agent = Agent(
    name="recall-agent",
    instructions="Search past sessions when the user asks about earlier decisions.",
    tools=["session_search"],
)
agent.start("What did we decide about the billing migration?")
  1. Search your sessions
    bash
    praisonai session search "billing migration"

    Prints a table of matching sessions ranked by score, each with a snippet centred on the match:

                  Search: billing migration
    โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
    โ”ƒ ID                   โ”ƒ Title           โ”ƒ Score โ”ƒ Snippet                                        โ”ƒ When              โ”ƒ
    โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
    โ”‚ zeta                 โ”‚ Zeta            โ”‚ 5.0   โ”‚ we need a billing migration migration migrโ€ฆ   โ”‚ 2026-06-10T14:32โ€ฆ โ”‚
    โ”‚ alpha                โ”‚ Alpha           โ”‚ 2.0   โ”‚ one mention of migration here                  โ”‚ 2026-06-09T11:04โ€ฆ โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

    An empty result prints:

    No sessions matched: billing migration
  2. Widen the search

    Return more sessions and more context around each hit:

    bash
    praisonai session search "billing migration" --limit 10 --window 8
  3. Get machine-readable output

    Add the global --json flag for a structured result:

    bash
    praisonai --json session search "billing migration"
    json
    {
      "query": "billing migration",
      "results": [
        {
          "session_id": "zeta",
          "title": "Zeta",
          "when": "2026-06-10T14:32:00Z",
          "snippet": "we need a billing migration migration migrโ€ฆ",
          "score": 5.0,
          "anchor_index": 12,
          "messages": [
            {"index": 11, "role": "user", "content": "...", "timestamp": "...", "archived": true},
            {"index": 12, "role": "assistant", "content": "...", "timestamp": "...", "archived": false}
          ]
        }
      ]
    }

    Each result is SessionHit.as_dict() โ€” the same shape as the Cross-Session Recall Discovery return.

    Each message carries an archived boolean โ€” true marks a turn recalled from archived_messages (compacted out of the active window by the default retention="compact" policy). See Recall Across Compacted History.


How It Works

session search delegates to SqliteSessionStore โ€” the same FTS5/bm25 engine as the session_search agent tool โ€” instead of a substring scan.

Diagram
SqliteSessionStorecanonical_cli_stores()praisonai CLIUserSqliteSessionStorecanonical_cli_stores()praisonai CLIUserloop[Each store]session search “billing migration”enumerate project + global storesSqliteSessionStore(session_dir=โ€ฆ)hits with FTS5/bm25 score + snippetcollapse duplicates by session_id (keep higher score)sort by (score, when) desc, truncate to –limittable (or JSON) of ranked hits

The command iterates the CLI’s canonical stores via canonical_cli_stores() โ€” the same helper session resume / show / delete / export use โ€” so id resolution stays consistent across every sub-command. See Session Resume.

StepWhat happens
Enumerate storesWalks the project-scoped store then the global default store via canonical_cli_stores()
Index per storeOpens a SqliteSessionStore(session_dir=โ€ฆ) and runs search(query, limit, window)
RankFTS5/bm25 scoring with snippets and lineage dedup, applied inside each store
Cross-store dedupCollapses duplicate session_ids across stores, keeping the higher-scoring copy
Sort & truncateSorts by (score desc, when desc) and keeps the top --limit hits

Cross-store dedup

A session resumed from the global default store keeps a project-side shadow, so the same id can match in both canonical stores. Without dedup those duplicate rows each consume a slot of --limit and crowd out distinct sessions. The command collapses by session_id, keeping the higher-scoring copy โ€” mirroring the lineage dedup the store already applies inside a single directory.

Compacted history stays searchable

The CLI walks the same archived + active projection as the session_search agent tool. A detail rolled into archived_messages under retention="compact" is still recallable via praisonai session search โ€” hits on archived turns come back with archived: true in each message entry (JSON mode) so the compaction boundary stays visible.

bash
# The detail was rolled into archived_messages weeks ago โ€” still findable from the terminal:
praisonai session search "deploy token XZ99" --limit 5

See Cross-Session Recall โ†’ Recall Across Compacted History.


Flags

bash
praisonai session search <query> [--limit/-n N] [--window/-w N]
Argument / FlagTypeDefaultDescription
querystr (positional, required)โ€”Free-text query to search across session transcripts
--limit, -nint5Maximum number of matching sessions to return
--window, -wint5Number of messages to include around each hit
--json (global)flagoffPrint {"query": ..., "results": [...]} instead of a table

Text mode prints a table with columns ID | Title | Score | Snippet | When. JSON mode prints {"query": "...", "results": [...]} where each result is SessionHit.as_dict().


Common Patterns

From an agent tool to the terminal

The session_search agent tool and praisonai session search share the same store โ€” pick whichever fits your workflow.

bash
# Same engine (SqliteSessionStore / FTS5+bm25) โ€” from the terminal:
praisonai session search "billing migration" --limit 5 --window 5

Pipe JSON into a script

bash
praisonai --json session search "auth refactor" \
  | jq -r '.results[] | "\(.score)\t\(.session_id)\t\(.snippet)"'

Resume the top hit

bash
SID=$(praisonai --json session search "billing migration" | jq -r '.results[0].session_id')
praisonai session resume "$SID"

Best Practices

Keep –window small for a quick scan

Start with the default --window 5. Widen only when you need more surrounding context after finding the right session โ€” then session resume it.

Treat scores as relative rankings

Scores are FTS5/bm25 ranks, not probabilities. A 5.0 beats a 2.0, but says nothing absolute โ€” read the snippets to confirm the match. Compaction count no longer skews a session’s rank โ€” the synthetic retention="compact" summary is kept for context in each hit but excluded from scoring (PraisonAI PR #6112).

Raise –limit when duplicates are expected

Cross-store dedup collapses same-id shadows, but distinct sessions on the same topic still compete for slots. Raise --limit when a topic spans many sessions.

Use –json to automate

The --json output is SessionHit.as_dict() per result โ€” pipe it into jq to feed ids straight into session resume, show, or export.


Related