Settings

July 26, 2026 · View on GitHub

Senpi uses JSON settings files with project settings overriding global settings.

LocationScope
~/.senpi/agent/settings.jsonGlobal (all projects)
.senpi/settings.jsonProject (current directory)

Edit directly or use /settings for common options.

Project Trust

On interactive startup, senpi asks before trusting a project folder that contains project-local settings, resources, or project .agents/skills and has no saved decision for the folder or a parent folder in ~/.senpi/agent/trust.json. Trusting a project allows senpi to load .senpi/settings.json and .senpi resources, install missing project packages, and execute project extensions.

Non-interactive modes (-p, --mode json, and --mode rpc) do not show a trust prompt. Without an applicable saved trust decision, they use defaultProjectTrust from global settings: ask (default) and never ignore those project resources, while always trusts them. Pass --approve/-a or --no-approve/-na to override project trust for one run.

If no extension or saved decision applies, defaultProjectTrust controls the fallback behavior. Set it to "ask", "always", or "never" in ~/.senpi/agent/settings.json, or change it with /settings.

senpi config and package commands use the same project trust flow, except senpi update never prompts. Pass --approve to trust project-local settings for one command or --no-approve to ignore them.

Use /trust in interactive mode to save a project trust decision for future sessions, including trust for the immediate parent folder. It writes ~/.senpi/agent/trust.json only; the current session is not reloaded, so restart senpi for changes to take effect.

Permissions

Senpi includes a built-in permission system for tool calls. It evaluates a preset first, then applies explicit rules from global settings, project settings, and CLI flags. The last matching rule wins.

SettingTypeDefaultDescription
permissionPresetstring"full-access"Permission preset: "full-access", "workspace", "read-only", or "ask"
permissionobject-Explicit permission rules that override the selected preset

Presets:

PresetBehavior
full-accessAllow all permission checks without prompting
workspaceAllow read, list, grep, edit, and bash; ask for external_directory
read-onlyAllow read, list, and grep; ask for edit, bash, and external_directory
askRestore prompt-on-unknown behavior

Example:

{
  "permissionPreset": "workspace",
  "permission": {
    "bash": {
      "rm *": "deny"
    },
    "edit": {
      "secrets/*": "ask"
    }
  }
}

Flat rules apply to all patterns for that permission:

{
  "permissionPreset": "read-only",
  "permission": {
    "bash": "deny"
  }
}

CLI overrides have the highest precedence:

senpi --permission-preset ask
senpi --permission-preset workspace --permission "bash:rm *=deny"

Permission rules are a confirmation policy, not a sandbox. Senpi, extensions, package installs, and child processes still run with the host process permissions.

All Settings

Model & Thinking

SettingTypeDefaultDescription
defaultProviderstring-Default provider (e.g., "anthropic", "openai")
defaultModelstring-Default model ID
defaultThinkingLevelstring-"off", "minimal", "low", "medium", "high", "xhigh", "max"
promptPresetstring"auto"Force a system prompt preset: "auto", "kimi-k2-6", "kimi-k2-7", "kimi-k3", "glm-5.2", "claude-fable-5", "claude-opus-5", "claude-opus-4-5", "claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8", "gpt-5", "gpt-5.2", "gpt-5.3-codex", "gpt-5.4", "gpt-5.5", or "gpt-5.6"
hideThinkingBlockbooleanfalseHide thinking blocks in output
showCacheMissNoticesbooleanfalseShow transcript notices for significant prompt-cache misses
thinkingBudgetsobject-Custom token budgets per thinking level

promptPreset

Use promptPreset when a provider's model ID does not auto-detect to the preset you want, or when you want to force one preset for a project.

{
  "promptPreset": "kimi-k2-6"
}

Project settings in .senpi/settings.json override global settings in ~/.senpi/agent/settings.json. When this value is anything other than "auto", it overrides any model-level promptPreset configured in models.json.

thinkingBudgets

{
  "thinkingBudgets": {
    "minimal": 1024,
    "low": 4096,
    "medium": 10240,
    "high": 32768
  }
}

UI & Display

SettingTypeDefaultDescription
themestring"dark"Theme name ("dark", "light", or custom)
externalEditorstring$VISUAL, then $EDITOR, then Notepad on Windows or nano elsewhereCommand for Ctrl+G external editor; takes precedence over environment variables
quietStartupbooleanfalseHide startup header
defaultProjectTruststring"ask"Fallback project trust behavior: "ask", "always", or "never". Global setting only
collapseChangelogbooleanfalseShow condensed changelog after updates
enableInstallTelemetrybooleantrueSend an anonymous install/update version ping after first install or changelog-detected updates. This does not control update checks
enableAnalyticsbooleanfalseOpt-in analytics data sharing. Currently only asked for during the experimental first-time setup (PI_EXPERIMENTAL=1)
trackingIdstring-Analytics tracking identifier, generated when enableAnalytics is turned on
doubleEscapeActionstring"tree"Action for double-escape: "tree", "fork", or "none"
treeFilterModestring"default"Default filter for /tree: "default", "no-tools", "user-only", "labeled-only", "all"
editorPaddingXnumber0Horizontal padding for input editor (0-3)
outputPadnumber1Horizontal padding for user messages, assistant messages, and thinking (0 or 1)
autocompleteMaxVisiblenumber5Max visible items in autocomplete dropdown (3-20)
showHardwareCursorbooleanfalseShow the terminal cursor while TUI positions it for IME support

For VS Code, include --wait so pi resumes after the editor exits:

{
  "externalEditor": "code --wait"
}

Telemetry and update checks

enableInstallTelemetry only controls the anonymous install/update ping to https://pi.dev/api/report-install. Opting out of telemetry does not disable update checks; senpi can still fetch the latest published @code-yeongyu/senpi version from the npm registry (registry.npmjs.org).

Set PI_SKIP_VERSION_CHECK=1 to disable the senpi version update check. Use --offline or PI_OFFLINE=1 to disable all startup network operations described here, including update checks, package update checks, and install/update telemetry.

Network

SettingTypeDefaultDescription
httpProxystring-HTTP proxy URL applied as HTTP_PROXY and HTTPS_PROXY. Global setting only.
{
  "httpProxy": "http://127.0.0.1:7890"
}

Warnings

SettingTypeDefaultDescription
warnings.anthropicExtraUsagebooleantrueShow a warning when Anthropic subscription auth may use paid extra usage
{
  "warnings": {
    "anthropicExtraUsage": false
  }
}

Compaction

SettingTypeDefaultDescription
compaction.enabledbooleantrueEnable auto-compaction
compaction.reserveTokensnumber16384Tokens reserved for LLM response
compaction.keepRecentTokensnumber20000Recent tokens to keep (not summarized)
{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}

Branch Summary

SettingTypeDefaultDescription
branchSummary.reserveTokensnumber16384Tokens reserved for branch summarization
branchSummary.skipPromptbooleanfalseSkip "Summarize branch?" prompt on /tree navigation (defaults to no summary)

Retry

SettingTypeDefaultDescription
retry.enabledbooleantrueEnable automatic agent-level retry on transient errors
retry.maxRetriesnumber3Maximum agent-level retry attempts
retry.baseDelayMsnumber2000Base delay for agent-level exponential backoff (2s, 4s, 8s)
retry.modelFallbackbooleantrueLet eligible retry failures advance through configured per-model fallback chains
retry.fallbackChainsRecord<string, string[]>{}Ordered exact model-selector to fallback-selector chains
retry.fallbackRevertPolicy"cooldown-expiry" | "never""cooldown-expiry"Automatic primary-model restoration policy
retry.abortServerSideFallbackbooleantrueAbort a turn when the provider substitutes a different model after a classifier decline
retry.provider.timeoutMsnumber300000Provider/SDK request timeout and stream idle timeout in milliseconds
retry.provider.maxRetriesnumber0Provider/SDK retry attempts
retry.provider.maxRetryDelayMsnumber60000Max server-requested delay honored on the same model before the fallback chain engages (60s)

A server-requested retry delay at or below retry.provider.maxRetryDelayMs is honored on the same model. A longer delay means the model is unavailable rather than busy, so Senpi engages the configured fallback chain instead of waiting, suppressing the primary for the requested duration; the turn fails with an informative error only when no chain candidate can take over.

Keep retry.provider.maxRetries at 0 unless provider-level retries are explicitly needed. Setting it above 0 can make SDK/provider retries handle out-of-usage-limit errors before senpi sees them, which may block the agent until the provider quota resets in some circumstances.

{
  "retry": {
    "enabled": true,
    "maxRetries": 3,
    "baseDelayMs": 2000,
    "provider": {
      "timeoutMs": 3600000,
      "maxRetries": 0,
      "maxRetryDelayMs": 60000
    }
  }
}

Model fallback chains

retry.fallbackChains maps one exact primary-model selector to an ordered list of fallback selectors. A selector is provider/model with an optional :thinking-level suffix. For example, this switches Fable 5 to Kimi K3 at max thinking when an eligible failure occurs:

{
  "retry": {
    "modelFallback": true,
    "fallbackChains": {
      "anthropic/claude-fable-5": ["ccapi/kimi-k3:max"]
    },
    "fallbackRevertPolicy": "cooldown-expiry"
  }
}

A chain is only for the exact primary model it names: selector lookup first considers an exact thinking-level selector, then the same provider/model without its thinking suffix. Wildcard selectors, role keys such as default, and other catch-all chains are not supported.

A fallback entry with :thinking-level requests that level on the target model; a bare entry inherits the current thinking level. Either value is clamped to the target model's supported levels. When an unpinned fallback later returns to the primary, it restores the original thinking level unless you changed it while using the fallback.

/fallback writes these settings to the global settings file. Project settings are still merged when read; because fallbackChains is a nested map, a project retry.fallbackChains replaces the global map rather than merging individual chain keys.

Fallback behavior and diagnostics

With retry.enabled and retry.modelFallback enabled, Senpi can switch from a transient or eligible hard provider failure to the next configured candidate. Transient failures (timeouts, overload, 429, 5xx, transport drops) first retry the same model on the existing exponential backoff; the chain engages only after retry.maxRetries attempts are spent, and each fallback candidate starts with a fresh retry budget. Hard failures (quota, auth, model-not-found) and classifier refusals still switch immediately. The switch continues the current turn without changing the existing conversation prefix, preserving prompt-cache inputs; fallback lifecycle events are never added to model context. Returning to a primary model happens only at a turn boundary, never while a response is streaming. Selector cooldowns are error-derived, and a provider retry-after hint always wins: quota and billing failures park a model for 30 minutes, rate limits for 30 seconds, overload for 45 seconds plus jitter, 5xx for 20 seconds, and timeout or connection/transport failures for 60 seconds; unmatched failures default to five minutes. A fully failing chain costs up to 1 + (chainLength + 1) * maxRetries provider calls plus per-rung backoff before the turn fails; with maxRetries: 0 every failure switches immediately, costing 1 + chainLength calls.

Anthropic streaming refusals are identified from typed stopDetails. A configured candidate receives an immediate pinned fallback switch with a user-visible fallback notice: Senpi does not retry the refusing model and a pinned fallback never auto-reverts. Set retry.fallbackRevertPolicy to "cooldown-expiry" (the default) to return an unpinned fallback to its primary after the primary's cooldown expires, or "never" to keep the fallback until you change models.

Provider-substituted models

Anthropic's server-side fallback betas can retry a classifier-declined request on a substitute model inside the same response, marking the handoff with a fallback content block; a gateway may enable this on your behalf. Honoring that response means paying for a model you did not select, and after the first handoff Anthropic routes later turns of the conversation straight to the substitute with no marker at all — reported only as a fallback_message entry in usage.iterations.

With retry.abortServerSideFallback enabled (the default), Senpi treats either signal as a decline: it aborts the request as soon as the signal arrives, discards the substitute's partial output, and re-enters the turn as a classifier refusal so your own retry.fallbackChains chooses the replacement model. The transcript shows Server fallback <from> -> <to> aborted, naming /fallback when no chain is configured for the current model.

Two caveats. Aborting minimizes but cannot eliminate cost: output already streamed before the abort is billed, and because per-attempt usage never arrives on an aborted stream, the turn carries a billing_incomplete_after_client_abort diagnostic instead of a precise cost. A served-model string that merely differs from the requested one never triggers an abort, because gateways and Bedrock-style endpoints legitimately rewrite model ids.

Set it to false to keep the substituted response instead. If a gateway in front of Senpi injects the fallback itself, disabling the injection there avoids launching the substitute at all and is cheaper than aborting it client-side.

Fallback decisions are process-local. A senpi-task or subagent child process reads its own settings and maintains its own in-memory suppression state; it does not affect its parent process. Disable fallback for one run without changing settings with --no-model-fallback or SENPI_NO_FALLBACK=1.

For diagnostics, Senpi writes sanitized NDJSON records for candidate skips, cooldowns, switches, reverts, manual clears, and validation warnings to <agentDir>/logs/fallback.log. The file is mode 0600 and rotates at 5 MB (fallback.log.1).

Message Delivery

SettingTypeDefaultDescription
steeringModestring"one-at-a-time"How steering messages are sent: "all" or "one-at-a-time"
followUpModestring"one-at-a-time"How follow-up messages are sent: "all" or "one-at-a-time"
transportstring"auto"Preferred transport for providers that support multiple transports: "sse", "websocket", "websocket-cached", or "auto"
httpIdleTimeoutMsnumber300000HTTP header/body idle timeout in milliseconds, also used by providers with explicit stream idle timeouts. Set to 0 to disable.
websocketConnectTimeoutMsnumber15000WebSocket connect/open handshake timeout in milliseconds for providers that support WebSocket transports. Set to 0 to disable.

OpenAI

SettingTypeDefaultDescription
openai.serviceTierstring-Injects OpenAI Responses service_tier: "auto", "flex", or "priority"
{
  "openai": {
    "serviceTier": "priority"
  }
}

When unset, senpi leaves provider payloads unchanged. This setting currently applies only to the built-in OpenAI Responses provider path.

Terminal & Images

SettingTypeDefaultDescription
terminal.showImagesbooleantrueShow images in terminal (if supported)
terminal.imageWidthCellsnumber60Preferred inline image width in terminal cells
terminal.clearOnShrinkbooleanfalseClear empty rows when content shrinks (can cause flicker)
images.autoResizebooleantrueResize images to 2000x2000 max
images.blockImagesbooleanfalseBlock all images from being sent to LLM

Shell

SettingTypeDefaultDescription
shellPathstring-Custom shell path (e.g., for Cygwin on Windows); supports a leading ~ for the home directory
shellCommandPrefixstring-Prefix for every bash command (e.g., "shopt -s expand_aliases")
npmCommandstring[]-Command argv used for npm package lookup/install operations (e.g., ["mise", "exec", "node@20", "--", "npm"])
{
  "npmCommand": ["mise", "exec", "node@20", "--", "npm"]
}

npmCommand is used for all npm package-manager operations, including installs, uninstalls, and dependency installs inside git packages. User-scoped npm packages install under ~/.senpi/agent/npm/; project-scoped npm packages install under .senpi/npm/. Use argv-style entries exactly as the process should be launched. When npmCommand is configured, git package dependency installs use plain install to avoid npm-specific flags in wrappers or alternate package managers.

Sessions

SettingTypeDefaultDescription
sessionDirstring-Directory where session files are stored. Accepts absolute or relative paths, plus ~.
{ "sessionDir": ".senpi/sessions" }

When multiple sources specify a session directory, precedence is --session-dir, SENPI_CODING_AGENT_SESSION_DIR, then sessionDir in settings.json.

Model Cycling

SettingTypeDefaultDescription
favoriteModelsstring[]-Favorite model patterns for Ctrl+P cycling (same format as --models CLI flag)
enabledModelsstring[]-Legacy global model-catalog narrowing patterns (same format as --models CLI flag)
{
  "enabledModels": ["anthropic/*", "openai/*"],
  "favoriteModels": ["anthropic/claude-*", "openai/gpt-5.4"]
}

enabledModels changes which models appear in the catalog, startup selection, and /model narrowing. favoriteModels is separate and only controls Ctrl+P cycling.

Markdown

SettingTypeDefaultDescription
markdown.codeBlockIndentstring" "Indentation for code blocks

Resources

These settings define where to load extensions, skills, prompts, and themes from.

Paths in ~/.senpi/agent/settings.json resolve relative to ~/.senpi/agent. Paths in .senpi/settings.json resolve relative to .senpi. Absolute paths and ~ are supported.

SettingTypeDefaultDescription
packagesarray[]npm/git packages to load resources from
enabledBuiltinExtensionsstring[]all builtinsOptional allowlist of builtin extension ids to load
disabledBuiltinExtensionsstring[][]Builtin extension ids to skip; overrides enabledBuiltinExtensions
extensionsstring[][]Local extension file paths or directories
skillsstring[][]Local skill file paths or directories
promptsstring[][]Local prompt template paths or directories
themesstring[][]Local theme file paths or directories
enableSkillCommandsbooleantrueRegister skills as /skill:name commands

Arrays support glob patterns and exclusions. Use !pattern to exclude. Use +path to force-include an exact path and -path to force-exclude an exact path.

packages

String form loads all resources from a package:

{
  "packages": ["pi-skills", "@org/my-extension"]
}

Object form filters which resources to load:

{
  "packages": [
    {
      "source": "pi-skills",
      "skills": ["brave-search", "transcribe"],
      "extensions": []
    }
  ]
}

See packages.md for package management details.

Example

{
  "defaultProvider": "anthropic",
  "defaultModel": "claude-sonnet-4-20250514",
  "defaultThinkingLevel": "medium",
  "theme": "dark",
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  },
  "retry": {
    "enabled": true,
    "maxRetries": 3
  },
  "enabledModels": ["claude-*", "gpt-4o"],
  "favoriteModels": ["anthropic/claude-sonnet-4-20250514", "openai/gpt-4o"],
  "warnings": {
    "anthropicExtraUsage": true
  },
  "packages": ["pi-skills"]
}

Project Overrides

Project settings (.senpi/settings.json) override global settings. Nested objects are merged:

// ~/.senpi/agent/settings.json (global)
{
  "theme": "dark",
  "compaction": { "enabled": true, "reserveTokens": 16384 }
}

// .senpi/settings.json (project)
{
  "compaction": { "reserveTokens": 8192 }
}

// Result
{
  "theme": "dark",
  "compaction": { "enabled": true, "reserveTokens": 8192 }
}