# Configuration Reference `magic-context.jsonc` has shared top-level settings plus harness-specific model execution blocks. The schema is shared by the OpenCode plugin and the Pi-compatible extension used on both Pi and OMP. ### Configuration locations Magic Context reads config from one shared CortexKit location across OpenCode, Pi, and OMP (project overrides user): | Path | Scope | |---|---| | `/.cortexkit/magic-context.jsonc` | Project | | `~/.config/cortexkit/magic-context.jsonc` | User-wide defaults | Project config always merges on top of user config. The unified setup wizard (`npx @cortexkit/magic-context@latest setup`) writes the user-level file with sensible defaults. > **Migrating from an earlier version?** Config used to live in per-harness paths (`~/.config/opencode/`, `~/.pi/agent/`, `/.opencode/`, `/.pi/`, or the project root). On first run after upgrading, Magic Context moves your existing config to the CortexKit location automatically and leaves a `.MOVED_READPLEASE` breadcrumb (preserving your original settings) at each old path. If two old locations held *different* settings it won't guess — it leaves both in place and warns you to consolidate by hand. ### Changing config without a restart Keys listed below apply from the next historian or dreamer run (or dream-timer tick); a run in progress keeps its original inputs. All other keys require a host restart. On a malformed edit, the last good config remains active and `/ctx-status` reports the error. The live mark in the [JSON schema](assets/magic-context.schema.json) is authoritative; this list is checked against it. - `cache_ttl` - `commit_cluster_trigger.enabled` - `commit_cluster_trigger.min_clusters` - `dreamer.disable` - `dreamer.maxTokens` - `dreamer.omp.fallback_models` - `dreamer.omp.model` - `dreamer.omp.tasks` - `dreamer.omp.thinking_level` - `dreamer.opencode.fallback_models` - `dreamer.opencode.model` - `dreamer.opencode.tasks` - `dreamer.opencode.variant` - `dreamer.pi.fallback_models` - `dreamer.pi.model` - `dreamer.pi.tasks` - `dreamer.pi.thinking_level` - `dreamer.tasks.classify-memories.schedule` - `dreamer.tasks.compress-cues.schedule` - `dreamer.tasks.curate.schedule` - `dreamer.tasks.evaluate-smart-notes.schedule` - `dreamer.tasks.maintain-docs.max_tokens` (default 12000; combined proposed docs budget) - `dreamer.tasks.maintain-docs.schedule` - `dreamer.tasks.map-memories.schedule` - `dreamer.tasks.promote-primers.promotion_threshold` - `dreamer.tasks.promote-primers.schedule` - `dreamer.tasks.refresh-primers.schedule` - `dreamer.tasks.retrospective.recency_days` - `dreamer.tasks.retrospective.schedule` - `dreamer.tasks.review-user-memories.promotion_threshold` - `dreamer.tasks.review-user-memories.schedule` - `dreamer.tasks.verify-broad.schedule` - `dreamer.tasks.verify.schedule` - `historian.expand_tools` - `historian.maxTokens` - `historian.omp.fallback_models` - `historian.omp.model` - `historian.omp.thinking_level` - `historian.opencode.fallback_models` - `historian.opencode.model` - `historian.opencode.variant` - `historian.pi.fallback_models` - `historian.pi.model` - `historian.pi.thinking_level` - `historian.two_pass` - `historian_timeout_ms` - `memory.auto_promote` - `memory.git_commit_indexing.enabled` - `memory.git_commit_indexing.max_commits` - `memory.git_commit_indexing.since_days` - `toast_duration_ms` `dreamer..tasks` is a map: each task's model, fallback chain, reasoning qualifier and timeout inherit this live behavior. Project-tier restrictions still apply; for example, historian model selection remains user-tier only. ### Per-harness model migration Historian and dreamer model execution live in independent `opencode`, `pi`, and `omp` blocks. On the first user-config read that finds the former flat model fields, Magic Context writes one exact-byte recovery copy at `.pre-per-harness.bak` before rewriting the config. **Magic Context retains `.pre-per-harness.bak` indefinitely and never garbage-collects it. You may delete it manually after you no longer need the recovery copy.** Only model-resolution fields move. The migration inventory is explicit: | Scope | Retained at its current level | Moved to the matching harness block | |---|---|---| | `historian` | `temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `maxSteps`, `permission`, `maxTokens`, `two_pass`, `disallowed_tools` | `model`, `fallback_models`, `variant`, `thinking_level` | | `dreamer` | `temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `maxSteps`, `permission`, `maxTokens`, `inject_docs` | `model`, `fallback_models`, `variant`, `thinking_level` | | `dreamer.tasks.` | `schedule`, `promotion_threshold` | `model`, `fallback_models`, `variant`, `thinking_level`, `timeout_minutes` | There is no catch-all migration rule: fields not listed in this table are not moved by the per-harness migration. Existing users need no additional OMP migration: when an `omp` block is absent, OMP resolves the matching `pi` block. ### Per-repository model profiles Use user-owned `profiles` when your work repositories and personal repositories need different hidden-agent models without duplicating durable configuration. This is kagbodji's use case: keep the personal default in user config, then let each work repository select the work model set. ```jsonc // ~/.config/cortexkit/magic-context.jsonc { "profile": "personal", "profiles": { "personal": { "historian": { "opencode": { "model": "anthropic/claude-sonnet-4-6" }, "pi": { "model": "github-copilot/claude-sonnet-4-6" }, "omp": { "model": "opencode/claude-sonnet-4-6", "thinking_level": "auto" } }, }, "work": { "historian": { "opencode": { "model": "openai/gpt-5.2-codex" }, "pi": { "model": "github-copilot/gpt-5.2-codex" }, "omp": { "model": "opencode/gpt-5.2-codex", "thinking_level": "high" } }, "dreamer": { "opencode": { "model": "openai/gpt-5.2-codex" } }, } } } ``` A work repository then needs only a selection key: ```jsonc // /.cortexkit/magic-context.jsonc { "profile": "work" } ``` Resolution is `user base → selected user profile → project config`; a project selection wins over the user default. Profile overlays deep-merge, so a profile can override one harness model while base fallback chains and other settings stay intact. Profiles are defined only in user config: a project may select a known name, but project-supplied `profiles` content is ignored with a warning. An unknown selected name also warns and uses the base configuration with no profile rather than disabling Magic Context. Profiles admit only hidden-agent model selection (`historian.opencode` / `historian.pi` / `historian.omp`, `dreamer.opencode` / `dreamer.pi` / `dreamer.omp`); embeddings, prompts, storage, compaction, memory gates, thresholds, and other durable behavior stay outside them. ### Cross-harness scoping Both plugins write to the same SQLite database at `~/.local/share/cortexkit/magic-context/context.db`. Tables are scoped by: - `harness` column (`'opencode'`, `'pi'`, or `'omp'`) for **session-scoped** data — OMP has its own first-class discriminator - `project_path` (resolved git root) for **project-scoped** data — memories, embeddings, dreamer runs, key-file pins, smart notes Project memories therefore flow across OpenCode, Pi, and OMP, while per-session state remains scoped to the OpenCode, Pi, or OMP runtime. > **OpenCode 2 hidden runs:** OpenCode 2.0.22 or newer is required. Each historian and Dreamer run gets a fresh child session on the resolved `historian.opencode` / `dreamer.opencode` model chain, preserving the calibrated prompt and keeping the user's model and token accounting untouched. Children are parented to the user's session when one exists and removed through the native session API when their run ends. On upgrade, recorded legacy children are removed in bounded, resumable boot batches. For semantic search to work cross-harness, every host resolves embedding config per project identity on each retrieval path. Keep the effective `embedding` block consistent across OpenCode, Pi, and OMP for the same project. ### Trusted-group shared storage By default, Magic Context enforces owner-only `0700` storage directories and `0600` storage files. For a deliberate Unix deployment where trusted users share one store and an operator manages permissions externally, set this **in user config only**: ```jsonc { "storage": { "enforce_private_permissions": false } } ``` For example, the operator may maintain the storage directory as `2770` and `context.db`, `context.db-wal`, and `context.db-shm` as `0660` for a trusted Unix group. With this setting disabled, Magic Context never re-tightens directory, database, WAL/SHM, model-cache, or RPC-file permissions; missing paths are still created using the operator's umask. Every group member that can read this store can read **all** stored session content and memories, so use this only for a deliberately trusted group. On Windows, POSIX modes are not meaningful, so the setting has no effect. ### JSON Schema Add `$schema` to your config file for autocomplete and validation in VS Code and other editors: ```jsonc { "$schema": "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json" } ``` Both setup wizards add this automatically. ### Prompt surface presets `prompt_surface` selects the built-in guidance/tool surface preset. The implicit default is `full`: ```jsonc { "prompt_surface": { "default": "full", "models": { "anthropic/claude-sonnet-4-6": "light", "openai/*": "light" } } } ``` Model keys use the same progressive, case-sensitive lookup walk as `cache_ttl`: exact `provider/model` keys, less-specific model variants, then the literal `provider/*` wildcard and `default`. The first slash separates the provider; additional slashes remain part of the model ID. Missing provider/model components fall back to `default`. > **OpenCode 1.x, Pi, and OMP limitation:** per-model routing in `models` applies to the guidance block only. Tool descriptions and parameter descriptions are registered once per process by those hosts, so they always follow `prompt_surface.default`. OpenCode 2 rewrites both kinds of tool prose on every `context` pass from the same resolver, keyed on the draft model. The built-in guidance treats context as a desk: tagged items stay on the desk while useful, `ctx_reduce` stamps spent items for later clearing, cleared work remains recoverable from the archive, memory is the pinboard, and notes are the tray for matters that belong later. `guidance_override_path` and `tool_descriptions` are user-level only. A project may select `default` and `models`, but repository-supplied guidance files and tool-description text are stripped with a warning. ```jsonc { "prompt_surface": { "guidance_override_path": "./guidance/primary.md", "tool_descriptions": { "ctx_search": "Search the project's durable context." } } } ``` A guidance override must be a readable complete `## Magic Context` section with exactly one marker; it does not replace shared subagent or runtime clauses. Tool overrides change only top-level descriptions and must use known tool IDs. These override fields are intentionally not available to project configuration. ### Doctor If something isn't working, run the unified doctor to auto-detect installed harnesses and fix common issues: ```bash # Auto-detect installed harnesses; if multiple are present, pick or prompt npx @cortexkit/magic-context@latest doctor # Target a specific harness explicitly npx @cortexkit/magic-context@latest doctor --harness opencode npx @cortexkit/magic-context@latest doctor --harness pi npx @cortexkit/magic-context@latest doctor --harness omp # Read-only inventory of reusable/retired OpenCode 2 hidden-run roots npx @cortexkit/magic-context@latest doctor list-hidden-sessions ``` The OpenCode doctor checks: installation, CLI version vs npm latest, plugin registration (preserves local dev paths), `magic-context.jsonc` parses + loads through the schema, conflicts (compaction, DCP, OMO hooks), TUI sidebar configuration, embedding endpoint, shared-DB existence + `PRAGMA integrity_check` + row counts, plugin npm cache, and historian debug dumps. The Pi doctor checks: Pi binary + version (requires `>= 0.71.0`), CLI version vs npm latest, settings registration, config validity, embedding endpoint reachability, shared-DB integrity, stale Pi extension caches, and historian debug dumps. The OMP doctor checks the OMP version, effective plugin enable state, `PI_CODING_AGENT_DIR`/profile/XDG path agreement, native compaction and automatic-memory conflicts, config validity, and shared DB integrity. `--force` installs/enables the plugin and repairs conflicting OMP settings. All doctors report `PASS X / WARN Y / FAIL Z` summary counts. Use `--force` for safe repairs and `--issue` to produce a sanitized issue report. ### SQLite backend Magic Context uses the runtime's built-in SQLite: `bun:sqlite` under Bun (OpenCode CLI/TUI) and `node:sqlite` under Node and Electron (Pi, OpenCode Desktop — Electron 41 embeds Node 24.14.1, which ships `node:sqlite` flag-free). There is no native module to install, no per-ABI prebuild, and nothing downloaded at runtime — the store works offline on first launch on every platform. --- ## Cache Awareness LLM providers cache conversation prefixes server-side. The cache window depends on your provider and subscription tier — Claude Pro offers 5 minutes, Max offers 1 hour, and pricing for cached vs. uncached tokens differs between API and subscription usage. Magic Context defers all mutations until the cached prefix expires. `cache_ttl` is how long Magic Context *assumes* a provider's cached prefix stays valid — it is MC's own deferral gate, not a control over the provider's cache. It does not change the provider's actual cache lifetime. The generic fallback is `"5m"`. GPT-5.6 and later (including every `gpt-6*` model) instead default to **30m**, including through `openai/`, `openai-codex/`, `openrouter/openai/`, and `azure/` prefixes. [OpenAI documents](https://developers.openai.com/api/docs/guides/prompt-caching) at least 30 minutes since the latest write or reuse (see “Cache lifetime” and “Summary of model differences”). Earlier models retain the generic fallback. You can tune it: ```jsonc { "cache_ttl": "5m" } ``` Per-model overrides for mixed-model workflows: ```jsonc { "cache_ttl": { "default": "5m", "anthropic/claude-opus-4-6": "60m", "anthropic/*": "never" } } ``` Keys are matched from most to least specific: the exact `provider/model`, the bare model ID, progressively shorter dash-prefixes of the model ID (`claude-opus-4-6` also matches a `claude-opus-4` entry), then the provider wildcard `provider/*`, then `default`. A more specific entry always wins over a wildcard, so the example above keeps `60m` for Opus 4.6 and applies `never` to every other Anthropic model. Harness provider aliases resolve to the canonical name first, so one entry covers the same model on OpenCode, Pi and OMP. Precedence: explicit per-model entry → built-in known-model lifetime → object `default` → `"5m"`. For the global string form, unset or `"5m"` means defaults (so GPT-6 gets 30m); any other string, such as `"10m"`, is an explicit policy and wins over built-ins. To force 5m on GPT-6, use a per-model entry. The policy is frozen per session and survives restarts; model switches resolve against the frozen policy rather than live config. `/ctx-status` shows the effective TTL and its source. Supported formats: `"30s"`, `"5m"`, `"1h"`. **`"never"` sentinel:** set `cache_ttl` to `"never"` to mean Magic Context *never assumes* the cached prefix expires — it disables the idle-TTL heuristic entirely. Both consumers — the scheduler (which converts defer passes to execute after TTL expiry) and the HARD-fold trigger (which folds m[1] into m[0] on a "free" prefix rebuild) — no longer act on idle time. Use this on lanes kept warm by an external keepwarm mechanism (prewarm proxies, dedicated cache-keep tools) where the idle heuristic false-positives and causes paid full-prefix cache-writes. After a genuine cold start (e.g. the keepwarm process died), MC won't detect the free-fold window on that lane — mutations then apply only at the execute threshold. `"never"` only changes MC's assumption; it does not extend the provider's cache lifetime. Provider-side extended TTL is a separate request-level concern (`cache_control: { ttl: "1h" }` in the request body). Higher-tier models with longer cache windows benefit from a longer TTL. Setting it too low wastes cache hits. Setting it too high delays reduction on long sessions. --- ## Core | Option | Type | Default | Description | |--------|------|---------|-------------| | `enabled` | `boolean` | `true` | Master toggle. | | `allow_home_project` | `boolean` | `false` | User-config-only opt-in for a Magic Context session launched from exactly `$HOME`. See below. | | `auto_update` | `boolean` | `true` | User-config-only plugin self-update toggle; project configs cannot disable it. | | `fail_closed_blocking` | `boolean` | `true` | User-config-only. When Magic Context cannot operate (schema fence mismatch, storage open/migration failure), block the primary-session prompt with a loud recovery error instead of silently degrading to native compaction. Set `false` only to restore the old degrade-silently behavior (not recommended). Project configs cannot set this. | | `language` | `string` | unset | User-config-only output language for Magic Context generated prose and primary guidance, as a 2-letter ISO 639-1 code, for example `"tr"`, `"es"`, or `"pt"`. Structural tokens stay in English. | | `cache_ttl` | `string` or `object` | `"5m"` | Time after a response before applying pending ops. String or per-model map. | | `output_reserve` | `number` or `object` | automatic | User-config-only output-token reservation override. `0` disables reservation; supports per-model maps. See below. | | `protected_tokens` | absolute integer (4,000–1,000,000) | derived | Token floor protected from automatic reclaim. This is a token count, not a tag count — values below 4,000 are rejected with a pointer message. When omitted, derives as `clamp(round(0.05 × usableSoft), min(16,000, round(0.08 × usableSoft)), 64,000)`. Project config may only raise the resolved user-or-derived floor. | | `toast_duration_ms` | `number` (0–60000) | `5000` | TUI toast lifetime for Magic Context notifications in milliseconds. Increase this if toasts disappear too quickly, or set to `0` to disable Magic Context toasts entirely. | | `execute_threshold_percentage` | `number` (20–90) or `object` | `65` | Context usage that forces queued ops to execute. Capped at 90% of the output-reserved safe window, leaving about 10% for in-turn input growth. Supports per-model maps. | | `execute_threshold_tokens` | `object` (per-model map) | — | **Optional absolute-tokens variant of `execute_threshold_percentage`.** Per-model map (e.g. `{ "default": 150000, "github-copilot/gpt-5.2-codex": 40000 }`). When set for a model, overrides the percentage-based threshold for that model. Clamped to `90% × context_limit` with a warn log. Requires a resolvable context limit — falls through to percentage if unavailable. See below. | | `clear_reasoning_age` | `number` | `50` | Clear thinking/reasoning blocks older than N tags. | | `historian_timeout_ms` | `number` | `600000` | Timeout per historian call (ms). | | `history_budget_percentage` | `number` (0.05–0.5) | `0.15` | Fraction of usable context (`context_limit × execute_threshold`) reserved for the history block. Triggers compression when exceeded. | | `compaction.enabled` | `boolean` | `true` | When `false`, use compaction-off mode: keep Magic Context's knowledge layer and let native compaction (or nothing) own the context window. Boot-resolved; restart after changing it. See below. | | `commit_cluster_trigger` | `object` | See below | Controls the commit-cluster historian trigger. | | `system_prompt_injection` | `object` | See below | Controls whether and where Magic Context augments the system prompt; lets you opt specific agents out. | | `keep_subagents` | `boolean` | `false` | Debug option: keep every settled Magic Context child session instead of deleting it after success: historian, all Dreamer tasks, smart-note evaluation and compilation, user-memory review, and memory migration. Kept Dreamer children can contain memory-pool text and user messages from other sessions of the same operator; their full transcript stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect. On OpenCode 2.0.22 or newer, every run uses a fresh child, parented when there is a user session. With this option on, settled children and historian children remain inspectable; unsettled Dreamer children are removed. With it off, each run's child is removed through the native session API, whether or not the host runs as a service. | | `todowrite` | `object` | See below | **Pi only.** Controls Magic Context's built-in `todowrite` tool and persistent task overlay. OpenCode has its own built-in `todowrite`, so this setting has no effect there. | | `sqlite` | `object` | See below | Per-connection SQLite tuning for Magic Context's own `context.db`. | | `storage.enforce_private_permissions` | `boolean` | `true` | User-config-only. By default, TypeScript mode creates/re-tightens 0700 directories and 0600 files. `false` delegates TypeScript-mode permissions to the operator; Rust transform mode always keeps the shared data directory owner-only (0700), regardless of this setting. | `protected_tags` is deprecated and ignored. Remove it and use `protected_tokens` when an explicit absolute floor is needed. ### `fail_closed_blocking` When Magic Context is enabled but cannot open its shared database — most often a **schema fence** (another harness upgraded `context.db` past this build) or a hard storage/migration failure — the default is to **block the primary turn loudly** with both version numbers (when known) and: ```bash npx @cortexkit/magic-context@latest doctor ``` rather than unregistering hooks and letting OpenCode/Pi/OMP native compaction run with no user-visible signal. Transient `SQLITE_BUSY` / `SQLITE_LOCKED` still pass through. Internal OpenCode agents (`title` / `summary` / `compaction`), Magic Context hidden children, and Pi subagent processes are not blocked. A healed storage open is re-probed periodically so service can resume without restart. Set `fail_closed_blocking: false` in **user config only** to restore silent degrade (not recommended). Project configs cannot set this field. ### `language` Set `language` in your **user config** when you want Magic Context generated prose to consistently use a language instead of relying on model auto-mirroring. The value is a 2-letter ISO 639-1 code (for example `tr`, `es`, `de`, `ja`, `pt`): ```jsonc { "language": "tr" } ``` This affects historian summaries, dreamer content, and the Magic Context guidance block. It does not translate schemas, XML tags, memory category names, code identifiers, file paths, commands, logs, or quoted text. Project configs cannot set this field for security, since it is injected into hidden-agent system prompts. Changing `language` does not rewrite existing compartments or memories. A project can temporarily have a mixed-language pool after a mid-project switch; older stored entries keep their original language until they are naturally rewritten or superseded. ### `allow_home_project` Set this in **user config only** to keep Magic Context memory for global troubleshooting or chat sessions that intentionally run from exactly your home directory: ```jsonc { "allow_home_project": true } ``` The default is `false`, so a home-directory session continues to run without a Magic Context project identity. Project-level config cannot enable this setting. The opt-in is deliberately narrow: Magic Context uses the deterministic `dir:` identity for the **canonical** `$HOME` path, and only an exact `$HOME` session can use it. A directory below `$HOME` always resolves as its own project; it never resolves into the home project by containment. The home identity is excluded from project-registry seed exports and cannot be added to a workspace, so it cannot turn a broad home directory into a fleet registry seed or a workspace-wide memory pool. This is also the recovery path for home memories created before the home-session gate: the identity is derived from the same canonical-path MD5 prefix, so setting the flag reconnects that existing `dir:` memory pool without a migration. If you leave the flag off after setup has disabled OpenCode native compaction (`compaction.auto=false`), home sessions have no context manager; either enable this flag or re-enable native compaction for those sessions. ### `commit_cluster_trigger` A **commit cluster** is a distinct work phase where the agent made one or more git commits, separated from other commit clusters by meaningful user turns. For example, if the agent commits a fix, then the user asks a new question, and the agent commits another change — that's 2 commit clusters. This heuristic detects natural work-unit boundaries and fires historian to compartmentalize them, even when context pressure is low. ```jsonc { "commit_cluster_trigger": { "enabled": true, // default: true "min_clusters": 3 // default: 3, minimum: 1 } } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | `boolean` | `true` | Enable commit-cluster based historian triggering. | | `min_clusters` | `number` | `3` | Minimum number of commit clusters in the unsummarized tail before historian fires. The tail must also contain at least one `trigger_budget` worth of tokens, where `trigger_budget = main_context × execute_threshold × 5%` clamped to `[5K, 50K]`. | Set `enabled: false` to disable this trigger entirely and rely only on pressure-based and tail-size triggers for historian. ### `system_prompt_injection` Controls whether and where Magic Context augments the system prompt (its guidance block plus the surrounding project-docs and user-profile blocks). OpenCode's internal hidden agents — `title`, `summary`, and `compaction` — are always skipped automatically; this section lets you opt out additional agents, or disable injection globally. ```jsonc { "system_prompt_injection": { "enabled": true, // default: true "skip_signatures": [""] // default } } ``` - **`enabled`** — when `false`, NO injection happens for ANY agent. Global escape hatch; Magic Context's transform and compaction still run, but nothing is added to the system prompt. - **`skip_signatures`** — substring opt-out list. If an agent's system prompt contains any of these strings, Magic Context skips ALL injection for that call. Use it to exempt a specific custom agent by putting the signature (e.g. the default ``) in that agent's prompt. ### `todowrite` (Pi only) Pi does not ship a built-in `todowrite` tool. Magic Context's task-list tool is off by default; enable it to use the tool, `/todos` command, and optional overlay: ```jsonc { "todowrite": { "enabled": true, // default: false (opt in to Pi todowrite) "overlay": true // default: true } } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | `boolean` | `false` | Set `true` to register Magic Context's Pi `todowrite` tool and `/todos` command. | | `overlay` | `boolean` | `true` | Show the persistent todo overlay above the editor while tasks are active. | Pi registers tools, slash commands, and widgets once at extension boot. If you `/cd` into a project with a different `todowrite.enabled` value, run `/reload` or restart Pi for the tool surface to change. ### `sqlite` Per-connection PRAGMAs applied to Magic Context's own `context.db` at open. These tune SQLite's runtime behaviour only — they do not change the schema or what is stored, and they do not touch OpenCode's or Pi's databases. ```jsonc { "sqlite": { "cache_size_mb": 64, // default: 64, min: 2, max: 2048 — page-cache size per connection "mmap_size_mb": 0 // default: 0 (disabled), min: 0, max: 8192 — memory-mapped I/O size } } ``` - **`cache_size_mb`** — how much page cache each connection keeps resident (`PRAGMA cache_size`). The DB grows large on long-lived projects, and several hot paths do repeated full-table scans; a larger cache keeps those pages in memory instead of re-reading from disk. Raised from SQLite's ~2 MB default to **64 MB**. - **`mmap_size_mb`** — memory-maps the database file (`PRAGMA mmap_size`) so reads avoid a copy through the page cache. Can reduce read overhead on large DBs at the cost of address space. **Disabled by default (`0`)**, matching SQLite's default; raise it (e.g. `256`) only if you want to experiment with read performance. Separately, Magic Context runs `PRAGMA optimize` (bounded by `PRAGMA analysis_limit=400`) on its 15-minute maintenance tick. This is self-gating — it re-analyses a table only when its row count has drifted enough to matter — so the query planner keeps choosing good indexes as the database grows. There is no config knob for it. ### `output_reserve` Magic Context budgets against the **safe input window**, not the catalog's combined input-plus-output number. By default it uses these rules: 1. A provider-declared `input` smaller than `context` is already pre-carved and is used unchanged. 2. Otherwise, output tokens are reserved from `context`, capped at 25% of the context window. 3. Google and Pi's `google-antigravity` Gemini family keep the full context window because their output quota is separate. Use `output_reserve` in user config to override rules 2 and 3. A number applies to every model; an object supports exact `provider/model`, bare-model, and `default` entries: ```jsonc { "output_reserve": { "default": 16384, "google/gemini-2.5-pro": 0 } } ``` Set `0` to disable reservation. Project configs cannot set this field. Very large values are clamped so the usable window remains at least half of the raw context (and never below the module's plausibility floor); Magic Context logs when a clamp is required. Percentages in `/ctx-status`, the sidebar, and the Pi TUI use this safe window. Consequently, the displayed percentage rises and absolute compaction points move earlier for shared-window models even when the raw provider context number is unchanged. ### `execute_threshold_tokens` An absolute-tokens alternative to `execute_threshold_percentage`. Useful when you want a hard cap expressed in tokens rather than a percentage — for example, when a provider limits effective prompt size below its advertised context window. ```jsonc { "execute_threshold_tokens": { "default": 150000, // fires at 150K for any model without an explicit entry "github-copilot/gpt-5.2-codex": 40000 // fires at 40K specifically for gpt-5.2-codex } } ``` **Behavior:** - Per-model map only — no bare-number form. All sessions are assumed to have different context limits, so the `default` key acts as a fallback inside the map. - **Tokens wins:** when a matching entry exists for the current model, it overrides the percentage-based threshold for that model. Other models continue to use `execute_threshold_percentage`. - **Progressive key lookup** just like percentage config — `openai/gpt-5.4-fast` matches `openai/gpt-5.4` if the derived key is absent. - **Clamped at 90% × context_limit** for the same cache-safety reason as percentage. If the clamp fires, a `log.warn` records the original and capped value. - Requires a **resolvable context limit** at runtime. On brand-new sessions before any response arrives, the context limit is unknown — in that case, resolution falls through to `execute_threshold_percentage`. Once the first response lands, the correct tokens-based threshold is applied on the following turn. **When to prefer tokens over percentage:** - You hit a provider-side prompt cap (like GitHub Copilot's `max_prompt_tokens` ignoring user config overrides — see the github-copilot interaction in the project KNOWN_ISSUES). - You want consistent compaction behavior across models with very different context window sizes. **When to prefer percentage:** - You want the threshold to scale proportionally with the model's window (bigger window → compacts later in absolute terms). - You're not targeting a specific provider cap. --- ## Model Resolution Each hidden agent (historian and dreamer) uses the `model` you configure for it. There is **no built-in fallback chain** — Magic Context never silently tries models you haven't configured (a hardcoded chain inevitably names providers you don't have, producing confusing `Model not found` errors). For OMP, harness selection is whole-block precedence: `historian.omp ?? historian.pi` and `dreamer.omp ?? dreamer.pi` (including task overrides). An explicit OMP block is authoritative even if it omits a model; only an absent block falls back to Pi. Within the selected block, if the configured primary fails (auth, transient, or returns unusable output), the fallback order is: 1. Your explicit `fallback_models` for that agent, in order. 2. **Historian only:** your active session model, as a last resort (a model you're already using). The dreamer uses only its configured `fallback_models`. If you set no `fallback_models`, a failing primary simply retries — it never jumps to an unconfigured model. Set `fallback_models` to add alternates of your own (each `"provider/model-id"`). > **Tip — Dreamer with local models:** Since the dreamer runs during idle time (typically overnight), it works well with local models. Even slower ones like `ollama/mlx-qwen3.5-27b-claude-4.6-opus-reasoning-distilled` are fine — there's no user waiting. ### Advanced agent fields Both agents (`historian` and `dreamer`) accept these additional fields beyond the common `model`, `fallback_models`, `temperature`, `variant`, `prompt`. Most map directly to OpenCode's `AgentConfig` and pass through unchanged. | Field | Type | Description | |-------|------|-------------| | `tools` | `{ [toolName: string]: boolean }` | Restrict which tools the agent can use. `{ "bash": false, "write": false }` disables those tools for this agent only. | | `permission` | `object` | Per-agent permission overrides. Sub-fields: `edit`, `bash`, `webfetch`, `doom_loop`, `external_directory`. Each accepts `"ask"`, `"allow"`, or `"deny"`. `bash` additionally accepts a record form for per-command rules. | | `disable` | `boolean` | Disable the agent without removing its config. Useful for toggling on/off during testing. | | `description` | `string` | Agent description shown in OpenCode UI. | | `mode` | `"subagent"` \| `"primary"` \| `"all"` | OpenCode agent mode. Magic Context internal agents run as `subagent`. | | `top_p` | `number` (0–1) | Nucleus sampling. | | `maxSteps` | `number` | Max reasoning steps per agent call. | | `maxTokens` | `number` | Max output tokens. For reasoning-heavy historian models, reserve enough output budget for the complete compartment structure after reasoning. | | `color` | `string` (`#RRGGBB`) | Display color in OpenCode UI. | Example — restricting historian to read-only tools and denying bash: ```jsonc { "historian": { "opencode": { "model": "github-copilot/gpt-5.4" }, "tools": { "bash": false, "write": false, "edit": false }, "permission": { "bash": "deny", "webfetch": "deny" } } } ``` --- ## Compaction-off mode Set this in the user configuration when Magic Context should provide knowledge without managing the context window: ```jsonc { "compaction": { "enabled": false } } ``` The setting is resolved at process boot. Restart OpenCode, Pi, or OMP after changing it. A project-tier `compaction.enabled` is stripped, so a cloned repository cannot disable the user's context management. ### What stays on - Memory, docs, user-profile, and key-file injection stays additive through m[0]/m[1]. The messages transform remains registered so knowledge still reaches the model. - Raw-message FTS indexing, `ctx_search`, `ctx_expand`, `ctx_memory`, `ctx_note`, `/ctx-embed`, notes, and dreamer maintenance remain available. Historian-dependent dreamer tasks simply find no candidates. - Native OpenCode, Pi, or OMP compaction is allowed to run. Magic Context does not enable native compaction. If native compaction is also disabled, boot and doctor report `no active compaction`; the sidebar uses `Context: % · native compaction` or `Context: % · no active compaction` and never renders MC's execute-threshold fill. ### What stops Magic Context does not tag new messages or write tags, create or inject compartments, write its compaction markers, fold, prune, drop, strip, splice, apply pending drops, run heuristic or emergency reclaim, add synthetic context-management todos, add temporal markers, nudge, or block on a failed transform. `ctx_reduce` is unavailable; `ctx_expand` remains available. `/ctx-wrapup`, `/ctx-recomp`, and `/ctx-flush` refuse with `Unavailable: magic-context is in compaction-off mode (compaction.enabled=false).` and make no context-management changes; `/ctx-embed` remains functional. `fail_closed_blocking` is inert in this mode: a transform failure passes the input messages through without blocking or cancelling the request. Magic Context's `compaction.enabled` in `magic-context.jsonc` is not OpenCode's `compaction.auto` or `compaction.prune` in `opencode.jsonc`. These are different files and different owners. The coexistence guarantee covers OpenCode, Pi, and OMP native compaction; DCP and OMO context-transforming hooks keep their existing conflict policy. ### Transitions and long sessions Disabling the mode removes Magic Context's own marker boundary lazily when a session is next resumed. History hidden solely by Magic Context becomes visible; a surviving native boundary still hides older history. The first turn after disabling may trigger one native compaction cycle on long sessions. Magic Context does not pre-trim or otherwise mitigate that spike. An unresumed session is not swept in the background. When turning compaction back on, run `/ctx-wrapup` if the historian is runnable so Magic Context can catch up. If the historian is disabled, no `/ctx-wrapup` suggestion is emitted. Native compaction covers child sessions (verified against OpenCode v1.18.4): subagents receive additive memory/docs injection but no Magic Context reclaim in this mode. Keep subagent tasks small, or keep `compaction.enabled` on for projects that rely on long subagent runs. Raw content hidden by a native boundary before Magic Context's first pass is not retroactively indexed; later passes index new and observed raw messages. If `transform_mode: "rust"` is also configured, compaction-off mode resolves to the TypeScript transform and emits one frozen boot warning. There is no Rust reduced-mode contract in this cycle. ## `historian` Automatic tool drops ride folds and flushes; below the force band they wait for the next fold rather than creating a pressure-only cache bust. Historian retains agent metadata at `historian`, while each harness receives its own strict model-resolution block: ```jsonc { "historian": { "two_pass": false, "opencode": { "model": { "model": "github-copilot/gpt-5.4", "variant": "high" }, "fallback_models": ["anthropic/claude-sonnet-4-6"] }, "pi": { "model": { "model": "github-copilot/gpt-5.4", "thinking_level": "high" }, "fallback_models": ["anthropic/claude-sonnet-4-6"] }, "omp": { "model": { "model": "opencode/gpt-5.4", "thinking_level": "auto" }, "fallback_models": [ { "model": "anthropic/claude-sonnet-4-6", "thinking_level": "inherit" } ] } } } ``` An OpenCode entry is either a model string or `{ "model": "provider/model", "variant": "..." }`. Pi and OMP entries use `{ "model": "provider/model", "thinking_level": "..." }`. Their entry objects and harness blocks are strict: Pi/OMP never accept `variant`, and OpenCode never accepts `thinking_level`. OMP accepts Pi's levels plus `inherit` and `auto`: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `inherit`, or `auto`. OMP also accepts its native `provider/model:level` selectors; host/provider capability gates still govern `max` and `auto`. | Field | Type | Description | |-------|------|-------------| | `historian.opencode.model` | OpenCode entry | Primary OpenCode model. | | `historian.opencode.fallback_models` | OpenCode entry[] | Ordered OpenCode fallback entries. New-shape fallbacks must be arrays. | | `historian.opencode.variant` | `string` | Default OpenCode reasoning variant. | | `historian.pi.model` | Pi entry | Primary Pi model. | | `historian.pi.fallback_models` | Pi entry[] | Ordered Pi fallback entries. New-shape fallbacks must be arrays. | | `historian.pi.thinking_level` | Pi thinking level | Default Pi reasoning level. | | `historian.omp.model` | OMP entry | Primary OMP model; absent `historian.omp` falls back to `historian.pi`. | | `historian.omp.fallback_models` | OMP entry[] | Ordered OMP fallback entries with per-entry thinking levels. | | `historian.omp.thinking_level` | OMP thinking level | Default OMP reasoning level, including `inherit` and `auto`. | | `historian.temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `maxSteps`, `permission`, `maxTokens`, `two_pass`, `disallowed_tools` | metadata | Retained at `historian`; these fields never move into a harness block. | --- ## `dreamer` Dreamer scheduling and agent metadata remain at `dreamer`, while task execution is isolated under the executing harness. A task schedule decides whether the task is due; the harness block decides how that harness runs it. ```jsonc { "dreamer": { "inject_docs": true, "tasks": { "verify": { "schedule": "0 3 * * *" }, "retrospective": { "schedule": "0 5 * * *", "recency_days": 30 }, "review-user-memories": { "schedule": "0 3 * * *", "promotion_threshold": 3 } }, "opencode": { "model": { "model": "anthropic/claude-sonnet-4-6", "variant": "high" }, "fallback_models": ["openai/gpt-5.4"], "tasks": { "verify": { "timeout_minutes": 30, "variant": "medium" } } }, "pi": { "model": { "model": "github-copilot/gpt-5.4", "thinking_level": "high" }, "fallback_models": ["openai/gpt-5.4"], "tasks": { "verify": { "timeout_minutes": 30, "thinking_level": "medium" } } }, "omp": { "model": { "model": "opencode/gpt-5.4", "thinking_level": "auto" }, "fallback_models": ["openai/gpt-5.4"], "tasks": { "verify": { "timeout_minutes": 30, "thinking_level": "inherit" } } } } } ``` | Location | Allowed fields | Description | |----------|----------------|-------------| | `dreamer.opencode` | `model`, `fallback_models`, `variant`, `tasks` | Strict OpenCode execution block. `fallback_models` is an array of OpenCode entries. | | `dreamer.pi` | `model`, `fallback_models`, `thinking_level`, `tasks` | Strict Pi execution block. `fallback_models` is an array of Pi entries. | | `dreamer.omp` | `model`, `fallback_models`, `thinking_level`, `tasks` | Strict OMP execution block; absent block falls back to `dreamer.pi`. | | `dreamer.opencode.tasks.` | `model`, `fallback_models`, `variant`, `timeout_minutes` | Strict OpenCode task execution override. | | `dreamer.pi.tasks.` | `model`, `fallback_models`, `thinking_level`, `timeout_minutes` | Strict Pi task execution override. | | `dreamer.omp.tasks.` | `model`, `fallback_models`, `thinking_level`, `timeout_minutes` | Strict OMP task execution override. | | `dreamer.tasks.` | `schedule` | Harness-independent task metadata. `schedule: ""` disables the task; there is no separate `enabled` key. | | `dreamer.tasks.retrospective` | `schedule`, `recency_days` | Retrospective metadata. `recency_days` is the source lookback in whole days (default `30`); older lines expire by advancing the content watermark without collecting them. | | `dreamer.tasks.review-user-memories`, `dreamer.tasks.promote-primers` | `schedule`, `promotion_threshold` | Task-specific promotion metadata. | | `dreamer.temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `maxSteps`, `permission`, `maxTokens`, `inject_docs` | metadata | Retained at `dreamer`; these fields never move into a harness block. | To disable the dreamer entirely, set `dreamer.disable: true`. To disable a single task, set its top-level `dreamer.tasks..schedule` to `""`; it can still be run on demand via `/ctx-dream `. ### The tasks | Task | Default schedule | What it does | |------|------------------|-------------| | `map-memories` | `0 2 * * *` | Map each memory to its backing files (or mark it file-independent) so verify has a per-memory gate target. Mostly a one-time backfill, then a cheap trickle. | | `verify` | `0 3 * * *` | Re-verify only memories whose mapped files changed since *that memory* was last checked, and fix or remove stale facts. | | `verify-broad` | `0 4 * * 0` | Re-verify the whole file-mapped pool against code, ignoring the change gate. | | `curate` | `0 4 * * 0` | Curate the whole active memory pool: consolidate duplicates, tighten wording, and archive low-value or redundant entries. | | `classify-memories` | `0 6 * * *` | Score memory importance, scope, and shareability so recall stays focused. | | `retrospective` | `0 5 * * *` | Learn from moments you had to correct or re-explain, and record the durable lesson. | | `maintain-docs` | `""` (off) | Propose section corrections for `ARCHITECTURE.md` and `STRUCTURE.md` without editing either file. | | `promote-primers` | `0 3 * * *` | Promote recurring standing questions the historian noticed into durable primers. | | `refresh-primers` | `0 3 * * *` | Re-investigate stale primers against current code and refresh their answers. | | `evaluate-smart-notes` | `0 3 * * *` | Surface smart notes whose `ctx_note` conditions have come true. | | `review-user-memories` | `0 3 * * *` | Promote recurring behavioral observations into the `` block (privacy-sensitive). | ### Retrospective privacy `retrospective` is default-on but cheap. It scans typed user messages newer than both its content watermark and `dreamer.tasks.retrospective.recency_days` (default 30 days). Oversized user pastes are clamped head+tail, and the oldest-first source window is admitted against the child model's usable input budget so a backlog drains without skipping newer rows. A cheap child turn first detects correction/re-explanation signal; on a signal, a ctx_search-only second turn emits XML learnings and the host validates/applies them. Project learnings become normal project memories; observation learnings are dropped unless `review-user-memories` is scheduled. ### How scheduling works A process-wide 15-minute timer checks every task's `next_due_at` regardless of user activity, so scheduled tasks trigger even when you aren't chatting: 1. The timer evaluates each task's cron schedule and collects the tasks that are due. 2. Due tasks pass their activity gate (e.g. memory tasks only run when there are memories to maintain), then run grouped by lease domain. The memory-maintenance tasks (`map-memories`, `verify`, `verify-broad`, `curate`, `classify-memories`, `retrospective`, and the primer tasks) share the memory lease so they never collide; docs, smart notes, and user-memory review use their own domains. 3. Each task runs in its own ephemeral child session and advances its own `next_due_at`. 4. `/ctx-dream` runs every enabled task now (honoring gates); `/ctx-dream ` force-runs one task immediately, ignoring its gate. A freshly-configured task first runs at its next scheduled time, not immediately on startup. --- ## `embedding` Controls semantic search for cross-session memories. | Field | Type | Default | Description | |-------|------|---------|-------------| | `provider` | `"local"` \| `"openai-compatible"` \| `"off"` | `"local"` | `"local"` runs `Xenova/all-MiniLM-L6-v2` in-process. `"off"` disables semantic ranking entirely — see below. | | `model` | `string` | `"Xenova/all-MiniLM-L6-v2"` | Embedding model. | | `local_dtype` | `string` | — | Local provider only. ONNX model dtype passed to the transformers.js feature-extraction pipeline (`auto`, `fp32`, `fp16`, `q8`, `int8`, `uint8`, `q4`, `bnb4`, `q4f16`, `q2`, `q2f16`, `q1`, `q1f16`). Omitted keeps the default `fp32` behavior. | | `endpoint` | `string` | — | Required for `"openai-compatible"`. | | `api_key` | `string` | — | Optional API key for remote endpoints. | | `query_instruction` | `string` \| `false` | model-family recipe | OpenAI-compatible query prefix. A string is prepended verbatim; `false` disables the built-in recipe. User-level only. | | `document_prefix` | `string` | model-family recipe | OpenAI-compatible stored-document prefix. Empty for Qwen3/gte/e5 instruct and `"search_document: "` for Nomic by default. User-level only. | Instruction-tuned embedding models are trained to distinguish retrieval queries from passages, so Magic Context automatically prepends the model card's query recipe for Qwen3-Embedding, gte-Qwen instruct, e5 instruct, and Nomic families. Plain local encoders are unchanged. Query instructions affect only live search vectors, not stored document vectors, so changing `query_instruction` does not re-embed the corpus; changing a non-empty `document_prefix` does because it changes every stored vector. The local provider downloads model files from Hugging Face. Set the standard `HF_ENDPOINT` environment variable to the base URL of a Hugging Face-compatible mirror when `huggingface.co` is unavailable; a trailing slash is optional. The mirror only changes where identical model files are downloaded and does not change embedding identities or trigger re-embedding. When `provider: "off"`: - No embeddings are generated, for memories or session history. `ctx_memory(write)` skips embedding inline and the background embedding sweep becomes a no-op. - `ctx_search` and memory injection fall back to FTS5 (BM25) ranking only. Keyword matches still work; semantic similarity does not. - Session-start memory injection still happens when `memory.enabled` is `true` — memories are ordered by utility tier plus `seen_count` rather than semantic similarity to the current turn. - Memories written while `off` is active will have no embedding row; if you later re-enable `"local"` or `"openai-compatible"`, the background sweep embeds them on the next 15-minute tick. ```jsonc { "embedding": { "provider": "openai-compatible", "model": "text-embedding-3-small", "endpoint": "https://api.openai.com/v1", "api_key": "{env:OPENAI_API_KEY}" } } ``` > **Note:** Any string in `magic-context.jsonc` can use `{env:VAR}` to reference an environment variable, or `{file:path}` to inline the contents of an external file (matching OpenCode's own config substitution). Paths are resolved relative to the config file's directory; `~/` expands to the home directory. Use `doctor` after editing — it probes the configured embedding endpoint and reports missing env vars, wrong URLs, auth failures, or providers that don't implement the embeddings API. > **Not every provider offers embeddings.** OpenRouter and Anthropic's public API do not expose `/embeddings`; use OpenAI, Voyage, Together, LM Studio, or the bundled `"local"` provider instead. `doctor` will flag 404/405 responses and show the actual error. > **Local provider — `local_dtype` (issue #259):** The default `Xenova/all-MiniLM-L6-v2` model is lightweight but performs poorly when matching queries in one language (e.g. Chinese) to memories in another (e.g. English). A multilingual model such as `Xenova/paraphrase-multilingual-MiniLM-L12-v2` fixes the recall, but its full-precision (`fp32`) ONNX weights are large (~448 MiB) and memory-hungry for a coding-agent process that may run parallel subagents. Set `embedding.local_dtype` to a quantized variant (e.g. `"q8"`) to load a smaller ONNX model (~113 MiB) with comparable retrieval quality and far lower peak RSS. The dtype is passed to the transformers.js `feature-extraction` pipeline and, because it changes the produced vectors, a non-default value folds into the embedding model identity — so switching dtype re-embeds your corpus rather than mixing incompatible vector spaces. Omit the field to keep the default `fp32` behavior; existing installs see zero change on upgrade. --- ## `memory` Cross-session memory settings. All memories are scoped to the current project (identified by git root commit hash, with directory-hash fallback for non-git projects). | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | `boolean` | `true` | Enable cross-session memory. When `false`, the `ctx_memory` tool is hidden, no `` block is injected, and historian/recomp do not promote any session facts to project memories. The `ctx_search` tool stays available but its memory source returns no results, and no memory rows are embedded. Session history is still embedded automatically and silently for semantic `ctx_search` whenever an embedding provider is configured; set `embedding.provider: "off"` to stop all embedding. | | `injection_budget_tokens` | `number` (500–20000) | `4000` | Token budget for memory injection into ``. | | `auto_promote` | `boolean` | `true` | Promote eligible session facts to project memories automatically after historian or `/ctx-recomp` runs. When `false`, historian and recomp do not write any new memories — agents can still create memories explicitly via `ctx_memory write`, and existing memories continue to be injected and searched normally. | | `retrieval_count_promotion_threshold` | `number` | `3` | Retrievals needed before a memory is auto-promoted to permanent. | --- ## Dreamer Sub-Features In Dreamer v2 the former `user_memories` sub-feature block became a first-class scheduled task: **`review-user-memories`** (see the `dreamer.tasks` table above). The `doctor` command migrates a legacy `dreamer.user_memories` config to the equivalent task entry automatically, preserving your enable/disable state and tuning values. (A legacy `dreamer.pin_key_files` block is dropped — key-files pinning has moved out of Magic Context.) - **`review-user-memories`** (was `user_memories`): set its `schedule` to enable, `""` to disable. Carries `promotion_threshold` (2–20, default 3). When scheduled, the historian extracts behavioral observations and this task promotes recurring patterns to stable user memories injected via ``. Privacy-sensitive — only runs when scheduled. ## History & Recall Features > These four features graduated out of the old `experimental.*` namespace. `temporal_awareness` and `caveman_text_compression` are now top-level keys; `auto_search` and `git_commit_indexing` moved under `memory.*`. The `doctor` command relocates legacy `experimental.*` configs automatically and preserves any user-set values. **`temporal_awareness` and `memory.auto_search` are now ON by default** — set them `false` to opt out. ### `temporal_awareness` | Key | Type | Default | |-----|------|---------| | `temporal_awareness` | `boolean` | `true` | When enabled, Magic Context surfaces wall-clock time to the agent in two cache-safe ways: 1. **User-message gap markers.** Each user message is prefixed with an HTML comment like ``, ``, or `` indicating time elapsed since the previous message's completion. Only shown when the gap exceeds 5 minutes. Derived from immutable `message.time.completed ?? message.time.created` timestamps. 2. **Compartment date ranges.** Each `` element in `` carries `start-date="YYYY-MM-DD"` and `end-date="YYYY-MM-DD"` attributes showing real-time boundaries. Lets agents reason correctly about workflow pacing, log durations, build times, "how long ago" references, and session age. Without this flag the agent has no sense of time at all. **Cache safety.** Markers are idempotent by regex detection and derive from static message timestamps — re-running the injector on any transform pass produces the same output, so enabling the flag busts cache once (on the first pass after flip) and then stays stable. Historian input is untouched. ### `memory.git_commit_indexing` | Key | Type | Default | |-----|------|---------| | `memory.git_commit_indexing.enabled` | `boolean` | `false` | | `memory.git_commit_indexing.since_days` | `number` | `365` | | `memory.git_commit_indexing.max_commits` | `number` | `2000` | Opt-in (default off; independent of `memory.enabled`). When enabled, Magic Context indexes HEAD git commits (skipping merges) from the project and makes them searchable through `ctx_search`. Commits are embedded using the configured embedding provider, so semantic search surfaces "when did we change the X pattern" or "why did we pick Y over Z" queries. - **HEAD only, no merges.** Abandoned experiments on feature branches don't pollute search; merged work becomes reachable from HEAD anyway. - **Windowed.** Only commits from the last `since_days` days are indexed (default 365). Older commits exit the window and are evicted when the project cap is exceeded. - **Project-scoped.** Commits are stored per project identity (git root commit) so worktrees and clones share the same index. - **Capped per project.** `max_commits` is a hard upper bound (default 2000). Oldest commits are evicted first when the cap is exceeded. - **Non-blocking.** Initial sweep runs at startup; incremental tick runs every 15 minutes from the dream timer. The sweep skips already-indexed SHAs. - **ctx_search integration.** Results appear as a `git_commit` source alongside `memory`, `session_fact`, and `message_history`. Each result carries the SHA, short SHA, author, and commit timestamp. ### `memory.auto_search` | Key | Type | Default | |-----|------|---------| | `memory.auto_search.enabled` | `boolean` | `true` | | `memory.auto_search.score_threshold` | `number` | `0.6` | | `memory.auto_search.min_prompt_chars` | `number` | `20` | On by default (independent of `memory.enabled` — it can still surface conversation/git hints with the memory store off; set `enabled: false` to opt out). Magic Context runs a background `ctx_search` on each new user message and, when a strong match is found, appends a compact "vague recall" hint to that user message. The hint surfaces highly compressed fragments from the best matches so the agent can decide whether to run `ctx_search` for the full content. The hint looks like: ```xml Your memory may contain related context (3 related fragments): - install.sh bunx --bun node stdin redirection - magic-context fail closed durable storage unavailable - commit abcd123 5d ago: install: force bun runtime in bunx invocation Run ctx_search to retrieve full context if relevant. ``` - **Memory fragments** are caveman-ultra compressed (stop words stripped, common verbs replaced) — dense keywords that mirror vague human recall. - **Commit fragments** are the raw commit message (truncated, prefixed with `sha + relative age`) — commit messages are already compressed. - **Session facts** use the same caveman-ultra compression as memories. **Parameters:** - `score_threshold`: minimum top-hit cosine score for the hint to fire (0.3–0.95, default 0.6). More permissive than direct injection because false-positive cost is small — the agent ignores irrelevant hints. - `min_prompt_chars`: minimum user message length to trigger auto-search (default 20). Short prompts like "yes" or "ok" don't get a hint. **Suppression rules.** The hint is not appended when: 1. `` or `` is already present on the user message (avoids double-nudging). 2. The user message is shorter than `min_prompt_chars`. 3. No result clears the threshold. 4. An earlier pass already appended a hint for this message id (replayed verbatim on defer passes for cache safety). **Cache safety.** The hint is appended to the current user message during the first transform pass of that turn — this message has not been cached by the provider yet because it just arrived. On subsequent defer passes the same hint text is replayed exactly (from a deterministic per-message cache), so the append is idempotent and never rewrites cached content. **Tokens.** Hints are hard-capped at ~200 tokens (3 fragments × ~20-40 tokens each plus framing). Well under the cost of full-content injection (~500+ tokens), while still giving the agent enough signal to decide whether to search. ### `caveman_text_compression` | Key | Type | Default | |-----|------|---------| | `caveman_text_compression.enabled` | `boolean` | `false` | | `caveman_text_compression.min_chars` | `number` | `500` | **Primary-session opt-in.** When enabled, each execute-threshold heuristic pass caveman-compresses long user and assistant text parts in primary sessions based on their position in the eligible tag window. Subagents are never caveman-compressed because their context is curated by the parent and they have no `ctx_expand` recovery path. **Age-tier partitioning.** Eligible tags (active, message-type, outside protected tail, text part ≥ `min_chars`) are sorted oldest-first and bucketed: | Position (oldest → newest) | Target caveman level | |---|---| | Oldest 20 % | **Ultra** — symbol connectives (`→`, `+`, `//`, `\|`), common-term abbreviations | | Next 20 % | **Full** — drop articles and most auxiliaries; fragments OK | | Next 20 % | **Lite** — drop filler and hedging; keep grammar | | Newest 40 % | Untouched | Tier boundaries are hardcoded to keep behavior predictable and prevent cache-busting storms from user tweaking. **Always compressed from the original.** The pristine pre-caveman text is persisted in `source_contents` per tag. When a tag shifts deeper (lite → full → ultra), caveman compresses the ORIGINAL text at the new target depth rather than the already-cavemaned intermediate, so repeated tier shifts converge to exactly the same output as direct compression at the final depth. **Cache safety.** Runs only on execute-threshold heuristic passes (same gate as automatic tool drops), so the single cache-busting pass materializes both tool drops and caveman compression together. Defer passes don't run caveman, and tier assignments are persisted in `tags.caveman_depth` so the next pass re-compresses only the tags that have shifted tiers. **Relationship to `ctx_reduce`.** Caveman compression is independent of the agent-driven `ctx_reduce` tool. `ctx_reduce` is still best for tool outputs the agent knows are spent; caveman targets old prose only, and dropped tags always win over caveman rewriting. To hide the reduce surface for a particular agent, remove or deny `ctx_reduce` in that agent's tool allow-list; Magic Context then omits `§N§` prefixes, nudges, and reduce guidance for that session. **When to enable.** Enable if you find historian/heuristics insufficient for your workload — typically sessions with very long pasted content or verbose agent explanations that the automatic pipeline doesn't reach. ### `smart_drops` | Key | Type | Default | |-----|------|---------| | `smart_drops` | deprecated, ignored | — | **Always on.** Content-aware reclaim of tool output that a later call has made obsolete is layered on top of the normal age-based auto-drop. Old configs containing `smart_drops` keep loading, but its value has no effect and doctor reports it as ignored. Supersession reclaims spent results regardless of age: | Class | Behavior | |---|---| | `todowrite` | Keep the newest snapshot, drop older ones (the live plan is re-injected every pass; older snapshots are stale). | | `ctx_reduce` | Keep the newest 3 active results by default, drop older ones (preserves the visible reduce rhythm). | | Zero-value meta (`bash_status`, `bash_kill`, `ctx_note` read/dismiss) | Drop all (worthless once the call ran; `ctx_note` write/update carry intent and are never dropped). | | Superseded edits | When a file is edited more than once, the newest edit stays in full and each older edit is compressed to a marker that keeps its `filePath` and a short region hint of the diff, so the agent still sees which file and region it edited. This is the largest source of reclaimable bytes. | **Cache safety.** Selection is age-independent, but supersession only acts on passes already rebuilding the cache. Sessions that previously set `smart_drops: false` drain their backlog on the first such pass, never on an ordinary defer pass. Drops replay byte-identically afterward. ### `protected_tools` User and project JSON-object maps merge over `{ "todowrite": 1, "ctx_reduce": 3 }`. Each nonnegative whole number keeps that many newest still-active results, ordered by tag number; dropped or historian-summarised results do not count. Project entries override matching user entries. Names ignore case and a leading `mcp_`; 0 disables a default. Emergency, age, supersession (including edit-marker compression), and duplicate removal honour the map even at ≥95%. There is no byte cap, so large protected outputs can reach refusal sooner. Queued drops, from the agent or historian publication, are held like the protected tail until newer calls displace the result and a later cache-rebuilding pass applies them. The historian's summary is unaffected; the raw result leaves at the next fold. Frozen strips are unaffected. Map changes and N+1 rotation never originate a bust or restore dropped results. ## Commands | Command | Description | |---------|-------------| | `/ctx-status` | Show current context usage, tag counts, pending queue, nudge state, and history compression info. | | `/ctx-flush` | Force-execute all pending operations and heuristic cleanup immediately. | | `/ctx-recomp` | Rebuild all compartments and facts from raw session history. Resumable across restarts. | | `/ctx-recomp -` | Partial rebuild of a message range (e.g. `/ctx-recomp 1-11322`). Snaps to enclosing compartment boundaries, rebuilds only those compartments using current historian rules, and leaves prior/tail compartments and all session facts untouched. Useful after upgrading historian prompt versions or model quality. Resumable across restarts; running with a different range while partial-recomp staging exists is rejected. Currently Desktop/Web-only (TUI falls back to full-recomp dialog; ranged TUI dialog is planned). | | `/ctx-dream` | Enqueue the current project for a dream run and process immediately. | --- ## Full example ```jsonc { "enabled": true, "cache_ttl": { "default": "5m", "anthropic/claude-opus-4-6": "58m" }, "execute_threshold_percentage": { "default": 65, "anthropic/claude-opus-4-6": 50 }, "protected_tokens": 20000, "toast_duration_ms": 12000, "history_budget_percentage": 0.15, "temporal_awareness": true, "historian": { "model": "github-copilot/gpt-5.4", "fallback_models": ["anthropic/claude-sonnet-4-6"] }, "dreamer": { "model": "github-copilot/gpt-5.4", "fallback_models": ["anthropic/claude-sonnet-4-6"], "tasks": { "map-memories": { "schedule": "0 2 * * *" }, "verify": { "schedule": "0 3 * * *" }, "verify-broad": { "schedule": "0 4 * * 0" }, "curate": { "schedule": "0 4 * * 0" }, "classify-memories": { "schedule": "0 6 * * *" }, "retrospective": { "schedule": "0 5 * * *" }, "maintain-docs": { "schedule": "" }, "promote-primers": { "schedule": "0 3 * * *", "promotion_threshold": 2 }, "refresh-primers": { "schedule": "0 3 * * *" }, "evaluate-smart-notes": { "schedule": "0 3 * * *" }, "review-user-memories": { "schedule": "0 3 * * *", "promotion_threshold": 3 } } }, "embedding": { "provider": "local" }, "memory": { "enabled": true, "injection_budget_tokens": 4000, "auto_promote": true, "auto_search": { "enabled": true, "score_threshold": 0.6, "min_prompt_chars": 20 }, "git_commit_indexing": { "enabled": false, "since_days": 365, "max_commits": 2000 } }, } ```