> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Session

> Manage conversation sessions for multi-turn interactions

The `session` command manages conversation sessions, allowing you to save, resume, and organize multi-turn interactions.

Every sub-command (`list`, `resume`, `search`, `show`, `delete`, `export`, `share`, `unshare`) and every `--continue` / `--session <id>` flag resolves the same session by the same id through a single resolver.

### Where `--continue` / `--session <id>` / `--fork` work

The same resume primitives are surfaced on four CLI entry points; each restores model + history + tool-call context from the same project-scoped / global session stores:

| Surface                       | Command                                                                                                                                                     |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bare interactive launch (TUI) | `praisonai -c` · `praisonai --session <id>` · `praisonai --fork --session <id>` — added in PR [#4912](https://github.com/MervinPraison/PraisonAI/pull/4912) |
| Prompt / YAML run             | `praisonai run --continue "…"` · `praisonai run --session <id> "…"` · `praisonai run --fork --session <id>`                                                 |
| Chat TUI                      | `praisonai chat --continue` · `praisonai chat --session <id>`                                                                                               |
| In-TUI slash command          | `/continue` (most recent) · `/continue <id>` (specific)                                                                                                     |

All four resolve the same session by the same id through the shared resolver, so a `--session <id>` on any surface picks up exactly where any other surface left off.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start[🆔 You have a session id] --> Listed{📋 Listed by<br/>session list?}
    Listed -->|Yes| Unified[✅ Use any sub-command<br/>show / delete / export / resume<br/>share / unshare]
    Listed -->|No| Legacy{🕰️ Legacy id<br/>pre-2026-07-17?}
    Legacy -->|Yes| Fallback[♻️ Best-effort<br/>SessionManager fallback]
    Legacy -->|No| NotFound[🚫 not-found]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef check fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    classDef legacy fill:#F59E0B,stroke:#7C90A0,color:#fff

    class Start input
    class Listed,Legacy check
    class Unified,NotFound result
    class Fallback legacy
```

## Quick Start

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# List sessions for current project
praisonai session list

# List sessions across all projects
praisonai session list --all
```

<Frame>
  <img src="https://mintcdn.com/praisonai/pFBcVNzCyPC2mUmz/cli/session-list-conversation-sessions.gif?s=5b9bf1d43448c6a2b4736c9627221705" alt="List conversation sessions example" width="1497" height="1104" data-path="cli/session-list-conversation-sessions.gif" />
</Frame>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Start a new session
praisonai session start my-project
```

## Commands

### Start a Session

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session start my-project
```

**Expected Output:**

```
🆕 Starting new session: my-project

Session created successfully!
┌─────────────────────┬────────────────────────────┐
│ Property            │ Value                      │
├─────────────────────┼────────────────────────────┤
│ Session ID          │ my-project                 │
│ Created             │ 2024-12-16 15:30:00        │
│ Status              │ active                     │
│ Messages            │ 0                          │
└─────────────────────┴────────────────────────────┘

You can now run commands with this session context.
Use: praisonai "your prompt" --session my-project
```

### List Sessions

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session list
```

**Expected Output:**

```
Project: my-app (ID: a1b2c3d4, identity: git-remote)
ID         Name            Status   Events  Tokens   Cost     Parent    Updated
abc12345   research-bot    active   12      12,345   $0.0140  -         2026-06-29 23:48 UTC
def67890   summariser      paused   3       420      $0.0004  abc12345  2026-06-29 21:11 UTC
```

The header line now includes an `identity:` tag showing which resolver was used (see [Project Sessions](/docs/features/project-sessions) → How It Works).

Sessions that have not yet accumulated any usage show `-` in the Tokens and Cost columns.

<Note>
  **Updated columns (praisonaiagents 1.6.85+):** The table now shows `ID | Name | Status | Events | Tokens | Cost | Parent | Updated`. Tokens are formatted with thousands separators (e.g. `12,345`); Cost is formatted as `$0.0140`. Sessions with no recorded usage show `-` for both. The **Parent** column shows an 8-character parent id prefix for a [forked session](/docs/features/session-forking), or `-` for a root session.
</Note>

**JSON output** — pass `--json` to get machine-readable output with usage data per session:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session list --json
```

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
[
  {
    "id": "abc12345",
    "name": "research-bot",
    "status": "active",
    "parent_id": null,
    "events": 12,
    "usage": {
      "input_tokens": 10000,
      "output_tokens": 2345,
      "cached_tokens": 0,
      "total_tokens": 12345,
      "cost": 0.014,
      "requests": 3
    },
    "total_tokens": 12345,
    "cost": 0.014,
    "updated": "2026-06-29T23:48:00Z"
  }
]
```

<Note>
  `session list` (no `--project`, no `--all`) merges the current project's session store with the global default store — every session `--continue`/`resume` could see, in one list, deduped by session id (freshest `updated_at` wins when the same id lives in both stores). Use `--all` to include *every* project's sessions, or `--project <id>` to restrict to one project's store only. Cross-store merge landed with the fix for [PraisonAI #2655](https://github.com/MervinPraison/PraisonAI/issues/2655).
</Note>

#### What shows up

Without `--all` or `--project`, the default listing surfaces:

* Sessions created by `praisonai run` (project store) ✅
* Sessions created by `chat`, `code`, gateway, TUI, API, or a bare `Agent(memory={"session_id": "..."})` (global default store) ✅ **new**
* Sub-agent / [forked](/docs/features/session-forking) child sessions still appear here (with their parent shown in the **Parent** column), but `--continue` skips them in favour of the last **root** session.

Passing `--project <id>` stays project-scoped only; `--all` widens to every project.

#### Token / Cost columns

The `Tokens` and `Cost` columns show cumulative totals across all runs for each session. Totals are persisted in session-store metadata under `usage` and updated automatically by `praisonai run` / direct prompts whenever `--session` is set or a default project session is active. A `-` is rendered when no usage has been recorded yet.

**JSON output (`--json`):**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "sessions": [
    {
      "session_id": "session-abc123",
      "total_tokens": 5220,
      "cost": 0.014,
      "usage": {
        "input_tokens": 1240,
        "output_tokens": 3980,
        "cached_tokens": 0,
        "total_tokens": 5220,
        "cost": 0.014,
        "requests": 2
      }
    }
  ]
}
```

See [Cost Tracking](/docs/cli/cost-tracking) for how per-session totals accumulate across runs. For roll-ups across sessions see [Usage](/docs/cli/usage).

**List sessions across all projects:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session list --all
```

**List sessions for a specific project:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session list --project a1b2c3d4
```

| Flag                  | Description                                                                             |
| --------------------- | --------------------------------------------------------------------------------------- |
| *(default — no flag)* | Merges the current project's store with the global default store, deduped by session id |
| `--all`               | Show sessions from all projects                                                         |
| `--project <id>`      | Show sessions for a specific project ID — that project's store only                     |
| `--json`              | Output as JSON (includes `usage`, `total_tokens`, `cost`, and `parent_id` per session)  |

### Rename a Session

`session rename` gives a session a human-readable title so `session list` reads like a menu of tasks instead of an agent name or message snippet. The title is display-only metadata — the session id never changes. See [Session Rename](/docs/features/session-rename) for the full feature page.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session rename 4f9a8c72-... "fix-auth-bug"
```

**Expected Output:**

```
Renamed session 4f9a8c72-... to: fix-auth-bug
```

| Argument     | Type  | Required | Description                                                                                     |
| ------------ | ----- | -------- | ----------------------------------------------------------------------------------------------- |
| `session_id` | `str` | Yes      | The session's opaque id (as shown in `session list`).                                           |
| `title`      | `str` | Yes      | New human-readable title. Pass an empty/whitespace-only string to clear a previously set title. |

Clear a previously set title by passing an empty string:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session rename 4f9a8c72-... ""
```

**Practical example — before and after:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session list
# session-abc123     (no title)      2 hours ago
# session-def456     "Auth Refactor" yesterday

praisonai session rename session-abc123 "Deploy runbook draft"
# ✅ Renamed session-abc123 → "Deploy runbook draft"

praisonai session rename session-abc123 ""
# ✅ Cleared title for session-abc123
```

An id that resolves nowhere exits **1** with the same remediation text as the other session commands:

```
Session not found: 4f9a8c72-...
Use 'praisonai session list' to see available sessions
```

A failed metadata write prints `Failed to rename session: <id>` and also exits 1.

**JSON output** — pass the global `--json` flag for a machine-readable result instead of the human-readable line:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session rename 4f9a8c72-... "fix-auth-bug" --json
```

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{"renamed": true, "session_id": "4f9a8c72-...", "title": "fix-auth-bug"}
```

`session list` displays a title using this order (`_session_title()`):

1. The explicit stored title, if set
2. The agent name
3. A message snippet

<Note>
  Any id that `resolve_session` accepts is renameable — including legacy-only sessions listed in `session list` but not yet migrated to the canonical store. The canonical store stores the title in `metadata["title"]`; the legacy fallback (`SessionManager.rename`) persists it in `metadata["name"]`. Both paths persist so any id you can `list`, `resume`, `show`, `delete`, or `export` can also be renamed. Renaming is currently **CLI-only** — there is no `/rename` bot chat command. Titles set here surface in `session list` and every resolver-backed sub-command. See [Bot Commands](/docs/features/bot-commands) for the chat-side command set.
</Note>

### Fork a Session

`session fork` copies a saved session into a new child session, keeping both timelines resumable. It mirrors `praisonai run --fork` but works on any saved session without entering the REPL. See [Session Forking](/docs/features/session-forking) for the full feature page, including the interactive `/branch` command.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session fork <session_id> [--at-message N] [--title "..."]
```

**Expected Output:**

```
Forked session: sess_abc12345 -> sess_def67890
Resume the fork with: praisonai session resume sess_def67890
```

| Argument / Flag  | Type  | Required | Description                                                                                  |
| ---------------- | ----- | -------- | -------------------------------------------------------------------------------------------- |
| `session_id`     | `str` | Yes      | Session to fork. Resolved project store first, then global.                                  |
| `--at-message N` | `int` | No       | Fork from this **0-based** message index. Valid range is `0..count-1`; out-of-range exits 1. |
| `--title "..."`  | `str` | No       | Optional title for the forked session.                                                       |

An unknown id exits **1** with the same remediation text as the other session commands:

```
Session not found: sess_missing
Use 'praisonai session list' to see available sessions
```

An out-of-range `--at-message` is rejected up front (no silent slice wrap):

```
--at-message 99 is out of range (session has 14 messages, valid 0..13)
```

**JSON output** — pass the global `--json` flag for a machine-readable result:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session fork sess_abc12345 --at-message 8 --title "cookie-path" --json
```

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "forked": true,
  "parent_id": "sess_abc12345",
  "session_id": "sess_def67890",
  "from_message_index": 8,
  "title": "cookie-path"
}
```

`from_message_index` and `title` are `null` when not passed.

### Resume a Session

`session resume` restores chat history, model, and agent name from a previous session.

History is preserved by default via `compact` retention — older turns are summarised and archived rather than dropped. See [Session Persistence — Retention Policies](/docs/docs/features/session-persistence#retention-policies).

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session resume my-project
```

**Expected Output:**

```
🔄 Session Resumed
┌─────────────────────────────────────────────┐
│ Session: my-project                         │
│ Model:   gpt-4o                             │
│ Messages restored: 12                       │
│ Usage:   10,000 in / 2,345 out · $0.0140   │
└─────────────────────────────────────────────┘

--- Restored Conversation ---
[user] Can you explain the authentication flow?
[assistant] Based on the code...
```

The `Usage:` line shows cumulative tokens and cost accumulated across all previous prompts in this session. If no usage has been recorded yet, the line is omitted.

#### Resume and continue with a prompt

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session resume my-project "Now refactor the auth module"
```

State is rehydrated, then the prompt runs through the shared `praisonai run --session <id>` path. The resume panel is suppressed when a prompt is provided — the run pipeline emits the only top-level output.

#### Show transcript only (legacy view)

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session resume my-project --transcript
```

Use `--transcript` to inspect a session without restoring state. The panel title shows "Session Transcript".

#### Cross-store lookup

`session resume` finds a session whether it was created via `praisonai run --continue` (project store) or via the gateway/TUI (global store). See [Storage Backends](/docs/storage/backends).

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant CLI as praisonai CLI
    participant PS as Project Store
    participant GS as Global Store
    participant Run as run pipeline

    User->>CLI: session resume <id> ["prompt"]
    CLI->>PS: session_exists(<id>)?
    alt found in project store
        PS-->>CLI: RehydratedSession
    else fall back
        CLI->>GS: session_exists(<id>)?
        GS-->>CLI: RehydratedSession or not found
    end
    alt prompt provided
        CLI->>Run: _run_prompt(prompt, model, session=<id>)
        Run-->>User: continuation output
    else no prompt
        CLI-->>User: "Session Resumed" panel + last 10 messages
    end

    classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef store fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef run fill:#10B981,stroke:#7C90A0,color:#fff

    class User,CLI cli
    class PS,GS store
    class Run run
```

<Note>
  When you pass a continuation prompt, the resume panel is suppressed — the run pipeline emits the only top-level output. To inspect a session without continuing, use `--transcript` or omit the prompt.
</Note>

### Search across sessions

`session search` runs a ranked full-text search over every stored transcript — the same FTS5/bm25 engine as the [`session_search` agent tool](/docs/features/cross-session-recall), reachable from the terminal.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session search "billing migration"
praisonai session search "billing migration" --limit 10 --window 8
```

It iterates the same canonical stores as `resume` / `show` / `delete` / `export`, dedupes hits by `session_id` across stores, and prints a table ranked by score (or JSON with `--json`). See [Session Search](/docs/cli/session-search) for the full page.

***

## Cost & Token Tracking

PraisonAI accumulates input/output/cached tokens and dollar cost on every session run, so you can see exactly what a conversation has spent.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Per-Session Usage Accounting"
        Run[💬 praisonai run] --> LLM[🤖 LLM Call]
        LLM --> Collector[📊 Token Collector]
        Collector --> Accum[➕ accumulate_session_usage]
        Accum --> Store[💾 Session Store]
        Store --> Footer[🧾 1,240 in / 3,980 out · $0.0140]
        Store --> List[📋 session list: Tokens / Cost]
        Store --> Resume[↩️ resume rehydrates totals]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef storage fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Run input
    class LLM,Collector,Accum process
    class Store storage
    class Footer,List,Resume output
```

### Quick Start

<Steps>
  <Step title="Run with a session">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai run "Summarise the Q3 report" --session q3-review
    ```

    After the answer, the CLI prints a one-line footer:

    ```
    1,240 in / 3,980 out · $0.0140
    ```
  </Step>

  <Step title="Check cumulative usage">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai session list
    ```

    ```
    ID          Name        Status   Events   Tokens    Cost      Updated
    q3-review   q3-review   active   1        12,440    $0.0710   10:14
    ```
  </Step>

  <Step title="Continue — totals keep accumulating">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai run "Drill into the revenue line" --session q3-review
    ```

    Totals continue from where you left off — they do not reset.
  </Step>
</Steps>

### What gets persisted

Each session stores a `usage` object in its metadata:

| Field           | Type    | Default | Description                                         |
| --------------- | ------- | ------- | --------------------------------------------------- |
| `input_tokens`  | `int`   | `0`     | Cumulative prompt tokens across the session.        |
| `output_tokens` | `int`   | `0`     | Cumulative completion tokens across the session.    |
| `cached_tokens` | `int`   | `0`     | Cumulative cached tokens (provider-reported).       |
| `total_tokens`  | `int`   | `0`     | `input + output` total.                             |
| `cost`          | `float` | `0.0`   | Cumulative dollar cost rounded to 6 decimal places. |
| `requests`      | `int`   | `0`     | Cumulative number of LLM interactions.              |

The persisted `total_tokens` and `cost` fields are exactly what [`praisonai usage`](/docs/cli/usage) aggregates across sessions by day, model, or project.

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "session_id": "q3-review",
  "metadata": {
    "usage": {
      "input_tokens": 1240,
      "output_tokens": 3980,
      "cached_tokens": 0,
      "total_tokens": 5220,
      "cost": 0.014,
      "requests": 1
    },
    "total_tokens": 5220,
    "cost": 0.014
  }
}
```

### Footer format

After each prompt run with an active session, the CLI prints:

```
1,240 in / 3,980 out · $0.0140
```

Format: `"{input:,} in / {output:,} out · ${cost:.4f}"` — locale-formatted integers, 4-decimal cost. The footer is suppressed in `--json` mode but usage is still persisted.

### Resume behaviour

Totals are rehydrated on `--continue` / `--session <id>` and keep accumulating — they do not reset. The resume panel shows a usage summary line:

```
╭─ Session Resumed: q3-review ──────────────────────────╮
│ Session: q3-review                                    │
│ Model:  gpt-4o-mini                                   │
│ Messages restored: 3                                  │
│ Usage:  12,440 tokens · $0.0710 (3 requests)          │
╰───────────────────────────────────────────────────────╯
```

### How it works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Run as praisonai run
    participant Collector as token_collector
    participant Pricing as cost_tracker.get_pricing
    participant Store as project session store

    User->>Run: "Summarise the Q3 report" --session q3-review
    Run->>Collector: per-call usage (input/output/cached)
    Run->>Pricing: price by model
    Pricing-->>Run: delta cost
    Run->>Store: merge deltas into metadata.usage
    Store-->>Run: updated cumulative totals
    Run->>Collector: reset() (avoid double-counting)
    Run-->>User: answer + footer "1,240 in / 3,980 out · $0.0140"
```

### Reading usage programmatically

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.cli.state.project_sessions import (
    read_session_usage,
    format_usage_footer,
)

usage = read_session_usage("q3-review")
print(format_usage_footer(usage))
# 1,240 in / 3,980 out · $0.0140

print(f"Cumulative requests: {usage['requests']}")
print(f"Cached tokens: {usage['cached_tokens']:,}")
```

### Notes

<AccordionGroup>
  <Accordion title="Best-effort — never breaks a run">
    Any failure (pricing lookup, persistence error, missing collector) leaves the session untouched. Usage accounting never breaks a run.
  </Accordion>

  <Accordion title="Multi-model aware">
    When a run uses more than one model, each model's tokens are priced individually with `get_pricing(model_name)`.
  </Accordion>

  <Accordion title="Cached tokens tracked separately">
    Provider-reported cached reads are accumulated in `cached_tokens` but excluded from cost — the provider already discounts them.
  </Accordion>

  <Accordion title="Footer only appears with an active session">
    One-shot `praisonai run "..."` without `--session` runs without footer output.
  </Accordion>
</AccordionGroup>

***

### Show Session Details

`session show` resolves against the same stores as `list` / `resume`, so any id you can list or resume is also showable.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session show my-project
```

**Expected Output:**

```
📊 Session Details: my-project

┌─────────────────────┬────────────────────────────┐
│ Property            │ Value                      │
├─────────────────────┼────────────────────────────┤
│ Session ID          │ my-project                 │
│ Agent               │ Researcher                 │
│ Model               │ gpt-4o                     │
│ Created             │ 2024-12-16 15:30:00        │
│ Updated             │ 2024-12-16 15:45:00        │
│ Messages            │ 12                         │
└─────────────────────┴────────────────────────────┘

Recent Messages:
────────────────────────────────────────────────────
[User] Can you explain the authentication flow?
[Agent] Based on the code, the authentication...
────────────────────────────────────────────────────
[User] How do I add OAuth support?
[Agent] To add OAuth support, you would need to...
────────────────────────────────────────────────────
```

`session show` now resolves through the same shared session resolver as `list` / `resume`, so its output includes the persisted **agent**, **model**, **created / updated** times, and **message count** for the resolved session.

<Note>
  **Identity invariant (praisonai #3133):** any id shown by `praisonai session list` — or resumable via `praisonai run --continue` — can be inspected with `session show`, exported with `session export`, and deleted with `session delete` using the **same id**. There is no hidden second store: `show` / `delete` / `export` now route through the same `DefaultSessionStore` (project-scoped + global) that `list` / `resume` use, so `id → session` is unambiguous. [praisonai/PraisonAI#3201](https://github.com/MervinPraison/PraisonAI/issues/3201) made this an invariant *by construction*: both the list-path and the show/delete/export-path now delegate to a single `canonical_cli_stores()` helper, and a regression test asserts they enumerate identical store instances.
</Note>

<Warning>
  **Legacy store deprecation:** the legacy per-session directory store (`SessionManager`) is still consulted as a **best-effort fallback** during the deprecation window, but **new** sessions land in `DefaultSessionStore`. If you're on an old version with sessions only in the legacy store, re-create or `session export` them so they migrate cleanly.
</Warning>

#### Show a Read-Only Recap

Render a "where were we" block instead of raw session details. Non-destructive — never modifies the session or triggers compaction.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session show <session-id> --recap
```

The recap prints in a `Session Recap` panel:

```
╭─ Session Recap ──────────────────────────────────────╮
│ 📌 Recap — where we were:                            │
│ [Previous conversation summary]                      │
│ User asked about deploy runbook; we outlined 1–3.    │
│ Recent:                                              │
│ • user: what about step 4?                           │
│ • assistant: step 4 covers rollback…                 │
╰──────────────────────────────────────────────────────╯
```

Combine with the global `--json` flag for structured output:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session show <session-id> --recap --json
# {"session_id": "...", "recap": "📌 Recap — where we were:\n..."}
```

This is the CLI counterpart of the [`/recap` bot chat command](/docs/features/bot-commands#recap) — same read-only recap, surfaced from the terminal.

#### Hand off a Session (Continuation Prompt)

Assemble a self-contained continuation prompt from durable state — recap describes where we were; handoff is a prompt a fresh context can act on.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session handoff <session-id>
praisonai session handoff <session-id> --json
praisonai session handoff <session-id> --copy
```

The prompt prints in a `Session Handoff` panel:

```
╭─ Session Handoff ────────────────────────────────────╮
│ You are resuming an interrupted run. Recover from     │
│ durable state; do not trust memory of a previous      │
│ conversation.                                         │
│                                                       │
│ GOAL: Ship the logging refactor                       │
│ DEFINITION OF DONE: All modules use structured logging│
│ PROGRESS: 3 of 5 workflow steps done; last completed: │
│ wire logger                                           │
│ RECENT ACTIONS (recap-derived):                       │
│ Recap — where we were: refactored logging in main.py  │
│ NEXT: continue toward the remaining work. First verify│
│ the current state on disk before acting.              │
╰───────────────────────────────────────────────────────╯
```

Combine with the global `--json` flag for structured output:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session handoff <session-id> --json
# {"session_id": "...", "handoff": "...", "has_goal": true, "has_checkpoint": true}
```

Defaults to the most recent session when no id is given. See [Session Handoff](/docs/features/session-handoff) for the full feature, options, and Python API.

### Export a Session

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session export my-project --format md
praisonai session export my-project --format json
```

`session export` resolves the id through the same shared resolver, so an id from `session list` always exports the same session — with a legacy-store fallback for legacy-only ids.

### Delete a Session

Delete targets the **single canonical store** that owns the id — a project-scoped `session delete` never removes an unrelated same-id session that lives only in the global store.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session delete my-project
```

**Expected Output:**

```
Delete session my-project? [y/N]: y
✅ Deleted session: my-project
```

Skip the confirmation prompt with `--yes` / `-y`:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session delete my-project --yes
```

An I/O failure is now surfaced instead of a fake success — the command exits non-zero:

```
❌ Failed to delete session: my-project
```

<Note>
  Legacy `SessionManager` sessions created before 2026-07-17 are also swept on delete, so a duplicate legacy record can't resurface a session you already deleted.
</Note>

### Export a Session

`session export` writes a resolved session as markdown (default) or JSON.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Print as markdown (default)
praisonai session export my-project

# Or as JSON
praisonai session export my-project --format json

# Save to a file
praisonai session export my-project --format md --output my-project.md
```

**Expected Output (markdown):**

```
# Session: research-bot

- **Session ID**: my-project
- **Agent**: research-bot
- **Model**: gpt-4o
- **Created**: 2024-12-16 15:30:00
- **Updated**: 2024-12-16 15:45:00
- **Messages**: 12

## Conversation

### user

Can you explain the authentication flow?

### assistant

Based on the code, the authentication...
```

When `--output` is set, the CLI writes the file and prints:

```
✅ Exported to: my-project.md
```

<Note>
  The interactive [`/export` slash command](/docs/cli/slash-commands#export) uses this **same renderer** — when a session id resolves, the REPL's `/export [file]` is byte-for-byte identical to `praisonai session export <id>`. It falls back to the in-memory transcript when no session is persisted yet, so `/export` works without `--session`.
</Note>

| Flag                        | Default    | Description                                                                                                                                                 |
| --------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--format` / `-f`           | `md`       | Export format — `md` or `json`                                                                                                                              |
| `--output` / `-o`           | *(stdout)* | Write to a file instead of printing                                                                                                                         |
| `--sanitise` / `--sanitize` | *(off)*    | Redact secrets, absolute paths, the cwd, and embedded file contents with stable `[redacted:<category>:<n>]` placeholders. Opt-in; default export unchanged. |
| `--redact-level`            | `standard` | Redaction level when `--sanitise` is set: `standard` or `strict`. An unknown value is rejected with an actionable error.                                    |

Legacy-only sessions fall back to the `SessionManager` exporter automatically — and legacy sessions **also** honour `--sanitise` now, so a raw transcript is never leaked through the fallback (see [Sanitise before sharing](#sanitise-before-sharing)).

#### Sanitise before sharing

`praisonai session export <id> --sanitise` (alias `--sanitize`) redacts secrets, absolute file paths, the working directory, and file contents embedded in tool I/O — replacing them with stable `[redacted:<category>:<n>]` placeholders — so a session is safe to paste into a bug report, share with a teammate, or attach to an audit trail. The default export (no flag) is **byte-for-byte unchanged**; sanitisation is strictly opt-in.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Sanitised Export"
        Raw[📝 Raw transcript] --> Redact[🛡️ redact_transcript<br/>standard · strict]
        Redact --> Secrets["[redacted:secret:n]"]
        Redact --> Paths["[redacted:path:n]"]
        Secrets --> Safe[✅ Safe to share]
        Paths --> Safe
    end

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef store fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Raw input
    class Redact process
    class Secrets,Paths store
    class Safe output
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Default — raw transcript, unchanged (may leak secrets)
praisonai session export my-project --format json

# Opt-in — secrets, paths, and cwd replaced with stable placeholders
praisonai session export my-project --format json --sanitise

# Same, US spelling
praisonai session export my-project --format md --sanitize

# Broader net (bearer tokens + PEM private-key blocks)
praisonai session export my-project --sanitise --redact-level strict
```

Every masked span becomes `[redacted:<category>:<n>]`, where the same source value maps to the **same** placeholder within one export — so the reader still follows which value recurs where, without ever seeing it.

```
Before:   read /home/alice/project/config.yaml — api_key=sk-ABCDEF0123456789ABCDEF
After:    read [redacted:path:1] — api_key=[redacted:secret:1]
```

The same path appearing three times becomes `[redacted:path:1]` three times, not `:1`, `:2`, `:3`.

**Redaction levels.**

| Level                             | Also masks                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | When to use                                                                                                                           |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `standard` (default for `export`) | Process-registered secrets; OpenAI-style `sk-…` **and Stripe `sk_live_…` / `sk_test_…`** keys; Slack / GitHub / AWS / Google / JWT token shapes; `key = value` / `key: value` pairs whose key **contains** any of `api_key`, `secret`, `token`, `password`, `access_key` — so `AWS_SECRET_ACCESS_KEY=…` and `STRIPE_SECRET_KEY=…` are caught, not only keys that *end* in one; absolute POSIX / Windows / UNC paths; and the current working directory. Detected secret values are masked as a **whole** — no half-masking when a value contains `/`. | Almost every case — a safe default that keeps the transcript readable.                                                                |
| `strict` (default for `share`)    | Everything `standard` masks, **plus** `Authorization: Bearer …` credentials and PEM `-----BEGIN … PRIVATE KEY-----` blocks                                                                                                                                                                                                                                                                                                                                                                                                                            | Wide sharing (public gist, open bug report, external audit) where over-redacting beats leaking. `session share` uses this by default. |

An unknown value is rejected up front (exit 1):

```
$ praisonai session export my-project --sanitise --redact-level loose
❌ Invalid --redact-level 'loose'. Choose one of: standard, strict.
```

<Note>
  The redactor is a small, self-contained helper (`praisonai_code/cli/state/redact.py`, stdlib only — no new dependencies). It reuses `praisonaiagents.secrets` when available, so any value registered via the process-wide secret registry is masked first, even if it does not match a built-in token shape.
</Note>

<Warning>
  `--sanitise` is a **best-effort safety net**, not a compliance guarantee. It masks the shapes above deterministically, but a novel secret shape that matches no built-in pattern and was never registered can still slip through. Before publishing a redacted transcript widely, skim the output for anything that looks like a credential and consider `--redact-level strict`.
</Warning>

<Note>
  Legacy `SessionManager` sessions (created before 2026-07-17) also honour `--sanitise` — they used to return a raw verbatim transcript ignoring the flag, a secret-leak gap closed by [praisonai/PraisonAI#3434](https://github.com/MervinPraison/PraisonAI/pull/3434).
</Note>

<Note>
  To hand a redacted transcript straight to someone, use [`session share`](#share-a-session) instead — it runs this same redactor, wraps the result in a self-contained HTML file, and returns a `file://` link, so there's no manual `export --sanitise > out.md` plus attach step.
</Note>

**Python API.** The same redaction is available on the resolver helper — handy when scripting an export from a notebook or CI job:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai_code.cli.state.session_resolver import export_session

# Default — raw
raw = export_session("my-project", format="json")

# Opt-in — sanitised
safe = export_session("my-project", format="json", redact=True, redact_level="standard")
```

And directly against a resolved payload (dict → dict, input never mutated):

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai_code.cli.state.redact import redact_transcript, REDACT_LEVELS

safe_payload = redact_transcript(
    payload,                          # dict from resolved.to_dict()
    level="standard",                 # or "strict"; must be in REDACT_LEVELS
    extra_secrets=["HUNTER2SECRET"],  # extra literal values to mask
)
```

### Share a Session

`session share` publishes a redacted, read-only HTML transcript to `~/.praisonai/shares/` and returns a `file://` link — no external service or dependency.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Share Flow"
        Session[💾 Session] --> Resolve[🔎 session_resolver]
        Resolve --> Export[📝 export_session<br/>format=md, redact=True]
        Export --> Redact[🛡️ redact_transcript<br/>standard · strict]
        Redact --> HTML[📄 Self-contained HTML<br/>~/.praisonai/shares/&lt;hash&gt;.html]
        HTML --> Link[🔗 file:// link]
    end

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef guard fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Session input
    class Resolve,Export process
    class Redact guard
    class HTML,Link output
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# default: strict (safe for public sharing)
praisonai session share sess-abc123

# → ✅ Shared session: sess-abc123
# → ℹ Link: file:///Users/alice/.praisonai/shares/1a2b3c4d5e6f7a8b.html

# opt down to standard if the recipient is trusted
praisonai session share sess-abc123 --redact-level standard

# Revoke — deletes the shared HTML file
praisonai session unshare sess-abc123

# → ✅ Unshared session: sess-abc123
```

`share` resolves `session_id` through the same shared resolver as `list` / `resume` / `show` / `export` (project store first, then global default store, then a best-effort legacy `SessionManager` fallback for pre-2026-07-17 ids), then calls `export_session(session_id, format="md", redact=True, redact_level=<level>)`. Sharing is **always** redacted — there is intentionally no `--no-redact` escape hatch. The redacted Markdown is wrapped in a single self-contained HTML file (inline CSS, no external assets), with the transcript inserted via `html.escape` inside a `<pre>` block so no session text can be interpreted as markup.

| Argument     | Type  | Required | Description                                                                   |
| ------------ | ----- | -------- | ----------------------------------------------------------------------------- |
| `session_id` | `str` | Yes      | Session ID to publish. Same id space as `session list` / `resume` / `export`. |

| Flag             | Type                          | Default      | Description                                                                                                                                                                                                                                                                                          |
| ---------------- | ----------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--redact-level` | `str` (`standard` / `strict`) | **`strict`** | Redaction level applied before publish — same meaning as [`session export --sanitise`](#sanitise-before-sharing). Defaults to `strict` because `share` publishes a transcript for wider distribution; pass `--redact-level standard` to opt down. An unknown value is rejected up front with exit 1. |

<Note>
  `--redact-level` defaults differ per sub-command: `session export --sanitise` defaults to `standard`, while `session share` defaults to **`strict`** because it publishes a transcript intended for wider distribution.

  | Command          | `--redact-level` default |
  | ---------------- | ------------------------ |
  | `session export` | `standard`               |
  | `session share`  | **`strict`**             |
</Note>

**Expected Output:**

```
✅ Shared session: sess-abc123
ℹ Link: file:///Users/alice/.praisonai/shares/1a2b3c4d5e6f7a8b.html
```

**JSON output** — pass `--json` for a machine-readable link and on-disk path:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "session_id": "sess-abc123",
  "shared": true,
  "url": "file:///Users/alice/.praisonai/shares/1a2b3c4d5e6f7a8b.html",
  "path": "/Users/alice/.praisonai/shares/1a2b3c4d5e6f7a8b.html"
}
```

An id that resolves nowhere exits 1 with the same remediation text as the other session commands:

```
❌ Session not found: sess-abc123
Use 'praisonai session list' to see available sessions
```

The `.html` file lands under the canonical data home alongside `sessions/`, so shares never split across a second home root:

```
~/.praisonai/
└── shares/                        # NEW — created on first share
    └── <sha256(id)[:16]>.html
```

The filename is `sha256(id)[:16]` — a **stable per-session path**. Re-sharing the same id overwrites the same file, and the write is **atomic**: the HTML is written to a sibling temp file, then `os.replace()` swaps it in, so a failed or interrupted write never truncates a previously published transcript. On an `OSError` the temp file is cleaned up and the command exits non-zero.

<Warning>
  Redaction is a **best-effort safety net**, not a compliance guarantee — the same caveat as [`export --sanitise`](#sanitise-before-sharing). A novel secret shape that matches no built-in pattern and was never registered via `praisonaiagents.secrets` can still slip through. Skim the published HTML before sharing widely; use `--redact-level strict` for anything public.
</Warning>

<Note>
  The shared file is a plain **`file://`** link on the local filesystem. It is not uploaded to any server — only people who can read `~/.praisonai/shares/` (or receive the file itself) can open it. To share externally, attach or upload the file separately.
</Note>

<Note>
  The `.html` filename is `sha256(id)[:16]`, not the session id — so ids containing `/`, `..`, or shell metacharacters are safe on every filesystem.
</Note>

<Tip>
  `share` is idempotent: re-running it overwrites the same file atomically, so an updated transcript never coexists with a stale copy at a second URL.
</Tip>

### Unshare a Session

`session unshare` revokes a previously published transcript by deleting the shared HTML file.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session unshare sess-abc123
```

`unshare` resolves the shared path deterministically via the same `sha256(id)[:16]` mapping — no session lookup — so it works even if the underlying session has since been deleted. A missing file is a clean **no-op**, not an error, so the command is idempotent and safe to script. A real `OSError` (permission denied, disk error) exits 1 with an actionable message.

| Argument     | Type  | Required | Description                |
| ------------ | ----- | -------- | -------------------------- |
| `session_id` | `str` | Yes      | Same id passed to `share`. |

**Expected Output (a file was removed):**

```
✅ Unshared session: sess-abc123
```

**Expected Output (nothing was published):**

```
ℹ No shared transcript found for: sess-abc123
```

**JSON output:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{"session_id": "sess-abc123", "revoked": true}
```

`revoked: false` means nothing was there to revoke — not that the operation failed.

<Note>
  `unshare` on a session that was never shared is a clean no-op, not an error. Safe to script.
</Note>

#### Related

* [Sanitise before sharing](#sanitise-before-sharing) — the underlying redactor is shared; `share` = `export --sanitise` + HTML wrap + atomic publish. Same `standard` vs `strict` table.
* [List sessions](#list-sessions) — how to find the id to share.
* Upstream: PraisonAI issue [#3590](https://github.com/MervinPraison/PraisonAI/issues/3590), commits `9c27592` + `cdd41eb`.

### Import a Session

`session import` reads a previously-exported JSON file and prints the resulting id.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session import my-project.json
```

**Expected Output:**

```
✅ Imported session: my-project
```

<Note>
  Only JSON files are accepted. The imported id is printed and can then be resumed with `praisonai --continue` or `praisonai session resume`.
</Note>

### Help

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai session help
```

**Expected Output:**

```
Session Commands:

  praisonai session start <name>    - Start a new session
  praisonai session list            - List all sessions
  praisonai session resume <id> [prompt]      - Resume a session, optionally with a prompt
                                --transcript  - Show transcript only (legacy view)
  praisonai session search <query> [--limit N] [--window N]  - Ranked full-text search across sessions
  praisonai session show <name> [--recap]     - Show session details (or a read-only recap)
  praisonai session handoff <id> [--json] [--copy]  - Generate a continuation prompt from durable state
  praisonai session rename <id> "<title>"     - Give a session a human-readable title ("" clears it)
  praisonai session fork <id> [--at-message N] [--title "..."]  - Fork a session into a new child timeline
  praisonai session delete <name>   - Delete a session
  praisonai session export <name> [--format md|json] [--output FILE]
                                  [--sanitise|--sanitize] [--redact-level standard|strict]  - Export a session
  praisonai session share <id> [--redact-level standard|strict]       - Publish a redacted HTML transcript (file:// link); defaults to strict
  praisonai session unshare <id>                                      - Revoke a shared transcript
  praisonai session import <file>                                     - Import a session (JSON)
  praisonai session help            - Show this help

Using Sessions with Prompts:
  praisonai "prompt" --session <name>   - Run with session context
```

## Token and Cost Tracking

Every session accumulates cumulative token usage and cost across all prompts, visible in `session list` and the resume panel.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Session usage accounting"
        Run[📝 praisonai run] --> Collect[🧠 token_collector]
        Collect --> Accumulate[💾 accumulate_session_usage]
        Accumulate --> Persist[(📦 .praisonai/sessions/&lt;id&gt;.json)]
        Persist --> Footer[⚡ 1,240 in / 3,980 out · $0.0140]
        Persist --> List[📋 praisonai session list]
        Persist --> Resume[🔁 --continue rehydrates totals]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef store fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class Run input
    class Collect,Accumulate process
    class Persist store
    class Footer,List,Resume result
```

After each run, a single-line footer is printed:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
$ praisonai run "Summarise yesterday's PRs"
... (agent output) ...
1,240 in / 3,980 out · $0.0140
```

The middle dot is U+00B7 (`·`). The footer reflects **cumulative** totals since session start, not just the last prompt. It is silently suppressed in JSON mode (`--json` / `--output json`) — there is no `--no-usage` flag.

When you resume a session, the cumulative totals are rehydrated so subsequent prompts keep accumulating:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
$ praisonai run --continue
Session: research-bot
Model:   gpt-4o-mini
Messages restored: 12
Usage:   12,345 in / 38,902 out · $0.0140
```

***

## Working with `praisonai run`

The same project-scoped store powers `--continue` and `--session` on `praisonai run`. As of [PR #1963](https://github.com/MervinPraison/PraisonAI/pull/1963), every surface restores history from and saves to this store:

| `praisonai run` surface                        | Restores history? | Saves new messages?      |
| ---------------------------------------------- | ----------------- | ------------------------ |
| Prompt mode (`praisonai run "..."`)            | Yes               | Yes (unless `--no-save`) |
| YAML / file mode (`praisonai run agents.yaml`) | Yes               | Yes (unless `--no-save`) |
| Actions mode (`--output actions`)              | Yes               | Yes (unless `--no-save`) |

As of the fix for [issue #2700](https://github.com/MervinPraison/PraisonAI/issues/2700), the actions-mode row is fully honoured for `--auto-save`, `--session`, `--continue`, and `--fork`. If you hit a `TypeError` about `auto_save` on an older `praisonai-code` build, upgrade and retry — see the [run.mdx troubleshooting section](/docs/docs/cli/run#troubleshooting-session-continuity).

As of [PR #2277](https://github.com/MervinPraison/PraisonAI/pull/2277), `--session <id>` and `--continue` now persist `model` and `agent_name` into session metadata so a later `session resume` reproduces the same configuration deterministically. For advanced programmatic use, the `rehydrate_session` helper in `praisonai.cli.session` returns a `RehydratedSession` with `session_id`, `chat_history`, `model`, `agent_name`, `metadata`, and `found` fields — see the [SDK reference](/docs/sdk/reference/praisonaiagents/modules/session) for details.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai run --continue "Add tests for the new endpoint"
```

<Note>
  Identical repeats survive resume. A bare `yes` typed three times is three turns; the resumed history is restored **by position**, not deduplicated — so `--continue` replays the conversation exactly as it happened.
</Note>

See [Run](/docs/cli/run) for complete session continuity documentation.

## Using Sessions with Prompts

### Continue a Conversation

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# First message
praisonai "What is Python?" --session learning

# Follow-up (context preserved)
praisonai "How do I install it?" --session learning

# Another follow-up
praisonai "Show me a hello world example" --session learning
```

**Expected Output (third message):**

````
📂 Session: learning (3 messages)

╭────────────────────────────────── Response ──────────────────────────────────╮
│ Based on our conversation about Python, here's a hello world example:        │
│                                                                              │
│ ```python                                                                    │
│ print("Hello, World!")                                                       │
│ ```                                                                          │
│                                                                              │
│ After installing Python as we discussed, save this to a file called         │
│ `hello.py` and run it with `python hello.py`                                │
╰──────────────────────────────────────────────────────────────────────────────╯
````

### Session with Other Features

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Session with memory
praisonai "Remember my preferences" --session project --memory

# Session with knowledge
praisonai "Search the docs" --session project --knowledge

# Session with planning
praisonai "Plan the implementation" --session project --planning
```

## Use Cases

### Project-Based Conversations

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Start project session
praisonai session start website-redesign

# Multiple conversations over time
praisonai "What's the current design?" --session website-redesign
praisonai "Suggest improvements" --session website-redesign
praisonai "Create implementation plan" --session website-redesign
```

### Learning Sessions

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Create learning session
praisonai session start learn-rust

# Progressive learning
praisonai "Explain ownership in Rust" --session learn-rust
praisonai "Show me an example" --session learn-rust
praisonai "What about borrowing?" --session learn-rust
```

### Code Review Sessions

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Start review session
praisonai session start pr-review-123

# Review conversation
praisonai "Review this PR" --session pr-review-123 --fast-context ./src
praisonai "What about security concerns?" --session pr-review-123
praisonai "Summarize the review" --session pr-review-123
```

## Auto-Save Sessions

Automatically save sessions after each agent run using the `--auto-save` flag:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Auto-save session with each interaction
praisonai "Analyze this code" --auto-save my-project

# Continue the conversation (auto-saved)
praisonai "Now refactor it" --auto-save my-project
```

<Note>
  As of PR [#4943](https://github.com/MervinPraison/PraisonAI/pull/4943), the **interactive** TUI (`praisonai` with no prompt) auto-persists every conversation by default — no `--auto-save` flag required, so `/sessions` and `/continue` work out of the box. The `--auto-save` flag shown here still applies to non-interactive `praisonai "<prompt>"` runs. See [Interactive TUI → Automatic session persistence](/docs/cli/interactive-tui#automatic-session-persistence).
</Note>

### Python API

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

from praisonaiagents.config import MemoryConfig

agent = Agent(
    name="Assistant",
    memory=MemoryConfig(auto_save="my-project")  # Auto-save session after each run
)

agent.start("Analyze this code")  # Session saved automatically
```

## History in Context

Load conversation history from previous sessions into the current context:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Load history from last 5 sessions
praisonai "Continue our discussion" --history 5
```

### Python API

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    memory=True,
    context=True,  # Enable context management for history
)

# Agent now has context from previous sessions
agent.start("What did we discuss yesterday?")
```

## Workflow Checkpoints

Save and resume workflow execution at any step:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import AgentFlowManager

manager = WorkflowManager()

# Execute with checkpoints (saves after each step)
result = manager.execute(
    "deploy-workflow",
    checkpoint="deploy-v1"
)

# Resume from checkpoint if interrupted
result = manager.execute(
    "deploy-workflow",
    resume="deploy-v1"
)

# List all checkpoints
checkpoints = manager.list_checkpoints()

# Delete a checkpoint
manager.delete_checkpoint("deploy-v1")
```

### Checkpoint Storage

```
~/.praisonai/
└── checkpoints/
    ├── deploy-v1.json
    └── build-v2.json
```

## Project-Scoped Sessions

Sessions are automatically scoped to your current project. PraisonAI detects your project by finding the git repository root, or uses the current working directory as a fallback.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
flowchart TB
    subgraph "Project Detection"
        CWD[📁 Current Directory] --> Git{🔍 Git Root?}
        Git -->|Found| GitRoot[📂 Git Repository Root]
        Git -->|Not Found| UseCWD[📂 Use Current Directory]
        GitRoot --> Hash[🔑 SHA256 Hash]
        UseCWD --> Hash
        Hash --> ProjectID[🆔 Project ID<br/>abc12345]
    end
    
    ProjectID --> SessionDir[💾 Session Directory<br/>~/.praisonai/sessions/projects/abc12345/]
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    
    class CWD input
    class Git,Hash process
    class ProjectID,SessionDir output
```

**Project identification:**

* **Project ID:** First 8 characters of SHA256 hash of project root path
* **Git detection:** Uses `git rev-parse --show-toplevel` with 5-second timeout
* **Fallback:** Current working directory if not in a git repository

**Storage structure:**

```
~/.praisonai/sessions/
├── projects/
│   ├── abc12345/          # Project sessions
│   │   ├── session-def789.json
│   │   └── session-ghi012.json
│   └── xyz98765/          # Another project
│       └── session-jkl345.json
└── global/                # Legacy global sessions
    ├── old-session.json
    └── another.json
```

## Session Storage

Sessions are stored in a project-scoped layout when using the default behavior:

```
~/.praisonai/sessions/projects/{project_id}/
└── {session_id}.json
```

With project-scoped sessions, your sessions are organized by project automatically. Legacy sessions remain accessible via the `--all` flag:

```
~/.praisonai/
└── memory/
    └── praison/
        └── sessions/
            ├── my-project.json
            ├── research-task.json
            └── code-review.json
```

### Storage Backend Options

Store sessions in different backends for production deployments:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# List sessions with SQLite backend
praisonai session list --storage-backend sqlite --storage-path ~/.praisonai/sessions.db

# List sessions with Redis backend (for distributed systems)
praisonai session list --storage-backend redis://localhost:6379

# List sessions with file backend (default)
praisonai session list --storage-backend file --storage-path ~/.praisonai/sessions
```

| Backend       | Best For                             |
| ------------- | ------------------------------------ |
| `file`        | Development, debugging               |
| `sqlite`      | Production, concurrent access        |
| `redis://url` | Distributed systems, shared sessions |

<Note>
  When `--storage-path` is omitted, `--storage-backend file` writes to `~/.praisonai/sessions/` and `--storage-backend sqlite` writes to `~/.praisonai/sessions.db` (both under the canonical `~/.praisonai/` data home). Prior releases anchored these defaults under `~/.praison/` — any existing sessions there are still readable via the same code paths, but new sessions land under the canonical root. See [praisonai/PraisonAI#3203](https://github.com/MervinPraison/PraisonAI/pull/3203).
</Note>

See [Storage Backends](/docs/storage/backends) for more details.

## Concurrent Sessions

Multiple `praisonai` processes can safely share the same session — the CLI store reloads, merges, and writes under an exclusive lock so no messages are lost when the TUI, `--interactive` mode, and `praisonai "…" --session` all touch the same file.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Terminal 1: keep the TUI open on a session
praisonai tui launch --session my-project

# Terminal 2: same session, ad-hoc message via --interactive
praisonai "Add a one-line summary" --interactive --session my-project

# Both messages end up in ~/.praisonai/sessions/my-project.json
# in arrival order — no silent drops.
```

### Merge Strategy

When two writers race, the session store merges their changes:

| Field                                                                         | Merge strategy                                                                                 |
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `messages`                                                                    | Union, deduped by `(role, content, timestamp)`; on-disk order preserved, new messages appended |
| `metadata`                                                                    | Dict merge, incoming wins on key conflict                                                      |
| `total_input_tokens` / `total_output_tokens` / `total_cost` / `request_count` | `max(on_disk, incoming)`                                                                       |
| `current_model`                                                               | Incoming if set, else on-disk                                                                  |
| `updated_at`                                                                  | `max(on_disk, incoming)`                                                                       |

### Lost-Update Prevention

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant A as Process A
    participant B as Process B
    participant File as Session File
    
    A->>File: Load [m1..m10]
    B->>File: Load [m1..m10]
    B->>B: Append m11
    B->>File: Save with lock: [m1..m11]
    A->>A: Append m12
    A->>File: Save with lock
    Note over A,File: Reloads under lock, sees [m1..m11]
    Note over A,File: Merges to [m1..m11,m12]
    File-->>A: Merged result saved
    
    classDef process fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef file fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    classDef wait fill:#F59E0B,stroke:#7C90A0,color:#fff
    
    class A,B process
    class File file
```

`praisonai session show` and `praisonai session resume` always reflect the latest on-disk state — the in-process cache is invalidated automatically when another process writes (mtime-based check).

This concurrent-save safety was added in [PR #1854](https://github.com/MervinPraison/PraisonAI/pull/1854). For the equivalent feature in the SDK-level store, see [Multi-Process Safety](/docs/docs/features/session-persistence#multi-process-safety).

<Note>
  This applies to the default file-backed session store. The `sqlite` / `redis://` backends in the Storage Backend Options table above handle concurrency via the database itself; the CLI does not add its own merge layer there.
</Note>

***

## How Session IDs Resolve

Every `session` sub-command and every `--continue` / `--session <id>` flag resolves the same session by the same id through a single `session_resolver`.

### Session Identity

`show`, `delete`, and `export` now read the **same** project-scoped + global `DefaultSessionStore` that `list`, `resume`, and `--continue` already use — so any id you can list or resume is also showable, deletable, and exportable by the same id.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant CLI as praisonai session
    participant Resolver as session_resolver
    participant Store as DefaultSessionStore
    participant Legacy as SessionManager

    User->>CLI: list / resume / show / delete / export <id>
    CLI->>Resolver: resolve_session(<id>)
    Resolver->>Store: project-scoped then global lookup
    alt id in canonical store
        Store-->>Resolver: ResolvedSession
        Resolver-->>CLI: found
        CLI-->>User: ✅ result
    else legacy id (pre-2026-07-17)
        Resolver->>Legacy: best-effort fallback
        Legacy-->>Resolver: ResolvedSession or not-found
        Resolver-->>CLI: found / not-found
        CLI-->>User: result or 🚫 not-found
    end

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef resolver fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    classDef legacy fill:#F59E0B,stroke:#7C90A0,color:#fff

    class User,CLI agent
    class Resolver,Store resolver
    class Legacy legacy
```

| Sub-command                                | Store path                                                                                                                                                |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list` / `resume` / `--continue`           | project-scoped + global `DefaultSessionStore`                                                                                                             |
| `search`                                   | **same** canonical stores via `canonical_cli_stores()`, indexed with `SqliteSessionStore` (FTS5/bm25), deduped by `session_id`                            |
| `show` / `delete` / `export`               | **same** project-scoped + global `DefaultSessionStore` (via `session_resolver`)                                                                           |
| `share` / `unshare`                        | **same** resolver + `export_session` path as `export --sanitise` (`share`); `unshare` deletes `~/.praisonai/shares/<sha256(id)[:16]>.html` with no lookup |
| any of the above, pre-2026-07-17 legacy id | best-effort `SessionManager` fallback (deprecation window)                                                                                                |

<Note>
  Delete targets the single store that owns the id and honours that store's confirmation — an I/O failure exits non-zero instead of reporting a fake success. The legacy store is always swept so a shadow record can't resurface a deleted session.
</Note>

***

## Cross-Platform Support

The `praisonai session` commands work on Windows, macOS, and Linux — file locking is automatic and platform-appropriate.

| Platform                                  | File locking                                                                       | Notes                                                                |
| ----------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| Linux / macOS                             | `fcntl.flock()`                                                                    | Exclusive on write, shared on read                                   |
| Windows                                   | Whole-file lock (`max(file_size, 1)` bytes) — matches Unix `fcntl.flock` semantics | Blocking exclusive on write, shared (`LK_RLCK`) on read              |
| Other (Pyodide, minimal embedded CPython) | None — logs a one-time warning                                                     | Single-process is safe; concurrent writers may corrupt session files |

<Warning>
  If you see this warning: `File locking unavailable on this platform (fcntl not available); concurrent writers may corrupt session files.`

  This means you're running on an environment without native file locking. Restrict to a single process, or migrate to a DB-backed storage backend (link to the `sqlite` / `redis` options in the Storage Backend Options table above).
</Warning>

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant CLI1 as praisonai session start foo
    participant Code as praisonai code
    participant Session as session file
    participant CLI2 as praisonai session show foo
    
    CLI1->>Session: Acquire write lock
    CLI1->>Session: Create session data
    CLI1->>Session: Release lock
    Code->>Session: Acquire write lock
    Code->>Session: Append conversation turn
    Code->>Session: Release lock
    CLI2->>Session: Acquire read lock (waits if needed)
    Session-->>CLI2: Return session data
    CLI2->>Session: Release lock
    
    classDef cli fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef file fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    
    class CLI1,CLI2,Code cli
    class Session file
```

On Windows, sessions are stored under `%USERPROFILE%\.praisonai\sessions\{session_id}.json` following the OS convention via `Path.home()`. (Legacy pre-canonical installs may still read from `%USERPROFILE%\.praison\sessions\` as a fallback — see [Storage Paths](/docs/concepts/storage-paths).) The same directory is shared by `praisonai code`, `praisonai run`, and the gateway/TUI, so a session written by one surface is readable by the others. Cross-platform locking was added in [PR #1837](https://github.com/MervinPraison/PraisonAI/pull/1837). Concurrent multi-process writes (e.g. TUI + `praisonai --interactive` sharing the same session directory) are preserved without message loss as of [PR #1885](https://github.com/MervinPraison/PraisonAI/pull/1885) and [PR #1892](https://github.com/MervinPraison/PraisonAI/pull/1892). For the SDK-level session store with the same cross-platform guarantees, see [Session Persistence](/docs/features/session-persistence).

Example usage across platforms:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Windows (PowerShell)
praisonai session start my-project
praisonai "Analyze this file" --session my-project

# Same commands work identically on macOS / Linux
```

***

## Best Practices

<Tip>
  Use descriptive session names that reflect the project or task for easy identification.
</Tip>

<Warning>
  Long sessions accumulate tokens. Consider starting fresh sessions for unrelated topics.
</Warning>

<CardGroup cols={2}>
  <Card title="Naming">
    Use descriptive names like `project-auth-feature`
  </Card>

  <Card title="Organization">
    Create separate sessions for different projects
  </Card>

  <Card title="Cleanup">
    Delete old sessions to free up storage
  </Card>

  <Card title="Context">
    Start new sessions when changing topics significantly
  </Card>
</CardGroup>

## Related

<CardGroup cols={2}>
  <Card title="Run Command" icon="play" href="/docs/cli/run">
    Session flags and usage footer for `praisonai run`
  </Card>

  <Card title="Session Persistence" icon="database" href="/docs/features/session-persistence">
    SDK-level session management
  </Card>

  <Card title="Session Forking" icon="code-branch" href="/docs/features/session-forking">
    Fork mid-session with `/branch` or `session fork`
  </Card>

  <Card title="Cost Tracking" icon="dollar-sign" href="/docs/cli/cost-tracking">
    Per-session persistence and `/cost` slash command
  </Card>

  <Card title="Project Sessions" icon="folder-tree" href="/docs/features/project-sessions">
    Persisted usage shape and project scoping
  </Card>

  <Card title="Usage" icon="chart-line" href="/docs/cli/usage">
    Aggregate token and cost reporting across all sessions
  </Card>

  <Card title="Gateway Session Portability" icon="download" href="/docs/features/gateway-session-portability">
    Back up / migrate / restore with `praisonai gateway sessions export` / `import`
  </Card>
</CardGroup>
