All settings are flat top-level keys in magic-context.jsonc. Create the file in your project root, .opencode/magic-context.jsonc, or ~/.config/opencode/magic-context.jsonc for user-wide defaults. Project config merges on top of user config.
Add $schema to your config file for autocomplete and validation in VS Code and other editors:
The setup wizard adds this automatically.
If something isn't working, run the doctor to auto-detect and fix common issues:
bunx --bun @cortexkit/opencode-magic-context@latest doctorDoctor checks: OpenCode installation, plugin registration, magic-context.jsonc existence, conflicts (compaction, DCP, OMO hooks), and TUI sidebar configuration. It auto-fixes what it can.
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. The default cache_ttl of "5m" matches most providers. You can tune it:
{
"cache_ttl": "5m"
}Per-model overrides for mixed-model workflows:
{
"cache_ttl": {
"default": "5m",
"anthropic/claude-opus-4-6": "60m"
}
}Supported formats: "30s", "5m", "1h".
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.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Master toggle. |
ctx_reduce_enabled |
boolean |
true |
When false, hides ctx_reduce tool, disables all nudges/reminders, and strips reduction guidance from prompts. Heuristic cleanup, compartments, memory, and search still work. Useful for testing whether automatic cleanup alone is sufficient. |
cache_ttl |
string or object |
"5m" |
Time after a response before applying pending ops. String or per-model map. |
protected_tags |
number (1–100) |
20 |
Last N active tags immune from immediate dropping. |
nudge_interval_tokens |
number |
10000 |
Minimum token growth between rolling nudges. |
execute_threshold_percentage |
number (20–80) or object |
65 |
Context usage that forces queued ops to execute. Capped at 80% max for cache safety. Supports per-model map. |
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 80% × context_limit with a warn log. Requires a resolvable context limit — falls through to percentage if unavailable. See below. |
auto_drop_tool_age |
number |
100 |
Auto-drop tool outputs older than N tags during execution. |
drop_tool_structure |
boolean |
true |
When true, dropped tool parts are fully removed from the transformed prompt. When false, tool call structure is preserved: tool name kept, tool inputs truncated to 5 chars + ...[truncated], tool output replaced with [truncated]. Preserving structure keeps the agent aware that prior tools ran (preventing hallucinated re-calls) at the cost of ~4K additional tokens per ~60 dropped tools. |
clear_reasoning_age |
number |
50 |
Clear thinking/reasoning blocks older than N tags. |
iteration_nudge_threshold |
number |
15 |
Consecutive assistant turns without user input before an iteration nudge. |
historian_timeout_ms |
number |
300000 |
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_markers |
boolean |
true |
Inject compaction boundaries into OpenCode's DB after historian publishes. Reduces transform input size for long sessions. |
commit_cluster_trigger |
object |
See below | Controls the commit-cluster historian trigger. |
compressor |
object |
See below | Controls the background compressor that merges older compartments when the history block exceeds its budget. |
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.
{
"commit_cluster_trigger": {
"enabled": true, // default: true
"min_clusters": 3 // default: 3, minimum: 1
}
}Compressor is a background pass that runs when the rendered <session-history> block exceeds its budget. It merges older compartments using progressively aggressive caveman-style compression at each depth level, enforcing style consistency via a deterministic post-process after the historian LLM call. Each compartment range can be compressed at most max_merge_depth times.
Depth tiers (applied progressively as compartments are re-compressed):
| Depth | Style | What happens |
|---|---|---|
| 1 | Merge only | Preserve narrative and all U: lines. Drop only duplicates spanning compartments. |
| 2 | Lite caveman | Drop filler words (just, really, basically) and hedging. Keep grammar. |
| 3 | Full caveman | Drop articles (the, a, an), weak auxiliaries. Fragments OK. Single paragraph per compartment. |
| 4 | Ultra caveman | Telegraphic. Symbol connectives (→, +, //, |). Pattern: [thing] [action] [reason]. |
| 5 | Title-only collapse | Content cleared (no LLM call). Raw messages recoverable via ctx_expand. |
Inspired by the caveman Claude Code skill which validated telegraph-style compression as LLM-friendly (and saves tokens without tokenizer fallback issues that character-dropping causes).
{
"compressor": {
"enabled": true, // default: true
"min_compartment_ratio": 1000, // default: 1000 (floor = ceil(total_raw_messages / ratio))
"max_merge_depth": 5, // default: 5 (1-5, deeper = more aggressive)
"cooldown_ms": 600000, // default: 600000 (10 min between background runs)
"max_compartments_per_pass": 15, // default: 15 (LLM batch cap)
"grace_compartments": 10 // default: 10 (newest N compartments never compressed)
}
}Merge ratios per depth (applied per LLM pass — small ratios preserve more narrative):
| Depth transition | Ratio | Shape |
|---|---|---|
| 0 → 1 | 1.33× (4:3) | Narrative merge; preserve all U: lines |
| 1 → 2 | 1.5× (3:2) | Drop filler, keep grammar (caveman-lite) |
| 2 → 3 | 2× (2:1) | Paragraph, fragments OK (caveman-full) |
| 3 → 4 | 2× (2:1) | Telegraph + symbol connectives (caveman-ultra) |
| 4 → 5 | — | Title-only collapse (no LLM, recoverable via ctx_expand) |
Selection strategy: The compressor picks the oldest contiguous run of compartments that share the SAME rounded compression depth (up to max_compartments_per_pass). This progresses naturally: depth-0 bands get compressed first → depth-1 bands compressed next → and so on. Each run goes through one LLM call.
Floor protection: The compressor never reduces your session's compartment count below ceil(total_raw_messages / min_compartment_ratio). For a 20K-message session with the default ratio, that's a floor of 20 compartments.
Grace period: The newest grace_compartments compartments are always excluded from compression. This protects freshly-published historian output from being re-compressed before it has been used. Default is 10, which works well even for long autonomous runs that publish many compartments per hour.
Ordinal snap: When the LLM drifts by ±1-2 ordinals on merged boundaries (e.g. outputs start=8161 when the actual input boundary is 8160), the runtime snaps those values to the enclosing input compartment's canonical boundary rather than rejecting the whole pass. Snaps are logged for observability.
Disable entirely: Set compressor.enabled: false to skip all background compression. Older sessions will simply carry a larger history footprint.
| 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.
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.
{
"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
defaultkey 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-fastmatchesopenai/gpt-5.4if the derived key is absent. - Clamped at 80% × context_limit for the same cache-safety reason as percentage. If the clamp fires, a
log.warnrecords 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_tokensignoring 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.
Each agent has a built-in fallback chain tried in order when no model is explicitly configured. If you have a GitHub Copilot subscription, Copilot-routed models are preferred for historian and dreamer since they use request-based pricing — ideal for single-prompt background work.
| Agent | Fallback Chain (first available wins) |
|---|---|
| Historian | github-copilot/claude-sonnet-4-6 → anthropic/claude-sonnet-4-6 → opencode-go/minimax-m2.7 → zai-coding-plan/glm-5 → openai/gpt-5.4 → google/gemini-3.1-pro |
| Dreamer | github-copilot/claude-sonnet-4-6 → anthropic/claude-sonnet-4-6 → google/gemini-3-flash → zai-coding-plan/glm-5 → opencode-go/minimax-m2.7 → openai/gpt-5.4-mini |
| Sidekick | cerebras/qwen-3-235b-a22b-instruct-2507 → opencode/gpt-5-nano → google/gemini-3-flash → openai/gpt-5.4-mini |
Setting model in any agent config overrides the fallback chain entirely. Setting fallback_models replaces the built-in chain with your custom list.
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-distilledare fine — there's no user waiting.
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. |
color |
string (#RRGGBB) |
Display color in OpenCode UI. |
Example — restricting historian to read-only tools and denying bash:
{
"historian": {
"model": "github-copilot/gpt-5.4",
"tools": { "bash": false, "write": false, "edit": false },
"permission": { "bash": "deny", "webfetch": "deny" }
}
}Configures the background historian agent that compresses session history into compartments.
{
"historian": {
"model": "github-copilot/gpt-5.4",
"fallback_models": [
"anthropic/claude-sonnet-4-6",
"bailian-coding-plan/kimi-k2.5"
],
"two_pass": false
}
}| Field | Type | Description |
|---|---|---|
model |
string |
Primary model. |
fallback_models |
string or string[] |
Models to try if the primary fails or is rate-limited. |
temperature |
number (0–2) |
Sampling temperature. |
variant |
string |
Agent variant. |
prompt |
string |
Custom system prompt override. |
two_pass |
boolean |
Default false. When true, runs a second editor pass after each successful historian output. The editor (a separate hidden historian-editor agent using the same fallback chain) re-reads the draft and removes low-signal U: lines, redundant paraphrases, and cross-compartment duplicates, producing cleaner narrative-first summaries. Falls back to the draft if the editor call or its validation fails, so it can never regress behavior. Adds one extra historian-scale call per compartment publication. Recommended for non-reasoning models and open-weight local models where the single-pass draft is noisier. For models with extended thinking/reasoning enabled in OpenCode (Claude 4+, GPT-5.x reasoning variants), the single-pass output is usually already clean and two_pass can stay false. |
Configures the dreamer agent — both the model it uses and the maintenance tasks it runs. Dreamer creates ephemeral child sessions inside OpenCode for each task.
{
"dreamer": {
"enabled": true,
"model": "github-copilot/gpt-5.4",
"fallback_models": ["anthropic/claude-sonnet-4-6"],
"schedule": "02:00-06:00",
"tasks": ["consolidate", "verify", "archive-stale", "improve", "maintain-docs"]
}
}| Field | Type | Description |
|---|---|---|
model |
string |
Primary model. |
fallback_models |
string or string[] |
Fallback models. |
temperature |
number (0–2) |
Sampling temperature. |
variant |
string |
Agent variant. |
prompt |
string |
Custom system prompt override. |
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
false |
Enable scheduled dreaming. |
schedule |
string |
"02:00-06:00" |
Time window for overnight runs (24h, supports overnight like "23:00-05:00"). |
max_runtime_minutes |
number |
120 |
Max total runtime per dream session. |
task_timeout_minutes |
number |
20 |
Minutes allocated per individual task. |
tasks |
string[] |
["consolidate", "verify", "archive-stale", "improve"] |
Tasks to run, in order. |
inject_docs |
boolean |
true |
Inject ARCHITECTURE.md and STRUCTURE.md into the agent system prompt. Content is cached per-session and refreshed on cache-busting passes. |
| Task | What it does |
|---|---|
consolidate |
Find semantically duplicate memories and merge each cluster into one canonical fact. |
verify |
Check CONFIG_DEFAULTS, ARCHITECTURE_DECISIONS, and ENVIRONMENT memories against actual code. |
archive-stale |
Archive memories that reference removed features, old paths, or discontinued workflows. |
improve |
Rewrite verbose or narrative memories into terse operational statements. |
maintain-docs |
Keep ARCHITECTURE.md and STRUCTURE.md at project root synchronized with the codebase. |
An independent 15-minute timer checks the schedule regardless of user activity, so overnight dreaming triggers even when the user isn't chatting. When the current time falls inside the configured window:
- The scheduler scans the memory store for projects with activity since the last dream.
- Eligible projects are enqueued into a SQLite-backed dream queue.
- The queue consumer processes one project at a time, creating a child session per task.
/ctx-dreamalso uses the same queue — it enqueues the current project and immediately processes.
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. |
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_searchand 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.enabledistrue— memories are ordered by utility tier plusseen_countrather than semantic similarity to the current turn. - Memories written while
offis 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.
{
"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.jsonccan 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. Usedoctorafter 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.doctorwill flag 404/405 responses and show the actual error.
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. |
injection_budget_tokens |
number (500–20000) |
4000 |
Token budget for memory injection into <session-history>. |
auto_promote |
boolean |
true |
Promote eligible session facts to memory automatically after historian runs. |
retrieval_count_promotion_threshold |
number |
3 |
Retrievals needed before a memory is auto-promoted to permanent. |
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.
{
"sidekick": {
"enabled": true,
"model": "github-copilot/grok-code-fast-1",
"fallback_models": ["cerebras/qwen-3-235b-a22b-instruct-2507"],
"timeout_ms": 30000
}
}| Field | Type | Description |
|---|---|---|
model |
string |
Primary model. |
fallback_models |
string or string[] |
Fallback models. |
temperature |
number (0–2) |
Sampling temperature. |
variant |
string |
Agent variant. |
prompt |
string |
Persistent agent-level system prompt override. Applies to every sidekick run. |
| 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. |
promptvssystem_prompt:promptis the persistent agent definition applied to every sidekick run.system_promptis a per-call override injected into that specific child session — useful when a single/ctx-auginvocation needs different guidance than the default.
| Key | Type | Default |
|---|---|---|
experimental.user_memories.enabled |
boolean |
false |
experimental.user_memories.promotion_threshold |
number |
3 |
When enabled, historian extracts behavioral observations about the user alongside compartments. These are stored as candidates and reviewed by dreamer during scheduled runs. Recurring patterns that appear across multiple historian runs are promoted to stable user memories, injected into all sessions via <user-profile> in the system prompt.
Requires dreamer to be enabled. Without dreamer, candidates accumulate but are never promoted to stable memories. The doctor command warns about this misconfiguration.
promotion_threshold: minimum number of semantically similar candidate observations before dreamer considers promoting to a stable memory (2–20, default 3).
| Key | Type | Default |
|---|---|---|
experimental.pin_key_files.enabled |
boolean |
false |
experimental.pin_key_files.token_budget |
number |
10000 |
experimental.pin_key_files.min_reads |
number |
4 |
When enabled, dreamer analyzes which files each session's agent reads most frequently (full reads only, not partial line ranges). Core orientation files — architecture, config, types — that are repeatedly re-read after context drops are pinned into the system prompt as a <key-files> block. Files are read fresh from disk on each cache-busting pass.
Requires dreamer to be enabled. Without dreamer, no key files are identified. The dreamer runs the analysis as a post-task step, inspecting all active non-subagent sessions for the project.
token_budget: maximum total tokens for all pinned files combined (2000–30000, default 10000). Files are selected by a knapsack solver to fit within this budget.min_reads: minimum number of full-file reads before a file is considered for pinning (2–20, default 4). Lower values are more aggressive but risk pinning task-specific files.
| Key | Type | Default |
|---|---|---|
experimental.temporal_awareness |
boolean |
false |
When enabled, Magic Context surfaces wall-clock time to the agent in two cache-safe ways:
- User-message gap markers. Each user message is prefixed with an HTML comment like
<!-- +5m -->,<!-- +2h 15m -->, or<!-- +3d 4h -->indicating time elapsed since the previous message's completion. Only shown when the gap exceeds 5 minutes. Derived from immutablemessage.time.completed ?? message.time.createdtimestamps. - Compartment date ranges. Each
<compartment>element in<session-history>carriesstart-date="YYYY-MM-DD"andend-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.
| 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 <start>-<end> |
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. |
{
"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,
"auto_drop_tool_age": 50,
"drop_tool_structure": true,
"history_budget_percentage": 0.15,
"compaction_markers": true,
"compressor": {
"enabled": true,
"min_compartment_ratio": 1000,
"max_merge_depth": 5,
"cooldown_ms": 600000
},
"historian": {
"model": "github-copilot/gpt-5.4",
"fallback_models": ["anthropic/claude-sonnet-4-6"]
},
"dreamer": {
"enabled": true,
"model": "github-copilot/gpt-5.4",
"fallback_models": ["anthropic/claude-sonnet-4-6"],
"schedule": "02:00-06:00",
"tasks": ["consolidate", "verify", "archive-stale", "improve", "maintain-docs"]
},
"embedding": {
"provider": "local"
},
"memory": {
"enabled": true,
"injection_budget_tokens": 4000,
"auto_promote": true
},
"sidekick": {
"enabled": true,
"model": "github-copilot/gpt-5.4",
"fallback_models": ["anthropic/claude-sonnet-4-6"],
"timeout_ms": 30000
},
"experimental": {
"user_memories": {
"enabled": false,
"promotion_threshold": 3
},
"pin_key_files": {
"enabled": false,
"token_budget": 10000,
"min_reads": 4
}
}
}
{ "$schema": "https://raw.githubusercontent.com/cortexkit/opencode-magic-context/master/assets/magic-context.schema.json" }