# 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. ### 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" } }, "sidekick": { "model": "anthropic/claude-haiku-4-5" } }, "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" } }, "sidekick": { "model": "openai/gpt-5.2-codex-mini" } } } } ``` 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`, and sidekick model fields); 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. 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/Pi/OMP v1 limitation:** per-model routing in `models` applies to the guidance block only. Tool descriptions are registered once per process by the v1 plugin API, so they always follow `prompt_surface.default`. Per-model tool descriptions are planned for the OpenCode v2 plugin API once the SDK stabilizes (tracked in [#260](https://github.com/cortexkit/magic-context/issues/260)). `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 ``` 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 default `"5m"` matches Anthropic's default TTL. 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" } } ``` 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_tags` | `number` (1–100) | `20` | Last N active tags immune from immediate dropping. | | `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: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success, so their full transcript stays in the host session store for inspection. Kept sessions accumulate until cleared manually — leave `false` for normal use. | | `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. Keep owner-only `0700` directories and `0600` files. Set `false` only for an externally managed trusted-group deployment; Magic Context will never re-tighten storage permissions. | ### `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, sidekick output, 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, so Magic Context registers an OpenCode-parity task-list tool by default. Disable it if another Pi extension already provides todo UX: ```jsonc { "todowrite": { "enabled": true, // default: true "overlay": true // default: true } } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | `boolean` | `true` | Register Magic Context's Pi `todowrite` tool and `/todos` command. Set `false` when using another todo extension. | | `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, dreamer, sidekick) 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 and sidekick use only their 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 All three agents (`historian`, `dreamer`, `sidekick`) 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`, `/ctx-flush`, and `/ctx-session-upgrade` 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 * * *" }, "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`, `promotion_threshold` | Harness-independent task metadata. `schedule: ""` disables the task; there is no separate `enabled` key. | | `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) | Keep `ARCHITECTURE.md` and `STRUCTURE.md` at project root synchronized with the codebase. | | `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 only new typed user messages since its last successful run; if there is no correction/re-explanation signal, it exits without a child session. On a signal, a ctx_search-only child 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. | When `provider: "off"`: - No embeddings are generated. `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. | | `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. | --- ## `sidekick` Optional prompt augmenter that runs on `/ctx-aug`. Sidekick is a hidden OpenCode subagent that creates an ephemeral child session, searches memories with `ctx_memory`, and returns a focused context briefing. It is useful when starting a new session. It's better to choose a fast and cheap model, even small local models. ```jsonc { "sidekick": { "enabled": true, "model": "github-copilot/grok-code-fast-1", "fallback_models": ["cerebras/qwen-3-235b-a22b-instruct-2507"], "timeout_ms": 30000 } } ``` ### Agent fields | Field | Type | Description | |-------|------|-------------| | `model` | `string` | Primary model. | | `fallback_models` | `string` or `string[]` | Fallback models. | | `temperature` | `number` (0–2) | Sampling temperature. Opt-in across every harness: omission sends no `temperature` instruction to the provider; set it explicitly for flash-class calibration. Some reasoning models reject or constrain temperature, so only set it for models that accept it. | | `variant` | `string` | **OpenCode only.** Agent variant — selects a thinking/reasoning preset. Pi uses `thinking_level` instead. | | `thinking_level` | `string` | **Pi only.** Explicit reasoning level (`off`/`low`/`medium`/`high`) passed to Pi for sidekick subagent runs. See `historian.thinking_level`. | | `prompt` | `string` | Persistent agent-level system prompt override. Applies to every sidekick run. | ### Operational fields | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | `boolean` | `false` | Enable sidekick. | | `timeout_ms` | `number` | `30000` | Timeout per run (ms). | | `system_prompt` | `string` | — | Per-invocation system prompt prepended to the sidekick child session for this `/ctx-aug` call only. Layered on top of `prompt` if both are set. | > **`prompt` vs `system_prompt`:** `prompt` is the persistent agent definition applied to every sidekick run. `system_prompt` is a per-call override injected into that specific child session — useful when a single `/ctx-aug` invocation needs different guidance than the default. --- ## 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 when `/ctx-aug` was invoked). 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` | `boolean` | `false` | **Experimental, opt-in.** Content-aware reclaim of tool output that a later call has made obsolete, layered on top of the normal age-based auto-drop. The age-based drop reclaims the *oldest* tool outputs first; smart-drops additionally reclaims outputs that are dead by *supersession*, 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 5 calls, 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 smart-drops only *acts* during a transform pass that is already rewriting the message array (the same execute-threshold gate the age-based drop uses), so it never causes a prompt-cache miss on its own. Every drop resolves to the same deterministic placeholder as the normal drops, so defer passes replay byte-for-byte identically. **When `smart_drops` is off, the messages sent to the model are byte-identical to the age-based-only behavior** — the entire feature is inert. **Cross-version note.** Once you enable `smart_drops`, all binaries that share your Magic Context database (e.g. multiple OpenCode instances, or OpenCode + Pi) should be on the release that introduced it. If a stale older binary co-runs with the feature on, the worst case is a one-time cache bust (the older binary fully drops what the newer one compressed); it never corrupts data. **When to enable.** Turn it on if you run very long, edit-heavy sessions and want to reclaim more context without losing the agent's record of what it did. The default stays off while cache stability is being validated in the wild. Requires a restart to take effect. ## 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. | | `/ctx-aug` | Run sidekick augmentation on the provided prompt. | --- ## 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_tags": 10, "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 } }, "sidekick": { "model": "github-copilot/gpt-5.4", "fallback_models": ["anthropic/claude-sonnet-4-6"], "timeout_ms": 30000 } } ```