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

# Persist Conversations with the Runtime Thread Store

> Use ChappieSDKRuntime to save conversation threads to disk so they survive app launches, with configurable retention and file-protection policies.

The base `ChappieSDK` product handles auth, transport, streaming, and host-tool execution. When you need conversations to survive app launches — so users can return to a thread they started yesterday — add the optional `ChappieSDKRuntime` product. It layers durable thread storage and normalized turn events on top of `ChappieClient`, keeping the base package lean for apps that have no persistence requirements.

## Add ChappieSDKRuntime to Your Target

`ChappieSDKRuntime` is a separate product in the same Swift package. Link it only to app targets that need persistent storage; never add it to framework or library targets that might be distributed independently.

<Steps>
  <Step title="Open Package Dependencies">
    In Xcode, go to **File → Add Package Dependencies…** and enter the Chappie SDK repository URL:

    ```text theme={null}
    https://github.com/b-nnett/chappie-sdk.git
    ```
  </Step>

  <Step title="Add ChappieSDKRuntime to your app target">
    When Xcode shows the product list, tick **ChappieSDKRuntime** in addition to **ChappieSDK**. Both products must be linked to the same app target.

    In a `Package.swift` manifest, the dependency looks like this:

    ```swift theme={null}
    .package(url: "https://github.com/b-nnett/chappie-sdk.git", from: "0.1.0")
    ```

    Then add both products to your target's `dependencies` array:

    ```swift theme={null}
    .product(name: "ChappieSDK", package: "chappie-sdk"),
    .product(name: "ChappieSDKRuntime", package: "chappie-sdk"),
    ```
  </Step>

  <Step title="Import both modules">
    At the top of any file that creates or uses a thread store, import both modules:

    ```swift theme={null}
    import ChappieSDK
    import ChappieSDKRuntime
    ```
  </Step>
</Steps>

## Basic Setup

After importing both modules, create a store, attach it to a runtime, and start sending messages. The runtime records every turn automatically — no extra calls required.

```swift theme={null}
import ChappieSDK
import ChappieSDKRuntime

let store = ChappieFileThreadStore()
let runtime = Chappie.client().runtime(store: store)

let reply = try await runtime.send("Summarize this project.")
let threads = try await runtime.listThreads()
let usage = try await store.usage()
```

`runtime.send(_:)` behaves like `client.send(_:)` but persists the full turn to the store before returning. `listThreads()` returns the active (non-archived) threads sorted by recency. `store.usage()` reports how many bytes the store is currently using against the configured cap.

## Choosing a Store

Chappie ships two store implementations. Pick the one that matches your app's needs.

<CardGroup cols={2}>
  <Card title="ChappieFileThreadStore" icon="hard-drive">
    Persists complete thread JSON to the app's support directory. Transcripts survive app launches, device restarts, and upgrades. Use this for any production conversation surface.
  </Card>

  <Card title="ChappieInMemoryThreadStore" icon="flask">
    Keeps threads in memory only. Threads vanish when the process exits. Use this for unit tests, UI tests, and short-lived sessions where disk I/O would add noise.
  </Card>
</CardGroup>

```swift theme={null}
// Production — survives launches
let store = ChappieFileThreadStore()

// Tests and short-lived sessions
let memoryStore = ChappieInMemoryThreadStore()
```

<Note>
  Both stores apply identical retention defaults and expose the same `ChappieThreadStore` protocol. You can swap between them or provide a fully custom implementation without changing any other code.
</Note>

## Default Retention Policy

Both stores apply the same retention defaults out of the box. You do not need to configure anything for the defaults to take effect.

| Parameter                      | Default value     |
| ------------------------------ | ----------------- |
| Total stored transcript cap    | 256 MiB           |
| Archive inactive threads after | 30 days           |
| Delete archived threads after  | A further 60 days |

## Customizing Retention

Pass a `ChappieThreadStoreRetentionPolicy` to tighten or relax the defaults. All time values are in seconds.

```swift theme={null}
let store = ChappieFileThreadStore(
    retentionPolicy: ChappieThreadStoreRetentionPolicy(
        maximumStoredTranscriptBytes: 512 * 1024 * 1024,
        archiveInactiveAfter: 14 * 24 * 60 * 60,   // 14 days
        deleteArchivedAfter: 45 * 24 * 60 * 60       // 45 days
    )
)
```

To disable all size and time limits entirely, use the `.unlimited` preset:

```swift theme={null}
let unlimitedStore = ChappieFileThreadStore(retentionPolicy: .unlimited)
```

<Warning>
  `.unlimited` stores every thread indefinitely and applies no size cap. Use it only during development or when your app manages its own eviction strategy. Leaving it enabled in production can exhaust the user's device storage over time.
</Warning>

## File Protection on iOS

`ChappieFileThreadStore` defaults to `NSFileProtectionComplete` on iOS. This means transcript files are encrypted at rest and inaccessible while the device is locked. That is the right default for most apps.

If your app needs to read or write transcripts while the device is locked — for example, a background extension that logs assistant turns during a background task — switch to `.completeUntilFirstUserAuthentication`:

```swift theme={null}
let backgroundStore = ChappieFileThreadStore(
    storagePolicy: ChappieFileThreadStoreStoragePolicy(
        fileProtection: .completeUntilFirstUserAuthentication
    )
)
```

<Tip>
  Only loosen file protection when your app has a genuine background-access requirement. Keep the default `NSFileProtectionComplete` for any store that is only ever accessed from the foreground.
</Tip>

On macOS, Chappie relies on the app sandbox and platform filesystem protections rather than a file-protection attribute.

## Archived Threads

Threads that have been inactive for longer than `archiveInactiveAfter` move to an archived state. Archived threads are hidden from `listThreads()` by default so your chat list UI stays clean.

You can still access archived threads in two ways:

<CodeGroup>
  ```swift List including archived theme={null}
  // Returns both active and archived threads
  let allThreads = try await runtime.listThreads(includeArchived: true)
  ```

  ```swift Resume by thread ID theme={null}
  // Directly resume an archived thread — no need to list first
  let reply = try await runtime.resume(threadID: knownThreadID, sending: "Continue where we left off.")
  ```
</CodeGroup>

Resuming an archived thread by ID reactivates it automatically, resetting its inactivity timer.

## Backup Exclusion

Transcript directories and JSON files are excluded from iCloud backups by default. This keeps potentially sensitive conversation history off iCloud Drive and avoids bloating the user's backup quota. You cannot opt back into iCloud backup through the public `ChappieFileThreadStoreStoragePolicy` API.

## Privacy and Transcript Content

<Warning>
  `ChappieFileThreadStore` stores complete thread JSON to disk. That can include **user text, assistant text, tool call arguments, tool outputs, raw payloads, metadata, errors, and compaction summaries**. The store does not redact any of this content automatically.

  If your app must not persist certain fields — for example, authentication tokens passed as tool arguments, or personally identifiable information in user messages — redact that data before it reaches the store, or implement a custom type that conforms to `ChappieThreadStore` and applies your redaction logic.
</Warning>

A custom store that conforms to `ChappieThreadStore` is the right extension point when you need field-level control over what gets written to disk. The runtime calls the same protocol methods regardless of which concrete store you provide.
