SalmonLoop supports repository-local JSON configuration.
By default, SalmonLoop looks for a config file at:
<repoRoot>/.salmonloop/config/config.json
/.salmonloop/ is intended to be local-only and should be gitignored.
This repository also includes a config.example.json at the project root as a starting point.
The precedence order is:
- Defaults
- Config file (JSON)
- Environment variables
- CLI flags
SalmonLoop can expose an A2A HTTP server plus ACP session persistence settings.
Configuration lives under the server top-level key.
Controls the A2A HTTP listener and optional token-based auth.
host: Host/interface to bind (default:127.0.0.1).port: TCP port to listen on (default:7431).tokens: Optional list of bearer tokens that are accepted by the A2A server. If empty, no token check is enforced.
Example:
{
"server": {
"a2a": {
"host": "127.0.0.1",
"port": 7431,
"tokens": ["dev-token"]
}
}
}Controls ACP session persistence retention and lock policy.
maxEntries: max persisted sessions (default:200).maxAgeMs: drop sessions older than this age (default:2592000000= 30 days).historyMaxEntries: max persisted history entries per session (default:40).lockStaleMs: lock stale threshold before reclaim (default:30000).lockHeartbeatMs: lock heartbeat interval while holding lock (default:5000).
Example:
{
"server": {
"acp": {
"sessionStore": {
"maxEntries": 500,
"maxAgeMs": 1209600000,
"historyMaxEntries": 80,
"lockStaleMs": 45000,
"lockHeartbeatMs": 3000
}
}
}
}Controls checkpoint manifest lock policy.
lockStaleMs: lock stale threshold before reclaim (default:30000).lockHeartbeatMs: lock heartbeat interval while holding lock (default:5000).
Example:
{
"server": {
"acp": {
"checkpointManifest": {
"lockStaleMs": 45000,
"lockHeartbeatMs": 3000
}
}
}
}Controls churn-aware target ranking layers:
primary: fixed boost applied to the primary target layer.rerank: churn contribution in the main ranking score.tiebreak: churn contribution only when final scores are tied.
Example:
{
"context": {
"churn": {
"weight": {
"primary": 10000,
"rerank": 0.35,
"tiebreak": 0.05
}
}
}
}Notes:
- Keep
primarymuch larger than semantic scores to guarantee deterministic primary-first behavior. - Keep
rerankmoderate (0.2-0.5) so churn improves ordering without overriding semantic intent. - Keep
tiebreaksmall (0-0.1) to avoid instability.
SalmonLoop uses canonical signatures for context consistency:
intentSignature: derived from instruction, primary file hint, selection, and diff scope.targetSetSignature: derived from resolved target list (path/reason/confidence/ranking).contextHash: canonical hash of final packed context content.- Signatures are versioned (
intent:v1:*,targets:v1:*,context:v1:*) for forward-compatible algorithm upgrades.
These signatures are emitted in context audit events to make cache hits/misses explainable.
When context.cache.mode is persistent, SalmonLoop resolves context.cache.path and enforces
that it stays under at least one configured context.cache.allowedRoots entry.
Example:
{
"context": {
"cache": {
"mode": "persistent",
"path": ".salmonloop/cache/context-cache.json",
"allowedRoots": [".salmonloop/cache"]
}
}
}Safety behavior:
- Default is fail-closed (
PERMISSION_DENIED_CONTEXT_CACHE_OUTSIDE_ROOT). - Canonical path checks also reject symlink-based escapes.
- One-off CLI override is available via
--allow-outside-cache-root(high risk).
Sets an upper bound (in bytes) for the persistent context cache payload.
Example:
{
"context": {
"cache": {
"maxPayloadBytes": 5242880
}
}
}Notes:
- When exceeded, persistent cache will fall back to memory (if configured) and emit an audit event.
- This helps avoid large cache files with sensitive context.
--config <path>: Explicitly load a config file (relative paths are resolved against the repo root).--no-config-file: Disable loading the repo config file.--print-config: Print the resolved config (redacted) and exit.
SalmonLoop separates what the TUI shows (mode) from how dense it renders (view).
Controls log visibility and summarization in the TUI:
quiet: Minimal output. Keep warnings/errors and essential phase milestones.normal: Default. Balanced output for new users.debug: Maximum detail, including debug-level messages and tool call details.
Example:
{
"ui": {
"log": {
"mode": "normal"
}
}
}Controls where audit logs are written.
repo(default):<repoRoot>/.salmonloop/runtime/audit/user:~/.salmonloop/runtime/audit/
Example:
{
"observability": {
"audit": {
"scope": "user"
}
}
}Limits in-memory audit trail growth for long-running sessions.
Example:
{
"observability": {
"audit": {
"buffer": {
"maxEvents": 10000,
"maxBytes": 20971520,
"droppedWarn": 100
}
}
}
}Notes:
- When limits are exceeded, low-severity events are dropped first.
- A summary
audit.droppedevent is emitted once space is available. - If
droppedWarnis exceeded, anaudit.dropped.warnevent is emitted.
Controls sensitive-data redaction for audit logs and cache payloads.
Example:
{
"security": {
"redaction": {
"enabled": true,
"mark": "[REDACTED]",
"maxDepth": 6,
"keyDenylist": ["secret", "token"],
"patterns": ["secret-[a-z]+"],
"disableDefaults": false
}
}
}Notes:
- Redaction is enabled by default.
- Disabling redaction is not recommended in shared or regulated environments.
keyDenylistforces full redaction for matching keys.patternsadds custom regex rules (applied to string values).disableDefaultsdisables built-in redaction patterns.
Environment variable overrides (preferred):
SALMONLOOP_UI_LOG_MODE:quiet|normal|debugSALMONLOOP_UI_MODE: alias
Controls rendering density:
compact: Most compact layout.standard: Default layout.full: Most verbose layout (more labels/timestamps/spacing).
Example:
{
"ui": {
"log": {
"view": "standard"
}
}
}Environment variable overrides:
SALMONLOOP_UI_LOG_VIEWSALMONLOOP_UI_LOGSALMONLOOP_UI_DENSITY
Notes:
- If
ui.log.viewis not set, SalmonLoop derives a default fromui.log.mode:quiet->compactnormal->standarddebug->full
- Setting
ui.log.viewexplicitly always wins over the derived default.
Minimal example:
{
"version": 1,
"llm": {
"active": "openaiMain",
"providers": {
"openaiMain": {
"type": "openai-compatible",
"client": {
"package": "@ai-sdk/openai"
},
"api": {
"baseUrl": "https://api.openai.com/v1",
"apiKey": null,
"timeoutMs": 60000,
"headers": {}
},
"models": {
"default": {
"id": "gpt-4.1-mini"
}
}
}
}
}
}Notes:
api.apiKeycan be stored inline for convenience, but it is sensitive. Keep.salmonloop/gitignored.- If
api.apiKeyis not set, SalmonLoop falls back to environment variables:SALMONLOOP_API_KEY(preferred)S8P_API_KEY(legacy)
SalmonLoop can:
- correlate LLM calls into a single Langfuse trace (
run-xxxx) via Langfuse headers - report run outcome (
metadata.salmonloop.*+ numeric scores) for success-rate tracking
Config example:
{
"observability": {
"langfuse": {
"enabled": true,
"outcome": true,
"endpoint": "https://your-litellm-host/langfuse/",
"apiKey": "langfuse-proxy-key",
"userId": "user-123"
}
}
}Notes:
observability.langfuse.endpointis the LiteLLM Langfuse proxy endpoint (not the Langfuse Cloud host).- If
endpointis omitted, SalmonLoop derives the LiteLLM root fromllm.providers.*.api.baseUrlby stripping/v1. - If the derived root host is a known public LLM provider host (OpenAI/Anthropic/Gemini), outcome reporting is disabled unless
you explicitly set
observability.langfuse.endpoint. enabledcontrols Langfuse correlation headers on LLM calls (spans/tokens).outcomecontrols the end-of-run ingestion request (scores +metadata.salmonloop.*). You can enable either independently.observability.langfuse.apiKeyis an optional auth key used for the outcome ingestion call to the LiteLLM/langfuse/*proxy route. Configure a dedicated key for the proxy; do not reuse the active LLM provider apiKey.- If
sessionIdis omitted, chat mode auto-uses the local chat session ID; run mode leaves it unset. sessionIdis mainly an override for special cases (e.g. CI batch runs / eval suites where you want many runs grouped); in normal interactive chat you should not set it manually.
Environment variable overrides:
SALMONLOOP_LANGFUSE: overrideobservability.langfuse.enabledSALMONLOOP_LANGFUSE_OUTCOME: overrideobservability.langfuse.outcomeSALMONLOOP_LANGFUSE_PROXY_URL: overrideobservability.langfuse.endpoint(can be either a root URL or a full/langfuse/endpoint)SALMONLOOP_LANGFUSE_API_KEY: overrideobservability.langfuse.apiKey(optional; used for outcome ingestion auth)SALMONLOOP_LANGFUSE_SESSION_ID: overrideobservability.langfuse.sessionIdSALMONLOOP_LANGFUSE_USER_ID: overrideobservability.langfuse.userIdSALMONLOOP_LANGFUSE_RELEASE: optional release string attached to traces (useful for regressions)
llm.providers.<key>.client.package selects the LLM client backend used to communicate with the provider.
If omitted, SalmonLoop uses its default internal client selection logic.
Supported values (current):
@ai-sdk/openai@ai-sdk/openai-compatible
Behavior:
- If
client.packageis supported, SalmonLoop uses the corresponding AI SDK provider client. - If
client.packageis set but not supported, SalmonLoop prints a warning and falls back to the default client.
Safety note:
client.packageonly affects the LLM transport/adapter layer. It does not change tool governance, file access rules, or the execution safety contract.
llm.providers.<key>.api.timeoutMs controls the LLM request timeout.
Notes:
- This value is used by the AI SDK transport when
client.packageselects an AI SDK adapter. - If
client.packageis omitted and SalmonLoop uses its default client logic, timeout behavior may differ by backend.
SALMONLOOP_API_KEY/S8P_API_KEY: API key fallback if not present in config.SALMONLOOP_BASE_URL/S8P_BASE_URL: Base URL fallback if not present in config. PreferSALMONLOOP_BASE_URLand omit the trailing slash (e.g.,https://openrouter.ai/api/v1); the runtime trims extra slashes.SALMONLOOP_MODEL/S8P_MODEL: Model choice (SALMONLOOP_MODEL preferred).
output.markdown.theme controls the Markdown theme used in the TUI for chat/run modes.
output.markdown.mode controls rendering behavior:
enhanced(default): SalmonLoop-enhanced rendering with line numbers and defensive formatting fixes.native: vanillamarked-terminalbehavior.
Supported values:
default(built-in marked-terminal theme)vivid(higher-contrast theme used by SalmonLoop)
Example:
{
"output": {
"markdown": {
"theme": "default",
"mode": "enhanced"
}
}
}For guidance on configuring MCP servers, tool plugins, and skill directories, see Extension configuration. That doc walks through the new .salmonloop/config/*.json files, scopes, and the effective extensions payload that SalmonLoop resolves before each run.