# Configuration

session-owl is configured via `/owl:settings` (a tabbed modal) and stored in `avtc-pi-session-owl-settings.json`, merged from three layers:

| Scope | Path |
|---|---|
| Global | `~/.pi/agent/avtc-pi-session-owl-settings.json` |
| Project | `<project>/.pi/avtc-pi-session-owl-settings.json` |
| Session | in-memory (survives `/reload`, lost on `/new`) |

All settings are **live-toggleable** — changes take effect at the next trigger, no reload needed. The `PI_SETTINGS_SESSION_OWL` environment variable propagates the merged session+project config to subagents.

## General

| Setting | Type | Default | Description |
|---|---|---|---|
| `enabled` | boolean | `true` | Master switch for session-owl. Off = session-owl stops capturing memory and stops adding its compaction summary (the engine's own compaction and other extensions are unaffected). |
| `subagentMode` | string | `on-compaction` | How session-owl treats subagents. Inherited = the same treatment as the main conversation; On compaction = their memory is captured only when the subagent itself compacts (small subagents that never compact are skipped entirely); Disabled = session-owl is off for subagents. When session-owl is disabled, it is off for subagents as well. |
| `ignoreConflicts` | boolean | `false` | Register even when a conflicting compaction extension is detected (for deliberate co-runs, e.g. benchmarking). |
| `defaultModel` | model | `null` | One model for all components. `null`/unset uses the current session model. Per-component models override this. On the Claude port the default is `haiku`. |
| `defaultThinkingLevel` | thinking-level | `null` | Thinking level for all components. `null`/Session default = use the session's thinking level; `off` = no thinking. Per-component levels override this. |
| `renderMode` | string | `observations-root` | What session-owl injects after compaction (the recall commands read the same view). `selected-root` = a focused, task-relevant view the Selector builds; `observations-root` = the root view of the memory graph. |
| `observerMode` | string | `on-threshold` | When the Observer captures memory. `on-threshold` = throughout the session, after a turn once enough new text accumulates; `on-compaction` = all at once, at compaction time only. |
| `builderMode` | string | `each-N-observations` | When the Builder runs: `on-compaction` (cheapest), `each-N-observations`, `on-session-context-threshold`, or `on-root-view-threshold`. |
| `selectorMode` | string | `on-compaction` | When the Selector runs (only if `renderMode=selected-root`): `on-compaction` or `on-session-context-threshold`. |
| `rootViewTargetNodes` | number | `null` | Advice for the Builder and Selector on how many roots to keep. A number = aim for about that many roots — a soft target, the token budget wins. No target = no advice; the agent shapes the root view on its own. |
| `rootViewStrategy` | string | `balanced` | How the Builder and Selector organize roots. Balanced = organize arrivals and consolidate related; By task = one root per task, in order; By category = roots by kind (requests, decisions, code understanding, work state, pitfalls, environment — open list); By recency = granular recent, compact older; By importance = dedicated roots for crit/high; By topic = one root per distinct subject. |
| `commandResultCap` | number | `50` | Max items an owl command shows before a `... +N more` footer. `null` = show all. |
| `findTimeoutMs` | duration | `30000` | Max duration a `find`/`owl_recall` search may run before it is stopped. |
| `llmCallTimeoutMs` | duration | `1200000` | Aborts any Observer, Builder, or Selector LLM call that runs longer than this. `null` = no limit. |
| `toolResultTokenBudget` | number | `6000` | Max size (in tokens) of a single `cat`/`find`/`ls`/`owl_recall` result. A larger result shows fewer items or less detail (each item stays whole); reading one observation in full is never cut off. |
| `debugDumpLimit` | number | `0` | Maximum per-stage dump files kept under `<harnessDir>/session-owl/dumps/<project>/` (0 = no dumps). Each dump captures what an Observer/Builder/Selector LLM run saw (system prompt, tools, per-call input) and produced (thinking, text, tool calls, results), in the tagged memory format. |

## Observer

| Setting | Type | Default | Description |
|---|---|---|---|
| `observerModel` | model | `null` | Overrides `defaultModel` for the Observer. |
| `observerThresholdTokens` | number | `4000` | `on-threshold` gate: emit a batch when accumulated unobserved tokens reach this. |
| `observerIncludeThinking` | boolean | `true` | Include non-redacted thinking blocks in the chunks the Observer reads. |
| `observerToolBlockCapTokens` | number | `null` | When capturing tool calls and results, trim each block to this many tokens (keeping the start and end). `null` = keep the whole block. |
| `observerMaxTokens` | number | `16384` | Maximum output tokens per Observer LLM call. |
| `observerThinkingLevel` | thinking-level | `null` | Thinking level for the Observer. `null`/Inherit default = use `defaultThinkingLevel`; `off` = no thinking. |

## Builder

| Setting | Type | Default | Description |
|---|---|---|---|
| `builderModel` | model | `null` | Overrides `defaultModel` for the Builder. |
| `builderEveryNObservations` | number | `40` | N for the `each-N-observations` `builderMode`. |
| `builderSessionContextThresholdTokens` | number | `200000` | Threshold for the `on-session-context-threshold` `builderMode`. |
| `builderRootViewThreshold` | number | `40000` | Target size (in tokens) for the root view of the memory graph. Also: the trigger for `on-root-view-threshold` mode, and the budget `builderSkipWithinBudget` checks. |
| `builderSkipWithinBudget` | boolean | `true` | After the Observer's compaction catch-up only: skip the final Builder run when the root view is within budget. Mid-catch-up and background Builder runs are never skipped. With `each-N-observations`, also run the Builder when the root view reaches the threshold — it keeps the budget this skip relies on. Off = the Builder always runs at least once. |
| `maxBuilderPasses` | number | `3` | Max passes per Builder run. |
| `builderMaxTokens` | number | `32768` | Maximum output tokens per Builder LLM call. |
| `builderThinkingLevel` | thinking-level | `null` | Thinking level for the Builder. `null`/Inherit default = use `defaultThinkingLevel`; `off` = no thinking. |

## Selector

(Only meaningful when `renderMode=selected-root`.)

| Setting | Type | Default | Description |
|---|---|---|---|
| `selectorModel` | model | `null` | Overrides `defaultModel` for the Selector. |
| `selectorSessionContextThresholdTokens` | number | `200000` | Threshold for the `on-session-context-threshold` `selectorMode`. |
| `selectorRootViewThreshold` | number | `20000` | Target size (in tokens) for the root view of the selected tree. |
| `maxSelectorPasses` | number | `3` | Max passes per Selector run. |
| `selectorMaxTokens` | number | `65536` | Maximum output tokens per Selector LLM call. |
| `selectorThinkingLevel` | thinking-level | `null` | Thinking level for the Selector. `null`/Inherit default = use `defaultThinkingLevel`; `off` = no thinking. |

## Models

Each component (Observer, Builder, Selector) resolves its model by the first of:

1. its **component-specific** model (`observerModel` / `builderModel` / `selectorModel`), if set;
2. else **`defaultModel`**, if set;
3. else the **current session model**.

So set a per-component model to tune individually (e.g. a larger model for the Builder), set `defaultModel` to apply one model everywhere, or leave both unset to follow the session.

## Thinking levels

Each component resolves its thinking level by the first of:

1. its **component-specific** level (`observerThinkingLevel` / `builderThinkingLevel` / `selectorThinkingLevel`), if not *Inherit default*;
2. else **`defaultThinkingLevel`**, if not *Session default*;
3. else the **session's thinking level**.

`off` at any tier disables thinking for that component (the reasoning field is omitted from the LLM call). Leave all unset to follow the session, or set `defaultThinkingLevel` once to apply a level everywhere.

## Settings UI & environment

- **`/owl:settings`** opens the tabbed modal (General · Observer · Builder · Selector). Changes apply live.
- **`PI_SETTINGS_SESSION_OWL`** — the merged session+project config is serialized into this env var so subagents inherit it.
- **One active session per session file** — sharing a single session's storage between multiple active sessions is unsupported: observation and node ids are sequential per session, so concurrent writes can corrupt, duplicate, or mis-merge records during builder/selector stages. Fork or branch instead — each gets its own session file and storage.


## Claude Code port

The Claude Code port (the repo's `claude/` directory) uses the same settings schema, stored under Claude's own paths:

| Scope | Path |
|---|---|
| Global | `~/.claude/avtc-session-owl-settings.json` |
| Project | `<project>/.claude/avtc-session-owl-settings.json` |
| Session | in-memory (bootstrapped from the `CLAUDE_SETTINGS_SESSION_OWL` environment variable) |

Everything else in this document applies unchanged. The knobs below exist only in the port:

- **`keepRecentTokens`** (number, default `20000`) — tokens of recent conversation kept verbatim alongside the memory summary when session-owl answers a compaction. Answered at the compaction seam; editable from the settings pane.
- **`mirrorRetentionDays`** (number, default `null` = keep forever — full-text recall never expires) — a number enables the startup retention sweep (expired sessions and sessions whose transcript is gone are truly deleted); the follow-cleanup mode tracks Claude Code's own `cleanupPeriodDays` instead.
- **`keptTailAggregatedThinking`** (boolean, default `true`) — folds the kept tail's thinking blocks into the memory summary as an attributed-reasoning section named "## Reasoning from earlier turns (attributed)"; `off` keeps the summary text only (this platform's compaction has no thinking field of its own).
- **`injectedSummaryRow`** (boolean, default `false`) — after a compaction, echoes the injected memory summary as one visible assistant row above the compaction command. Invisible to the agent.

The retention sweep runs as a Node helper at session start on CLI surfaces; non-CLI surfaces defer it to the next CLI run. Dry-run by default: `node claude/helpers/prune.mjs --days N [--apply]`.
