# Settings

> Settings load and in-memory reload, stale resource refresh, and merge rules for provider retry and related session options.

- Repository: earendil-works/pi
- GitHub: https://github.com/earendil-works/pi
- Human docs: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1
- Complete Markdown: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1/llms-full.txt

## Source Files

- `packages/coding-agent/examples/sdk/10-settings.ts`
- `packages/coding-agent/test/suite/regressions/3616-settings-inmemory-reload.test.ts`
- `packages/coding-agent/test/suite/regressions/2753-reload-stale-resource-settings.test.ts`
- `packages/coding-agent/test/suite/regressions/7572-provider-retry-settings-merge.test.ts`
- `packages/coding-agent/README.md`
- `packages/coding-agent/package.json`

---

---
title: "Settings"
description: "Settings load and in-memory reload, stale resource refresh, and merge rules for provider retry and related session options."
---

`SettingsManager` in `@earendil-works/pi-coding-agent` owns settings load, merge, in-memory overrides, queued persistence, and reload. Sessions receive a manager instance (disk-backed, storage-backed, or pure in-memory) and read options such as compaction, retry, thinking level, images, theme, and prompt filters through that surface.

## Construction surfaces

| Factory | Purpose |
|---------|---------|
| `SettingsManager.create(cwd)` | Load merged global + project settings from disk for a working directory |
| `SettingsManager.inMemory(initial?)` | Seed settings with no file I/O (tests and ephemeral embeds) |
| `SettingsManager.fromStorage(storage)` | Build from an `InMemorySettingsStorage` (or compatible storage) with explicit global/project layers |

Public package entry for embedders:

```ts
import {
  createAgentSession,
  SessionManager,
  SettingsManager,
} from "@earendil-works/pi-coding-agent";
```

Package `piConfig.configDir` is `.pi` (agent config root used with per-agent directories such as `agentDir` in runtime factories).

## Load and merge

`SettingsManager.create(cwd)` loads **global + project** settings into one effective view. `getGlobalSettings()` returns the effective object the manager holds after load and overrides (naming reflects the global store surface used by the manager, not “global-only keys”).

Project and global layers merge at nested objects. For **provider retry**, project values override only the keys they set; sibling global keys remain:

| Layer | Path | Keys in evidence |
|-------|------|------------------|
| Global | `retry.provider` | `timeoutMs`, `maxRetryDelayMs` |
| Project | `retry.provider` | `maxRetries` |

After merge, `getProviderRetrySettings()` flattens the effective provider retry object:

```json
{
  "timeoutMs": 30000,
  "maxRetries": 2,
  "maxRetryDelayMs": 45000
}
```

Storage for layered tests uses `InMemorySettingsStorage.withLock("global" | "project", () => JSON.stringify(...))`, then `SettingsManager.fromStorage(storage)`.

## Overrides, setters, and persistence

### Runtime overrides

`applyOverrides(partial)` patches the live manager without requiring a full replace. Documented example keys:

```ts
settingsManager.applyOverrides({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 5, baseDelayMs: 1000 },
});
```

Pass the manager into session construction:

```ts
const { session } = await createAgentSession({
  settingsManager,
  sessionManager: SessionManager.inMemory(),
});
```

### Immediate memory vs durable write

- **Setters** (for example `setDefaultThinkingLevel`, `setTheme`) update memory immediately and **queue** persistence writes.
- Call **`await settingsManager.flush()`** when the app needs a durability boundary.
- **`drainErrors()`** returns queued I/O failures for the app layer:

```ts
const settingsErrors = settingsManager.drainErrors();
for (const { scope, error } of settingsErrors) {
  console.warn(`Warning (${scope} settings): ${error.message}`);
}
```

Each error entry has `scope` and `error` (`Error` with `.message`).

## In-memory managers and reload

`SettingsManager.inMemory({ ... })` is the no-disk path. **`reload()` must not wipe seeded values.**

Verified invariants:

| Operation | Result |
|-----------|--------|
| `inMemory({...})` then `await reload()` | Initial keys and getters unchanged |
| Same manager under `DefaultResourceLoader.reload()` | Thinking level, image auto-resize, compaction flags preserved |
| `setTheme("dark")` → `flush()` → `reload()` | Theme plus prior in-memory keys all retained |

Example seed and getters:

```ts
const settingsManager = SettingsManager.inMemory({
  defaultThinkingLevel: "high",
  images: { autoResize: false },
  compaction: { enabled: false },
});

await settingsManager.reload();

settingsManager.getDefaultThinkingLevel(); // "high"
settingsManager.getImageAutoResize();      // false
settingsManager.getCompactionEnabled();    // false
settingsManager.getGlobalSettings();       // full effective object
```

`DefaultResourceLoader` accepts `settingsManager` plus resource toggles (`noExtensions`, `noSkills`, `noPromptTemplates`, `noThemes`, `noContextFiles`) so resource reloads can run without reloading those asset classes.

## Disk settings and stale resource refresh

Agent-directory `settings.json` participates in session reload. After startup, writing filters and calling `session.reload()` refreshes both settings and dependent resources.

Example: exclude a prompt template that was already loaded:

```json
{
  "prompts": ["-prompts/test.md"]
}
```

After `await runtime.session.reload()`:

1. `runtime.services.settingsManager.getGlobalSettings().prompts` equals `["-prompts/test.md"]`.
2. Session `promptTemplates` no longer include the excluded template name (`test`).

This is the stale-resource path: reload re-reads settings and re-applies resource filters so the session does not keep templates that settings now exclude.

Interactive mode also exposes a built-in **`/settings`** UI that can temporarily replace the editor; embedders use the same manager APIs rather than that TUI.

## Settings keys in evidence

Keys appear in SDK examples, regressions, or both. This table is the evidenced surface only—not a full schema.

| Key / path | Role |
|------------|------|
| `compaction.enabled` | Compaction on/off |
| `retry.enabled` | Retry feature flag |
| `retry.maxRetries` | Top-level retry count (override example) |
| `retry.baseDelayMs` | Top-level retry base delay (override example) |
| `retry.provider.timeoutMs` | Provider retry timeout |
| `retry.provider.maxRetries` | Provider retry count |
| `retry.provider.maxRetryDelayMs` | Provider max delay between retries |
| `defaultThinkingLevel` | Default thinking level (e.g. `"high"`, `"low"`) |
| `images.autoResize` | Image auto-resize |
| `theme` | Theme id (e.g. `"dark"`) |
| `prompts` | Prompt include/exclude patterns (e.g. `"-prompts/test.md"`) |

### Accessors

| Method | Returns |
|--------|---------|
| `getGlobalSettings()` | Effective settings object |
| `getProviderRetrySettings()` | Flattened `retry.provider` merge result |
| `getDefaultThinkingLevel()` | Thinking level string |
| `getImageAutoResize()` | boolean |
| `getCompactionEnabled()` | boolean |
| `getTheme()` | Theme string |

## Lifecycle diagram

```text
  create(cwd) | inMemory(seed) | fromStorage(storage)
              │
              ▼
     ┌────────────────────┐
     │  SettingsManager   │
     │  effective settings│
     └─────────┬──────────┘
               │
     applyOverrides / setters ──► memory update
               │                    │
               │                    └─ queue write ─► flush() ─► drainErrors()
               │
     createAgentSession({ settingsManager, ... })
               │
               ▼
     session / DefaultResourceLoader / AgentSessionRuntime
               │
               ├── reload()  (in-memory: keep seed + flushed sets)
               └── session.reload()  (disk: re-read settings.json,
                                      refresh prompts / resources)
```

## SDK patterns

### Disk-backed session with overrides

```ts
const cwd = process.cwd();
const settingsManager = SettingsManager.create(cwd);
settingsManager.applyOverrides({
  compaction: { enabled: false },
  retry: { enabled: true, maxRetries: 5, baseDelayMs: 1000 },
});

const { session } = await createAgentSession({
  settingsManager,
  sessionManager: SessionManager.inMemory(),
});
// ...
session.dispose();
```

### Test / no I/O

```ts
const inMemorySettings = SettingsManager.inMemory({
  compaction: { enabled: false },
  retry: { enabled: false },
});

const { session } = await createAgentSession({
  settingsManager: inMemorySettings,
  sessionManager: SessionManager.inMemory(),
});
```

### Durability boundary

```ts
settingsManager.setDefaultThinkingLevel("low");
await settingsManager.flush();
const settingsErrors = settingsManager.drainErrors();
```

Reference implementation: `packages/coding-agent/examples/sdk/10-settings.ts`.

## Failure modes and regression contracts

| Issue | Contract |
|-------|----------|
| Nested project `retry.provider` | Must deep-merge with global provider retry; do not drop sibling global keys |
| In-memory `reload()` | Must keep initial seed; must keep values set and flushed before reload |
| Resource loader reload | Must not reset in-memory settings when reloading other resources |
| Session reload after `settings.json` change | Settings and filtered resources (e.g. prompt templates) must match new disk settings |

<Warning>
Setters only queue writes. Without `flush()`, process exit may miss persistence. Always surface `drainErrors()` at the app boundary after flush or bulk updates.
</Warning>

<Note>
Provider retry lives under `retry.provider` in storage layers and is read as a flat object via `getProviderRetrySettings()`. Top-level `retry` fields used in `applyOverrides` (`enabled`, `maxRetries`, `baseDelayMs`) are a separate override path from the nested provider merge.
</Note>

## Related pages

<CardGroup>
  <Card title="Providers and models" href="/providers-and-models">
    Built-in and dynamic providers, model refresh, and provider-retry messaging.
  </Card>
  <Card title="Compaction" href="/compaction">
    Auto and manual compaction triggers and session interactions.
  </Card>
  <Card title="SDK" href="/sdk">
    Embed pi with custom models, tools, and settings managers.
  </Card>
  <Card title="SDK examples" href="/sdk-examples">
    Copy-paste recipes including settings and full-control setups.
  </Card>
  <Card title="Session runtime" href="/session-runtime">
    AgentSessionRuntime services and embedding without the interactive TUI.
  </Card>
  <Card title="Prompt templates" href="/prompt-templates">
    Prompt templates as session configuration and filter targets.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Retry and network failures and related operational issues.
  </Card>
</CardGroup>
