# session_store

Wrap a state store in a `SessionStore` that journals sessions, turns, and tool receipts for both built-in harnesses.

`nanocode::session_store` returns a `SessionStore` with the Codex and Claude checkpoint formats registered. `builder.durability` takes it, or any `StateStore` directly. With a request ID, a prompt can be repeated: the repeat recovers a crashed turn or returns the recorded result.

```rust
use nanocode::{JsonlStore, Nanocode, PromptRequest};

// 1. Open a JSONL journal and wrap it in a session store
let store = nanocode::session_store(
    JsonlStore::open_async("sessions.jsonl").await?,
)?;

// 2. Journal every session of the runtime in the store
let runtime = Nanocode::builder()
    .durability(store.clone())
    .build_async()
    .await?;
let (agent, _events) = runtime.spawn().build().await?;
agent
    .prompt(
        PromptRequest::new("Run the checklist.")
            .request_id("checklist-1"),
    )
    .await?
    .await?;

// 3. Wait for the writes, then read the saved turns
agent.flush().await?;
println!(
    "{} turns",
    store.load(agent.session_id()).await?.turns.len()
);
```

## Signature

```rust
// Accepts any StateStore, such as JsonlStore, MemoryStore, SqlStore, or
// KeyValueStore
pub fn session_store<S: durability::StateStore + 'static>(store: S)
    -> durability::Result<durability::SessionStore>
```

## Parameters

### store

* **Type:** `impl StateStore + 'static`

The storage behind the session store. Without `builder.durability`, native runtimes use a `JsonlStore` in the nanocode home, and WASM runtimes use a `MemoryStore`.

| Store           | Path                                  | Notes                                                                                               |
| --------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `JsonlStore`    | `nanocode::JsonlStore`                | One append-only file with a rebuildable SQLite index beside it. Native only, on a local filesystem. |
| `MemoryStore`   | `nanocode::durability::MemoryStore`   | Keeps sessions in memory, for tests and ephemeral sessions.                                         |
| `SqlStore`      | `nanocode::durability::SqlStore`      | Runs over a host that implements `Sql`, such as a SQLite database.                                  |
| `KeyValueStore` | `nanocode::durability::KeyValueStore` | Runs over a host that implements `KeyValue` with compare-and-swap.                                  |

```rust
use nanocode::durability::MemoryStore;

// Keeps sessions only for the life of the process
let store = nanocode::session_store(MemoryStore::new()?)?;
```

## Returns

### SessionStore

* **Type:** `durability::SessionStore`

A cloneable catalog of the sessions in the store. Clones share the store and the registered formats.

| Method                           | Returns                                                  |
| -------------------------------- | -------------------------------------------------------- |
| `load(id)`                       | The `StoredSession`: its summary record and `turns`.     |
| `list()`                         | The `SessionSummary` of each session.                    |
| `query(&query)`                  | One `SessionPage` of a filtered listing.                 |
| `summary(id)`                    | The summary of one session, or `None`.                   |
| `turns(id)`, `pending_turns(id)` | The stored turns, or only the turns that did not finish. |
| `turn(id, request_id)`           | The stored turn of one request ID, or `None`.            |
| `flush()`                        | Waits for pending writes to the store.                   |

```rust
// Lists the sessions in the store
for summary in store.list().await? {
    println!("{}", summary.record.session_id);
}
```

## JsonlStore

> [!NOTE]
> `JsonlStore` exists only on native targets.

An append-only JSONL journal. The store keeps a rebuildable SQLite offset index beside the log as `<log>.index.sqlite`, and the first open builds it for an existing log.

| Method                                                 | Behavior                                                                                                  |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `JsonlStore::open(path)`, `open_async(path)`           | Opens or creates the journal at `path`.                                                                   |
| `JsonlStore::open_home(home)`, `open_home_async(home)` | Opens `sessions.jsonl` in a home directory.                                                               |
| `compact(&mut self)`                                   | Compacts the journal and returns `CompactionStats`: `before_bytes`, `after_bytes`, and `records_removed`. |
| `close(self)`                                          | Stops the store after its pending jobs.                                                                   |

```rust
use nanocode::JsonlStore;

// Opens the journal without blocking the executor
let journal =
    JsonlStore::open_async("/var/lib/agent/sessions.jsonl").await?;
```

## SqlStore

A `StateStore` over a host that runs SQL transactions. The host implements `Sql`, whose `transaction` method runs a list of `SqlStatement` values in one transaction and returns their rows. `SqlStore` creates its `states`, `owners`, and `records` tables itself.

```rust
use nanocode::durability::{
    Sql, SqlRow, SqlStatement, SqlStore, StoreError, StoreFuture,
};

struct Database;

// Runs the statements of one transaction on the host database
impl Sql for Database {
    fn transaction<'a>(
        &'a self,
        _operation: &'static str,
        statements: &'a [SqlStatement<'a>],
    ) -> StoreFuture<'a, Result<Vec<Vec<SqlRow>>, StoreError>> {
        Box::pin(async move { run_in_transaction(statements).await })
    }
}

let store = nanocode::session_store(SqlStore::new(Database))?;
```

## KeyValueStore

A `StateStore` over a key-value host. The host implements `KeyValue`: `get`, `put_values`, and `compare_and_swap` are required, and `get_values` and `list_keys` have default implementations. `compare_and_swap` must be linearizable, so an eventually consistent store cannot back it.

```rust
use nanocode::durability::KeyValueStore;

// Keeps this application's keys under one prefix
let store = nanocode::session_store(
    KeyValueStore::new(kv).with_prefix("agents/"),
)?;
```

## DurableAgentExt

The trait that attaches a `DurableSession` to an agent builder. `ClaudeBuilder` and `CodexBuilder` implement it, and the runtime calls it for every session when durability is on. Third-party backends implement it next to `DurableBackendRegistration`.

```rust
// The trait as nanocode::DurableAgentExt declares it
pub trait DurableAgentExt: Sized {
    fn durability(
        self,
        state: DurableSession,
    ) -> impl Future<Output = Result<Self, NanocodeError>>;
}
```

## Errors

### durability::Error

`session_store`, `MemoryStore::new`, and the `SessionStore` methods return `nanocode::durability::Error`. `load` returns `Error::SessionNotFound` for an unknown session ID. `Error::Store` wraps a `StoreError` from the underlying store.

```rust
use nanocode::durability::Error;

// Treats an unknown session as a new one
match store.load("missing").await {
    Err(Error::SessionNotFound { .. }) => eprintln!("no such session"),
    other => drop(other?),
}
```
