Skip to content
Crystal CodeDocs
ENEnglish

Configuration

Host settings live in ~/.crystal/config.json. Provider and model definitions live in ~/.crystal/providers.json. The built-in DeepSeek and OpenAI catalog is available even when that file is absent. Your definitions overlay it. Edit the file you changed, then restart. Editing provider definitions in the TUI is not available. <workspace>/.crystal/config.json is not read.

providers.json What is read
Present The operator catalog. The legacy config.json.providers field is ignored
Absent The legacy field, including apiKey and apiKeyEnvironment

Saving preferences does not rewrite an existing providers.json. If that file is absent and config.json still has a providers object, the save copies it to providers.json and keeps the original field. Later edits belong in providers.json.

Top-level fields:

Field Meaning
provider Active provider name
model Active model id (must exist under that provider)
approval default, edit, review, audit, or full
approvalModel Optional reviewer. enabled, provider, model, and optional thinkingEffort. Omitted means off. enabled: false keeps a stored provider, model, and gear unused
compactionModel Optional summarizer. enabled, provider, model, and optional thinkingEffort (default, off, or a gear that model lists). Omitted means off, and summaries use the session model. enabled: false keeps a stored provider, model, and gear unused. The model does not need imageInput. Opening it requires a catalog model; a missing credential or unknown model is refused and the saved value stays
imageModel Optional vision model for describe_image. provider, model, and optional thinkingEffort. Omitted means the tool is absent. The model must have imageInput. Thinking capability stays on the model entry
thinkingEffort Host thinking gear: default, off (none is the same), or an effort token the model lists. max and maximum are different tokens. Tokens are stored in lowercase and sent unchanged
contextSelections Saved local context budgets as a nested map, for example {"openai": {"my-model": 272000}}. /context writes this map; the older flat {"openai/my-model": 272000} form is still read and is rewritten nested on the next save. An absent or unsupported choice uses that model’s lowest declared budget
tierSelections Saved service-tier tokens as a nested map, for example {"openai": {"my-model": "flex"}}. /tier writes this map. An absent choice, or a token the model no longer lists, omits the field
speedSelections Saved speed tokens in the same shape as tierSelections. /speed writes this map
skills Enable the skill tool and available-skill guidance (default true)
ruleFiles OpenCode-compatible rule files. global and project each default to true and are omitted until off. Does not affect instructions.md or .crystal.md
externalTools Enable operator tool set discovery (default true)
plugins Enable operator plugin discovery (default true)
workspaceTrust Ask before the first interactive session in a directory (default true; omitted when on). Does not apply to crystal run
externalToolApproval Per-source trust for tool-set author declarations: home and project, each author or host (defaults Home author, Project host)
estimatedTokens Show a live four-characters-per-token estimate on the progress row during Thinking and Writing (default false)
verboseResults Parent switch for success result panels (default true; omitted when on). Off hides every success result already on screen, including read, search, skill, edit, write, bash, and background bash. Does not hide Tool cards; those follow verboseCalls. Failures stay. verboseTools and verboseCommands do not draw results while this is off. crystal run does not read it
verboseCalls Show Tool cards (default true; omitted when on). Off hides every Tool card already on screen: read, search, edit, write, bash, background bash, and external tools such as Apply Patch. Success and failure results stay. Independent of verboseResults, verboseTools, and verboseCommands. crystal run does not read it
verboseTools While verboseResults is on, show read, search, and skill result panels (default true; omitted when on). Edit and write results stay. No visible effect while verboseResults is off
verboseCommands While verboseResults is on, show full output from bash, bash_start, bash_status, bash_logs, and bash_stop (default true; omitted when on). Off keeps a hidden-line hint and the last line. No visible effect while verboseResults is off
verboseApprovals Show policy and grant auto-pass cards (default true; omitted when on). Off hides those cards already on screen. Review allow cards follow verboseReview. The ask overlay stays
verboseReview Show review allow cards (default true; omitted when on). Off hides those cards already on screen, including Outcome and rationale. Independent of verboseApprovals. The ask overlay, reviewer Deny text, and crystal run stay
verboseThinking Show Thinking panels in the interactive transcript (default true; omitted when on). Off hides live and restored cards. The gear, progress row, and export stay. crystal run --show-thinking is separate
exportDirectory Export root: omitted or home for {home}/exports, workspace for <workspace>/.crystal/exports, or any absolute/~ path
customStatusLine Enable the ordered custom status line (default false; the existing adaptive status line remains the default)
statusLine Ordered custom fields used only when customStatusLine is enabled. Built-in names are checked on load. An extra name is kept when it matches a plugin field name, even if that plugin is not loaded yet
compactionThreshold Fraction of the selected context budget that triggers compaction (greater than 0, at most 1; default 0.8)
showCompactionSummary Print the summary after a manual /compact (default true; omitted when on). /compact summary toggles it; on or off sets it
showAutomaticCompactionSummary Print the same summary after automatic compaction (default false; omitted when off). /compact auto toggles it (automatic is the same); on or off sets it. Does not change /compact
microCompaction Optional removal of old successful tool results before each model call. Omitted means off. Object with enabled (default false), protectTurns (at least 1, default 2), protectTokens (non-negative, default 40000), and minimumTokens (non-negative, default 20000). Invalid values stop startup. /compact micro toggles it; on or off sets it. Does not summarize
bashTimeoutSeconds Built-in bash per-command timeout. Omitted keeps 600 seconds. A positive integer up to 4,294,967 replaces it. null or "unlimited" disables that timer; cancelling the turn still stops the command
backgroundBashTimeoutSeconds bash_start background task timeout. Omitted keeps 10800 seconds (3 hours). A positive integer up to 4,294,967 replaces it. null or "unlimited" disables that timer; bash_stop and session shutdown still stop the task
retry Work-turn and compaction retry policy. Object with enabled (default true; false disables retries), maximumRetries (0-100, default 5), initialDelaySeconds (0-3600, default 2), backoffFactor (1-100, default 2), jitterFactor (0-1, default 0.25), and maximumDelaySeconds (0-86400, default 30; the backoff cap when Retry-After is absent). Invalid values stop startup. Work retries publish RetryScheduled
approvalRetry The same object shape for the Review and Audit approval reviewer. Independent of retry. Reviewer retries are silent; exhausted retries fall back to your approval prompt

Add named endpoints and model tables in providers.json, not in new config.json configurations.

There is no global context window. Models that are not listed cannot be selected.

Starter catalog (merged with providers.json):

Provider Protocol Default base URI Starter models
deepseek deepseek https://api.deepseek.com/ deepseek-flash, deepseek-v4-flash (compatibility alias), deepseek-v4-pro
openai openai https://api.openai.com/v1/ gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna
gemini gemini https://generativelanguage.googleapis.com/v1beta/ gemini-3.8-flash, gemini-3.1-pro-preview
ollama ollama http://localhost:11434/ qwen3:8b (pull this model before selecting it)

Built-in DeepSeek V4 models enable thinking with efforts low, high, and max. Each has a 1,000,000-token context window. Starter OpenAI models use a 400,000-token window and do not enable thinking unless you add it. The Ollama starter assumes a 4,096-token local context. If the local model uses a different context size, set that size in providers.json and configure the Ollama model to match it. Other local or cloud model IDs may be added there. Automatically fetching model-list information from a provider is planned and is not available in this build.

Anthropic Messages is also a built-in protocol, but the starter catalog ships no entry for it, so Claude models come from providers.json. See Example: add an Anthropic (Claude) provider.

Field Meaning
protocol deepseek, openai, responses, anthropic, gemini, or ollama
baseUri Absolute API base URI; the adapter appends the protocol’s chat path
organization Optional OpenAI organization for the openai protocol
project Optional OpenAI project for the openai protocol
replayReasoningContent Replay provider reasoning content (DeepSeek always replays it); reasoning that cannot be replayed keeps its readable text (assistant text, or the protocol’s readable reasoning field)
tokenLimit Chat Completions output field: max_tokens or max_completion_tokens (ignored by responses and anthropic)
apiKeyEnvironment Environment variable name checked first for this provider
apiKey Literal, {env:NAME}, or {file:path}
requiresApiKey Whether a missing key is an error (default true, except ollama; set false for an unauthenticated endpoint)
options Optional object of protocol-specific settings. Values are JSON strings, numbers, or booleans. Unknown names, and names that do not apply to protocol, fail loading. temperature, topP, and maxTokens stay on the model
models Table of selectable model ids

Provider names are letters, digits, hyphen, or underscore.

All protocol adapters send the constant User-Agent: Crystal Code, with no version. responses authenticates with Authorization: Bearer; anthropic uses x-api-key and anthropic-version: 2023-06-01. Both adapters use direct HTTP and JSON/SSE handling; no provider SDK is required. Gemini uses x-goog-api-key and native generateContent SSE. Ollama uses native /api/chat JSON lines and omits authorization when no key is configured. For a provider with requiresApiKey: false, an explicit provider key or its specific environment variable is still used; CRYSTAL_API_KEY is skipped.

Amazon Bedrock models that support its OpenAI-compatible Chat Completions API can be configured with protocol: "openai", a regional https://bedrock-runtime.REGION.amazonaws.com/openai/v1/ base URI, and a Bedrock API key. This path uses bearer authentication. IAM SigV4 credentials are not supported by the current adapter.

Field Meaning
contextWindow Required positive maximum model context size in tokens. Upper bound for selectable local budgets; used as the sole budget when contextWindows is omitted or empty
contextWindows Optional distinct positive token budgets no greater than contextWindow, for example [272000, 1000000]. /context cycles these values; the lowest is the initial budget. Compaction and the status bar use the selected budget
temperature Optional, 0 to 2
topP Optional, 0 to 1
maxTokens Optional positive output-token cap
thinking Whether the model accepts reasoning hints
thinkingEfforts Effort tokens this endpoint accepts. A token starts with a letter and then uses only letters, digits, underscores, and hyphens. max and maximum are different tokens. The host stores each token in lowercase and sends it unchanged
thinkingCanDisable Whether /thinking off is accepted (default true). Built-in Gemini 3 models set this to false
serviceTiers Service-tier tokens this model accepts. A token starts with a letter and then uses only letters, digits, underscores, and hyphens. The host stores each token in lowercase and does not interpret it. default is sent when it is listed; it does not mean unset. An empty or omitted list means this model has no service tier
speeds Speed tokens this model accepts, using the same token rules. fast and ultrafast belong here when the endpoint accepts them. An empty or omitted list means this model has no speed
imageInput Whether this model may receive images (default false; supported by all built-in protocols)
audioInput Declared audio input (default false). Stored and visible to plugins. The host does not accept or send audio
videoInput Declared video input (default false). Stored and visible to plugins. The host does not accept or send video

audioInput and videoInput only record the claim. Crystal Code stores them and shows them to plugins. It does not attach, paste, persist, or send audio or video, and those flags do not register multimodal tools. imageInput is still the only flag that allows image input. Service tier and speed are sent only when the active model lists the selected token. The host does not choose the JSON field and does not compare the response with the request. Chat Completions and OpenAI Responses write one set slot to service_tier and reject both when the client is created, before the choice is saved. Anthropic Messages writes service_tier and speed separately. DeepSeek, Gemini, and Ollama reject either slot when the client is created. A saved combination the protocol cannot send fails at startup. The endpoint address is not used to decide.

Gemini and Ollama native adapters accept inline images in user messages. Tool images are sent on Gemini functionResponse parts and on the Ollama tool message images array. Gemini tool images require a model that accepts multimodal function responses.

thinkingEffort is a host setting, not a model field. Changing models never fails: if the model does not support thinking, requests omit reasoning hints; if the stored gear is not in that model’s list, the request uses the provider default and the stored choice is unchanged. The host stores a listed token in lowercase and forwards that text. Adapters send the token unchanged. max and maximum are different tokens. openai and responses are also used for third-party gateways such as OpenRouter, so a catalog value is not renamed to xhigh. List xhigh, max, or maximum only when that endpoint accepts that spelling. Gemini 3 uppercases the token as thinkingLevel. Gemini 2.5 maps minimal, low, medium, and high to thinking budgets and rejects any other token. Anthropic requests summarized thinking text when thinking is on, unless that endpoint sets options.thinkingDisplay to omitted. An empty thinkingEfforts list is on/off only. When thinkingCanDisable is false, Off is left out of the gear. A stored Off choice uses the provider default and the status bar shows Think Default.

Example: add an OpenAI-compatible provider

Section titled “Example: add an OpenAI-compatible provider”

Add this entry to ~/.crystal/providers.json. Keep the secret out of this file. Write it in ~/.crystal/credentials.json under openrouter.

{
"openrouter": {
"protocol": "openai",
"baseUri": "https://openrouter.ai/api/v1/",
"replayReasoningContent": true,
"tokenLimit": "max_tokens",
"models": {
"anthropic/claude-sonnet-4": {
"contextWindow": 200000,
"temperature": 0.2,
"maxTokens": 8192,
"thinking": true,
"thinkingEfforts": ["low", "medium", "high"]
}
}
}
}

OPENROUTER_API_KEY overrides that file when the process environment sets it. Restart after editing providers.json. Select the model with /model openrouter anthropic/claude-sonnet-4, or set provider and model in config.json. CLI --provider and --model override that selection for one run; /approval (including /approval model and /approval thinking), /thinking, /context, /tier, /speed, and /model write their values to config.json. /context stores each model’s selected budget in contextSelections. /tier and /speed store each model’s token in tierSelections and speedSelections.

Example: add an Anthropic (Claude) provider

Section titled “Example: add an Anthropic (Claude) provider”

Anthropic Messages is a built-in protocol, but the starter catalog ships no Anthropic entry, so Claude models are reached by adding one to ~/.crystal/providers.json. Write the key in ~/.crystal/credentials.json under anthropic.

{
"anthropic": {
"protocol": "anthropic",
"baseUri": "https://api.anthropic.com/v1/",
"models": {
"claude-sonnet-4": {
"contextWindow": 200000,
"maxTokens": 8192,
"thinking": true,
"thinkingEfforts": ["low", "medium", "high"],
"imageInput": true
}
}
}
}

ANTHROPIC_API_KEY overrides that file for one process. Restart after editing providers.json, then select the model with /model anthropic claude-sonnet-4, or set provider and model in config.json. The same entry reaches any Anthropic Messages gateway: change baseUri and the model id to match it. List the endpoint token in thinkingEfforts, such as max or xhigh. The adapter sends that token unchanged, and it asks for summarized thinking text whenever thinking is on. Set options.thinkingDisplay to omitted on this endpoint to leave the Anthropic display field off. summarized is the default and is not written back. The name is valid only for the anthropic protocol. Omit temperature and topP for models that reject sampling parameters. Thinking tokens count toward maxTokens.

Example: add OpenCode Zen protocol endpoints

Section titled “Example: add OpenCode Zen protocol endpoints”

One gateway can group models that use different wire protocols under one provider name. Put this example in providers.json. The key for both endpoints belongs in credentials.json under opencode-zen. OPENCODE_ZEN_API_KEY overrides that file for one process.

{
"opencode-zen": [
{
"protocol": "responses",
"baseUri": "https://opencode.ai/zen/v1/",
"models": {
"gpt-5.6-sol": {
"contextWindow": 1050000,
"maxTokens": 128000,
"thinking": true,
"thinkingEfforts": ["low", "medium", "high", "maximum"],
"imageInput": true
}
}
},
{
"protocol": "anthropic",
"baseUri": "https://opencode.ai/zen/v1/",
"models": {
"claude-sonnet-5": {
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": true,
"thinkingEfforts": ["low", "medium", "high", "maximum"],
"imageInput": true
}
}
}
]
}

Add a protocol: "openai" object to the same array for Chat Completions models. Each model id must be unique within a provider. Listed efforts are sent as written. maximum is not rewritten to xhigh or max.