Files
oh-my-pi/packages/coding-agent/src/config/settings-schema.ts
T
can1357 005c3cd81a feat(coding-agent/task): added subagent runtime timeout and context-window progress reporting
- Added a task.maxRuntimeMs setting with a default disabled state for per-subagent runtime limits.
- Updated subprocess execution to enforce the configured wall-clock timeout, mark timeouts as aborts, and include timeout-specific abort messaging.
- Tracked per-turn context size and context window through task progress/results and updated UI renderers to display current context against window with cumulative tokens rendered separately.
2026-05-15 14:46:54 +02:00

2881 lines
84 KiB
TypeScript

import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
import { TASK_SIMPLE_MODES } from "../task/simple-mode";
import { getThinkingLevelMetadata } from "../thinking";
import { EDIT_MODES } from "../utils/edit-mode";
/** Unified settings schema - single source of truth for all settings.
* Unified settings schema - single source of truth for all settings.
*
* Each setting is defined once here with:
* - Type and default value
* - Optional UI metadata (label, description, tab)
*
* The Settings singleton provides type-safe path-based access:
* settings.get("compaction.enabled") // => boolean
* settings.set("theme.dark", "titanium") // sync, saves in background
*/
// ═══════════════════════════════════════════════════════════════════════════
// Schema Definition Types
// ═══════════════════════════════════════════════════════════════════════════
export type SettingTab =
| "appearance"
| "model"
| "interaction"
| "context"
| "memory"
| "editing"
| "tools"
| "tasks"
| "providers";
/** Tab display metadata - icon is resolved via theme.symbol() */
export type TabMetadata = { label: string; icon: `tab.${string}` };
/** Ordered list of tabs for UI rendering */
export const SETTING_TABS: SettingTab[] = [
"appearance",
"model",
"interaction",
"context",
"memory",
"editing",
"tools",
"tasks",
"providers",
];
/** Tab display metadata - icon is a symbol key from theme.ts (tab.*) */
export const TAB_METADATA: Record<SettingTab, { label: string; icon: `tab.${string}` }> = {
appearance: { label: "Appearance", icon: "tab.appearance" },
model: { label: "Model", icon: "tab.model" },
interaction: { label: "Interaction", icon: "tab.interaction" },
context: { label: "Context", icon: "tab.context" },
memory: { label: "Memory", icon: "tab.memory" },
editing: { label: "Editing", icon: "tab.editing" },
tools: { label: "Tools", icon: "tab.tools" },
tasks: { label: "Tasks", icon: "tab.tasks" },
providers: { label: "Providers", icon: "tab.providers" },
};
/** Status line segment identifiers */
export type StatusLineSegmentId =
| "pi"
| "model"
| "mode"
| "path"
| "git"
| "pr"
| "subagents"
| "token_in"
| "token_out"
| "token_total"
| "token_rate"
| "cost"
| "context_pct"
| "context_total"
| "time_spent"
| "time"
| "session"
| "hostname"
| "cache_read"
| "cache_write"
| "session_name";
/** Submenu choice metadata. */
export type SubmenuOption<V extends string = string> = {
value: V;
label: string;
description?: string;
};
interface UiBase {
tab: SettingTab;
label: string;
description: string;
/** Condition function name - setting only shown when true */
condition?: string;
}
interface UiBoolean extends UiBase {}
interface UiEnum<T extends readonly string[]> extends UiBase {
/** Submenu options. When omitted, the enum renders as an inline toggle derived from `values`. */
options?: ReadonlyArray<SubmenuOption<T[number]>>;
}
interface UiNumber extends UiBase {
/** Submenu options. Without options, a numeric setting has no UI representation (intentional hide). */
options?: ReadonlyArray<SubmenuOption>;
}
interface UiString extends UiBase {
/**
* Submenu options.
* - Array → submenu with these choices.
* - "runtime" → submenu populated by the runtime layer (theme registry, etc.).
* - Omitted → renders as a free text input.
*/
options?: ReadonlyArray<SubmenuOption> | "runtime";
}
/** Wide ui shape exposed to consumers that walk the schema generically. */
export type AnyUiMetadata = UiBase & {
options?: ReadonlyArray<SubmenuOption> | "runtime";
};
interface BooleanDef {
type: "boolean";
default: boolean;
ui?: UiBoolean;
}
interface StringDef {
type: "string";
default: string | undefined;
ui?: UiString;
}
interface NumberDef {
type: "number";
default: number;
ui?: UiNumber;
}
interface EnumDef<T extends readonly string[]> {
type: "enum";
values: T;
default: T[number];
ui?: UiEnum<T>;
}
interface ArrayDef<T> {
type: "array";
default: T[];
ui?: UiBase;
}
interface RecordDef<T> {
type: "record";
default: Record<string, T>;
ui?: UiBase;
}
type SettingDef =
| BooleanDef
| StringDef
| NumberDef
| EnumDef<readonly string[]>
| ArrayDef<unknown>
| RecordDef<unknown>;
// ═══════════════════════════════════════════════════════════════════════════
// Schema Definition
// ═══════════════════════════════════════════════════════════════════════════
export interface ModelTagDef {
name: string;
color?: string;
}
export interface ModelTagsSettings {
[key: string]: ModelTagDef;
}
// Typed defaults for array/record settings — named constants avoid `as` casts
// under `as const` while still letting SettingValue infer the correct element type.
const EMPTY_STRING_ARRAY: string[] = [];
const EMPTY_STRING_RECORD: Record<string, string> = {};
const DEFAULT_CYCLE_ORDER: string[] = ["smol", "default", "slow"];
const EMPTY_MODEL_TAGS_RECORD: ModelTagsSettings = {};
const HINDSIGHT_RECALL_TYPES_DEFAULT: string[] = ["world", "experience"];
export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
{
pattern: "^\\s*(cat|head|tail|less|more)\\s+",
tool: "read",
message: "Use the `read` tool instead of cat/head/tail. It provides better context and handles binary files.",
},
{
pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+",
tool: "search",
message: "Use the `search` tool instead of grep/rg. It respects .gitignore and provides structured output.",
},
{
pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)",
tool: "find",
message: "Use the `find` tool instead of find/fd. It respects .gitignore and is faster for glob patterns.",
},
{
pattern: "^\\s*sed\\s+(-i|--in-place)",
tool: "edit",
message: "Use the `edit` tool instead of sed -i. It provides diff preview and fuzzy matching.",
},
{
pattern: "^\\s*perl\\s+.*-[pn]?i",
tool: "edit",
message: "Use the `edit` tool instead of perl -i. It provides diff preview and fuzzy matching.",
},
{
pattern: "^\\s*awk\\s+.*-i\\s+inplace",
tool: "edit",
message: "Use the `edit` tool instead of awk -i inplace. It provides diff preview and fuzzy matching.",
},
{
pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+.*[^|]>\\s*\\S",
tool: "write",
message: "Use the `write` tool instead of echo/cat redirection. It handles encoding and provides confirmation.",
},
];
export const SETTINGS_SCHEMA = {
// ────────────────────────────────────────────────────────────────────────
// General settings (no UI)
// ────────────────────────────────────────────────────────────────────────
lastChangelogVersion: { type: "string", default: undefined },
autoResume: {
type: "boolean",
default: false,
ui: {
tab: "interaction",
label: "Auto Resume",
description: "Automatically resume the most recent session in the current directory",
},
},
// macOS power assertions (caffeinate flags). No-op on other platforms.
"power.preventIdleSleep": {
type: "boolean",
default: true,
ui: {
tab: "interaction",
label: "Prevent Idle Sleep (macOS)",
description: "caffeinate -i: keep the system awake while a session is open",
},
},
"power.preventSystemSleep": {
type: "boolean",
default: false,
ui: {
tab: "interaction",
label: "Prevent System Sleep on AC (macOS)",
description: "caffeinate -s: block all system sleep while on AC power",
},
},
"power.declareUserActive": {
type: "boolean",
default: false,
ui: {
tab: "interaction",
label: "Declare User Active (macOS)",
description: "caffeinate -u: keep the display lit and treat the user as active",
},
},
"power.preventDisplaySleep": {
type: "boolean",
default: false,
ui: {
tab: "interaction",
label: "Prevent Display Sleep (macOS)",
description: "caffeinate -d: keep the display from idle-sleeping while a session is open",
},
},
shellPath: { type: "string", default: undefined },
extensions: { type: "array", default: EMPTY_STRING_ARRAY },
"marketplace.autoUpdate": {
type: "enum",
values: ["off", "notify", "auto"] as const,
default: "notify",
ui: {
tab: "tools",
label: "Marketplace Auto-Update",
description: "Check for plugin updates on startup (off/notify/auto)",
options: [
{ value: "off", label: "Off", description: "Don't check for plugin updates" },
{ value: "notify", label: "Notify", description: "Check on startup and notify when updates are available" },
{ value: "auto", label: "Auto", description: "Check on startup and auto-install updates" },
],
},
},
enabledModels: { type: "array", default: EMPTY_STRING_ARRAY },
disabledProviders: { type: "array", default: EMPTY_STRING_ARRAY },
disabledExtensions: { type: "array", default: EMPTY_STRING_ARRAY },
modelRoles: { type: "record", default: EMPTY_STRING_RECORD },
modelTags: { type: "record", default: EMPTY_MODEL_TAGS_RECORD },
modelProviderOrder: { type: "array", default: EMPTY_STRING_ARRAY },
cycleOrder: { type: "array", default: DEFAULT_CYCLE_ORDER },
// ────────────────────────────────────────────────────────────────────────
// Appearance
// ────────────────────────────────────────────────────────────────────────
// Theme
"theme.dark": {
type: "string",
default: "titanium",
ui: {
tab: "appearance",
label: "Dark Theme",
description: "Theme used when terminal has dark background",
options: "runtime",
},
},
"theme.light": {
type: "string",
default: "light",
ui: {
tab: "appearance",
label: "Light Theme",
description: "Theme used when terminal has light background",
options: "runtime",
},
},
symbolPreset: {
type: "enum",
values: ["unicode", "nerd", "ascii"] as const,
default: "unicode",
ui: {
tab: "appearance",
label: "Symbol Preset",
description: "Icon/symbol style",
options: [
{ value: "unicode", label: "Unicode", description: "Standard symbols (default)" },
{ value: "nerd", label: "Nerd Font", description: "Requires Nerd Font" },
{ value: "ascii", label: "ASCII", description: "Maximum compatibility" },
],
},
},
colorBlindMode: {
type: "boolean",
default: false,
ui: {
tab: "appearance",
label: "Color-Blind Mode",
description: "Use blue instead of green for diff additions",
},
},
// Status line
"statusLine.preset": {
type: "enum",
values: ["default", "minimal", "compact", "full", "nerd", "ascii", "custom"] as const,
default: "default",
ui: {
tab: "appearance",
label: "Status Line Preset",
description: "Pre-built status line configurations",
options: [
{ value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
{ value: "minimal", label: "Minimal", description: "Path and git only" },
{ value: "compact", label: "Compact", description: "Model, git, cost, context" },
{ value: "full", label: "Full", description: "All segments including time" },
{ value: "nerd", label: "Nerd", description: "Maximum info with Nerd Font icons" },
{ value: "ascii", label: "ASCII", description: "No special characters" },
{ value: "custom", label: "Custom", description: "User-defined segments" },
],
},
},
"statusLine.separator": {
type: "enum",
values: ["powerline", "powerline-thin", "slash", "pipe", "block", "none", "ascii"] as const,
default: "powerline-thin",
ui: {
tab: "appearance",
label: "Status Line Separator",
description: "Style of separators between segments",
options: [
{ value: "powerline", label: "Powerline", description: "Solid arrows (Nerd Font)" },
{ value: "powerline-thin", label: "Thin chevron", description: "Thin arrows (Nerd Font)" },
{ value: "slash", label: "Slash", description: "Forward slashes" },
{ value: "pipe", label: "Pipe", description: "Vertical pipes" },
{ value: "block", label: "Block", description: "Solid blocks" },
{ value: "none", label: "None", description: "Space only" },
{ value: "ascii", label: "ASCII", description: "Greater-than signs" },
],
},
},
"statusLine.sessionAccent": {
type: "boolean",
default: true,
ui: {
tab: "appearance",
label: "Session Accent",
description: "Use the session name color for the editor border and status line gap",
},
},
"tools.artifactSpillThreshold": {
type: "number",
default: 50,
ui: {
tab: "tools",
label: "Artifact spill threshold (KB)",
description: "Tool output above this size is saved as an artifact; tail is kept inline",
options: [
{ value: "1", label: "1 KB", description: "~250 tokens" },
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
{ value: "20", label: "20 KB", description: "~5K tokens" },
{ value: "30", label: "30 KB", description: "~7.5K tokens" },
{ value: "50", label: "50 KB", description: "Default; ~12.5K tokens" },
{ value: "75", label: "75 KB", description: "~19K tokens" },
{ value: "100", label: "100 KB", description: "~25K tokens" },
{ value: "200", label: "200 KB", description: "~50K tokens" },
{ value: "500", label: "500 KB", description: "~125K tokens" },
{ value: "1000", label: "1 MB", description: "~250K tokens" },
],
},
},
"tools.artifactTailBytes": {
type: "number",
default: 20,
ui: {
tab: "tools",
label: "Artifact tail size (KB)",
description: "Amount of tail content kept inline when output spills to artifact",
options: [
{ value: "1", label: "1 KB", description: "~250 tokens" },
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
{ value: "20", label: "20 KB", description: "Default; ~5K tokens" },
{ value: "50", label: "50 KB", description: "~12.5K tokens" },
{ value: "100", label: "100 KB", description: "~25K tokens" },
{ value: "200", label: "200 KB", description: "~50K tokens" },
],
},
},
"tools.artifactHeadBytes": {
type: "number",
default: 20,
ui: {
tab: "tools",
label: "Artifact head size (KB)",
description:
"Amount of head content kept inline alongside the tail when output spills to artifact (middle elision). 0 disables — keep tail only.",
options: [
{ value: "0", label: "0 KB", description: "Disabled; tail-only truncation" },
{ value: "1", label: "1 KB", description: "~250 tokens" },
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
{ value: "20", label: "20 KB", description: "Default; ~5K tokens" },
{ value: "50", label: "50 KB", description: "~12.5K tokens" },
{ value: "100", label: "100 KB", description: "~25K tokens" },
{ value: "200", label: "200 KB", description: "~50K tokens" },
],
},
},
"tools.outputMaxColumns": {
type: "number",
default: 768,
ui: {
tab: "tools",
label: "Output column cap",
description:
"Per-line byte cap for streaming tool outputs (bash, ssh, python, js eval) and `read`. Lines wider than this are ellipsis-truncated; remaining bytes up to the next newline are dropped. 0 disables.",
options: [
{ value: "0", label: "Off", description: "No per-line cap" },
{ value: "256", label: "256", description: "Tight" },
{ value: "512", label: "512" },
{ value: "768", label: "768", description: "Default" },
{ value: "1024", label: "1024" },
{ value: "2048", label: "2048" },
{ value: "4096", label: "4096", description: "Loose" },
],
},
},
"tools.artifactTailLines": {
type: "number",
default: 500,
ui: {
tab: "tools",
label: "Artifact tail lines",
description: "Maximum lines of tail content kept inline when output spills to artifact",
options: [
{ value: "50", label: "50 lines", description: "~250 tokens" },
{ value: "100", label: "100 lines", description: "~500 tokens" },
{ value: "250", label: "250 lines", description: "~1.25K tokens" },
{ value: "500", label: "500 lines", description: "Default; ~2.5K tokens" },
{ value: "1000", label: "1000 lines", description: "~5K tokens" },
{ value: "2000", label: "2000 lines", description: "~10K tokens" },
{ value: "5000", label: "5000 lines", description: "~25K tokens" },
],
},
},
"statusLine.showHookStatus": {
type: "boolean",
default: true,
ui: {
tab: "appearance",
label: "Show Hook Status",
description: "Display hook status messages below status line",
},
},
"statusLine.leftSegments": { type: "array", default: [] as StatusLineSegmentId[] },
"statusLine.rightSegments": { type: "array", default: [] as StatusLineSegmentId[] },
"statusLine.segmentOptions": { type: "record", default: {} as Record<string, unknown> },
// Images and terminal
"terminal.showImages": {
type: "boolean",
default: true,
ui: {
tab: "appearance",
label: "Show Inline Images",
description: "Render images inline in terminal",
condition: "hasImageProtocol",
},
},
"images.autoResize": {
type: "boolean",
default: true,
ui: {
tab: "appearance",
label: "Auto-Resize Images",
description: "Resize large images to 2000x2000 max for better model compatibility",
},
},
"images.blockImages": {
type: "boolean",
default: false,
ui: { tab: "appearance", label: "Block Images", description: "Prevent images from being sent to LLM providers" },
},
"tui.maxInlineImageColumns": {
type: "number",
default: 100,
description:
"Maximum width in terminal columns for inline images (default 100). Set to 0 for unlimited (bounded only by terminal width).",
},
"tui.maxInlineImageRows": {
type: "number",
default: 20,
description:
"Maximum height in terminal rows for inline images (default 20). Set to 0 to use only the viewport-based limit (60% of terminal height).",
},
// Display rendering
"display.tabWidth": {
type: "number",
default: 3,
},
"display.showTokenUsage": {
type: "boolean",
default: false,
ui: {
tab: "appearance",
label: "Show Token Usage",
description: "Show per-turn token usage on assistant messages",
},
},
showHardwareCursor: {
type: "boolean",
default: true, // will be computed based on platform if undefined
ui: { tab: "appearance", label: "Show Hardware Cursor", description: "Show terminal cursor for IME support" },
},
clearOnShrink: {
type: "boolean",
default: false,
ui: {
tab: "appearance",
label: "Clear on Shrink",
description: "Clear empty rows when content shrinks (may cause flicker)",
},
},
// ────────────────────────────────────────────────────────────────────────
// Model
// ────────────────────────────────────────────────────────────────────────
// Reasoning and prompts
defaultThinkingLevel: {
type: "enum",
values: THINKING_EFFORTS,
default: "high",
ui: {
tab: "model",
label: "Thinking Level",
description: "Reasoning depth for thinking-capable models",
options: [...THINKING_EFFORTS.map(getThinkingLevelMetadata)],
},
},
hideThinkingBlock: {
type: "boolean",
default: false,
ui: { tab: "model", label: "Hide Thinking Blocks", description: "Hide thinking blocks in assistant responses" },
},
repeatToolDescriptions: {
type: "boolean",
default: false,
ui: {
tab: "model",
label: "Repeat Tool Descriptions",
description: "Render full tool descriptions in the system prompt instead of a tool name list",
},
},
// Sampling
temperature: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Temperature",
description: "Sampling temperature (0 = deterministic, 1 = creative, -1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "0", label: "0", description: "Deterministic" },
{ value: "0.2", label: "0.2", description: "Focused" },
{ value: "0.5", label: "0.5", description: "Balanced" },
{ value: "0.7", label: "0.7", description: "Creative" },
{ value: "1", label: "1", description: "Maximum variety" },
],
},
},
topP: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Top P",
description: "Nucleus sampling cutoff (0-1, -1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "0.1", label: "0.1", description: "Very focused" },
{ value: "0.3", label: "0.3", description: "Focused" },
{ value: "0.5", label: "0.5", description: "Balanced" },
{ value: "0.9", label: "0.9", description: "Broad" },
{ value: "1", label: "1", description: "No nucleus filtering" },
],
},
},
topK: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Top K",
description: "Sample from top-K tokens (-1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "1", label: "1", description: "Greedy top token" },
{ value: "20", label: "20", description: "Focused" },
{ value: "40", label: "40", description: "Balanced" },
{ value: "100", label: "100", description: "Broad" },
],
},
},
minP: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Min P",
description: "Minimum probability threshold (0-1, -1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "0.01", label: "0.01", description: "Very permissive" },
{ value: "0.05", label: "0.05", description: "Balanced" },
{ value: "0.1", label: "0.1", description: "Strict" },
],
},
},
presencePenalty: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Presence Penalty",
description: "Penalty for introducing already-present tokens (-1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "0", label: "0", description: "No penalty" },
{ value: "0.5", label: "0.5", description: "Mild novelty" },
{ value: "1", label: "1", description: "Encourage novelty" },
{ value: "2", label: "2", description: "Strong novelty" },
],
},
},
repetitionPenalty: {
type: "number",
default: -1,
ui: {
tab: "model",
label: "Repetition Penalty",
description: "Penalty for repeated tokens (-1 = provider default)",
options: [
{ value: "-1", label: "Default", description: "Use provider default" },
{ value: "0.8", label: "0.8", description: "Allow repetition" },
{ value: "1", label: "1", description: "No penalty" },
{ value: "1.1", label: "1.1", description: "Mild penalty" },
{ value: "1.2", label: "1.2", description: "Balanced" },
{ value: "1.5", label: "1.5", description: "Strong penalty" },
],
},
},
serviceTier: {
type: "enum",
values: ["none", "auto", "default", "flex", "scale", "priority"] as const,
default: "none",
ui: {
tab: "model",
label: "Service Tier",
description: "OpenAI processing priority (none = omit parameter)",
options: [
{ value: "none", label: "None", description: "Omit service_tier parameter" },
{ value: "auto", label: "Auto", description: "Use provider default tier selection" },
{ value: "default", label: "Default", description: "Standard priority processing" },
{ value: "flex", label: "Flex", description: "Use flexible capacity tier when available" },
{ value: "scale", label: "Scale", description: "Use Scale Tier credits when available" },
{ value: "priority", label: "Priority", description: "Use Priority processing" },
],
},
},
// Retries
"retry.enabled": { type: "boolean", default: true },
"retry.maxRetries": {
type: "number",
default: 3,
ui: {
tab: "model",
label: "Retry Attempts",
description: "Maximum retry attempts on API errors",
options: [
{ value: "1", label: "1 retry" },
{ value: "2", label: "2 retries" },
{ value: "3", label: "3 retries" },
{ value: "5", label: "5 retries" },
{ value: "10", label: "10 retries" },
],
},
},
"retry.baseDelayMs": { type: "number", default: 2000 },
"retry.fallbackChains": { type: "record", default: {} as Record<string, string[]> },
"retry.fallbackRevertPolicy": {
type: "enum",
values: ["cooldown-expiry", "never"] as const,
default: "cooldown-expiry",
ui: {
tab: "model",
label: "Fallback Revert Policy",
description: "When to return to the primary model after a fallback",
options: [
{
value: "cooldown-expiry",
label: "Cooldown expiry",
description: "Return to the primary model after its suppression window ends",
},
{ value: "never", label: "Never", description: "Stay on the fallback model until manually changed" },
],
},
},
// ────────────────────────────────────────────────────────────────────────
// Interaction
// ────────────────────────────────────────────────────────────────────────
// Conversation flow
steeringMode: {
type: "enum",
values: ["all", "one-at-a-time"] as const,
default: "one-at-a-time",
ui: {
tab: "interaction",
label: "Steering Mode",
description: "How to process queued messages while agent is working",
},
},
followUpMode: {
type: "enum",
values: ["all", "one-at-a-time"] as const,
default: "one-at-a-time",
ui: {
tab: "interaction",
label: "Follow-Up Mode",
description: "How to drain follow-up messages after a turn completes",
},
},
interruptMode: {
type: "enum",
values: ["immediate", "wait"] as const,
default: "immediate",
ui: {
tab: "interaction",
label: "Interrupt Mode",
description: "When steering messages interrupt tool execution",
},
},
"loop.mode": {
type: "enum",
values: ["prompt", "compact", "reset"] as const,
default: "prompt",
ui: {
tab: "interaction",
label: "Loop Mode",
description: "What happens between /loop iterations before re-submitting the prompt",
options: [
{
value: "prompt",
label: "Prompt",
description: "Re-submit the prompt as a follow-up message (current behavior)",
},
{
value: "compact",
label: "Compact",
description: "Compact the session context, then re-submit the prompt",
},
{ value: "reset", label: "Reset", description: "Start a new session, then re-submit the prompt" },
],
},
},
// Input and startup
doubleEscapeAction: {
type: "enum",
values: ["branch", "tree", "none"] as const,
default: "tree",
ui: {
tab: "interaction",
label: "Double-Escape Action",
description: "Action when pressing Escape twice with empty editor",
},
},
treeFilterMode: {
type: "enum",
values: ["default", "no-tools", "user-only", "labeled-only", "all"] as const,
default: "default",
ui: {
tab: "interaction",
label: "Session Tree Filter",
description: "Default filter mode when opening the session tree",
},
},
autocompleteMaxVisible: {
type: "number",
default: 5,
ui: {
tab: "interaction",
label: "Autocomplete Items",
description: "Max visible items in autocomplete dropdown (3-20)",
options: [
{ value: "3", label: "3 items" },
{ value: "5", label: "5 items" },
{ value: "7", label: "7 items" },
{ value: "10", label: "10 items" },
{ value: "15", label: "15 items" },
{ value: "20", label: "20 items" },
],
},
},
"startup.quiet": {
type: "boolean",
default: false,
ui: {
tab: "interaction",
label: "Quiet Startup",
description: "Skip welcome screen and startup status messages",
},
},
"startup.checkUpdate": {
type: "boolean",
default: true,
ui: {
tab: "interaction",
label: "Check for Updates",
description: "If false, skip update check",
},
},
collapseChangelog: {
type: "boolean",
default: false,
ui: { tab: "interaction", label: "Collapse Changelog", description: "Show condensed changelog after updates" },
},
// Notifications
"completion.notify": {
type: "enum",
values: ["on", "off"] as const,
default: "on",
ui: { tab: "interaction", label: "Completion Notification", description: "Notify when the agent completes" },
},
"ask.timeout": {
type: "number",
default: 0,
ui: {
tab: "interaction",
label: "Ask Timeout",
description: "Auto-select recommended option after timeout (0 to disable)",
options: [
{ value: "0", label: "Disabled" },
{ value: "15", label: "15 seconds" },
{ value: "30", label: "30 seconds" },
{ value: "60", label: "60 seconds" },
{ value: "120", label: "120 seconds" },
],
},
},
"ask.notify": {
type: "enum",
values: ["on", "off"] as const,
default: "on",
ui: { tab: "interaction", label: "Ask Notification", description: "Notify when ask tool is waiting for input" },
},
// Speech-to-text
"stt.enabled": {
type: "boolean",
default: false,
ui: { tab: "interaction", label: "Speech-to-Text", description: "Enable speech-to-text input via microphone" },
},
"stt.language": {
type: "string",
default: "en",
},
"stt.modelName": {
type: "enum",
values: ["tiny", "tiny.en", "base", "base.en", "small", "small.en", "medium", "medium.en", "large"] as const,
default: "base.en",
ui: {
tab: "interaction",
label: "Speech Model",
description: "Whisper model size (larger = more accurate but slower)",
options: [
{ value: "tiny", label: "tiny", description: "Multilingual; fastest, lowest accuracy" },
{ value: "tiny.en", label: "tiny.en", description: "English-only; fastest" },
{ value: "base", label: "base", description: "Multilingual; small and fast" },
{ value: "base.en", label: "base.en", description: "English-only; default" },
{ value: "small", label: "small", description: "Multilingual; balanced" },
{ value: "small.en", label: "small.en", description: "English-only; balanced" },
{ value: "medium", label: "medium", description: "Multilingual; accurate but slower" },
{ value: "medium.en", label: "medium.en", description: "English-only; accurate but slower" },
{ value: "large", label: "large", description: "Multilingual; most accurate" },
],
},
},
// ────────────────────────────────────────────────────────────────────────
// Context
// ────────────────────────────────────────────────────────────────────────
// Context promotion
"contextPromotion.enabled": {
type: "boolean",
default: true,
ui: {
tab: "context",
label: "Auto-Promote Context",
description: "Promote to a larger-context model on context overflow instead of compacting",
},
},
// Compaction
"compaction.enabled": {
type: "boolean",
default: true,
ui: {
tab: "context",
label: "Auto-Compact",
description: "Automatically compact context when it gets too large",
},
},
"compaction.strategy": {
type: "enum",
values: ["context-full", "handoff", "off"] as const,
default: "context-full",
ui: {
tab: "context",
label: "Compaction Strategy",
description: "Choose in-place context-full maintenance, auto-handoff, or disable auto maintenance (off)",
options: [
{
value: "context-full",
label: "Context-full",
description: "Summarize in-place and keep the current session",
},
{ value: "handoff", label: "Handoff", description: "Generate handoff and continue in a new session" },
{
value: "off",
label: "Off",
description: "Disable automatic context maintenance (same behavior as Auto-compact off)",
},
],
},
},
"compaction.thresholdPercent": {
type: "number",
default: -1,
ui: {
tab: "context",
label: "Compaction Threshold",
description: "Percent threshold for context maintenance; set to Default to use legacy reserve-based behavior",
options: [
{ value: "default", label: "Default", description: "Legacy reserve-based threshold" },
{ value: "10", label: "10%", description: "Extremely early maintenance" },
{ value: "20", label: "20%", description: "Very early maintenance" },
{ value: "30", label: "30%", description: "Early maintenance" },
{ value: "40", label: "40%", description: "Moderately early maintenance" },
{ value: "50", label: "50%", description: "Halfway point" },
{ value: "60", label: "60%", description: "Moderate context usage" },
{ value: "70", label: "70%", description: "Balanced" },
{ value: "75", label: "75%", description: "Slightly aggressive" },
{ value: "80", label: "80%", description: "Typical threshold" },
{ value: "85", label: "85%", description: "Aggressive context usage" },
{ value: "90", label: "90%", description: "Very aggressive" },
{ value: "95", label: "95%", description: "Near context limit" },
],
},
},
"compaction.thresholdTokens": {
type: "number",
default: -1,
ui: {
tab: "context",
label: "Compaction Token Limit",
description: "Fixed token limit for context maintenance; overrides percentage if set",
options: [
{ value: "default", label: "Default", description: "Use percentage-based threshold" },
{ value: "25000", label: "25K tokens", description: "Quarter of a 200K window" },
{ value: "50000", label: "50K tokens", description: "Half of a 200K window" },
{ value: "100000", label: "100K tokens", description: "Half of a 200K window" },
{ value: "150000", label: "150K tokens", description: "Three-quarters of a 200K window" },
{ value: "200000", label: "200K tokens", description: "Full standard context window" },
{ value: "300000", label: "300K tokens", description: "Large context window" },
{ value: "500000", label: "500K tokens", description: "Very large context window" },
],
},
},
"compaction.handoffSaveToDisk": {
type: "boolean",
default: false,
ui: {
tab: "context",
label: "Save Handoff Docs",
description: "Save generated handoff documents to markdown files for the auto-handoff flow",
},
},
"compaction.remoteEnabled": {
type: "boolean",
default: true,
ui: {
tab: "context",
label: "Remote Compaction",
description: "Use remote compaction endpoints when available instead of local summarization",
},
},
"compaction.reserveTokens": { type: "number", default: 16384 },
"compaction.keepRecentTokens": { type: "number", default: 20000 },
"compaction.autoContinue": { type: "boolean", default: true },
"compaction.remoteEndpoint": { type: "string", default: undefined },
// Idle compaction
"compaction.idleEnabled": {
type: "boolean",
default: false,
ui: {
tab: "context",
label: "Idle Compaction",
description: "Compact context while idle when token count exceeds threshold",
},
},
"compaction.idleThresholdTokens": {
type: "number",
default: 200000,
ui: {
tab: "context",
label: "Idle Compaction Threshold",
description: "Token count above which idle compaction triggers",
options: [
{ value: "100000", label: "100K tokens" },
{ value: "200000", label: "200K tokens" },
{ value: "300000", label: "300K tokens" },
{ value: "400000", label: "400K tokens" },
{ value: "500000", label: "500K tokens" },
{ value: "600000", label: "600K tokens" },
{ value: "700000", label: "700K tokens" },
{ value: "800000", label: "800K tokens" },
{ value: "900000", label: "900K tokens" },
],
},
},
"compaction.idleTimeoutSeconds": {
type: "number",
default: 300,
ui: {
tab: "context",
label: "Idle Compaction Delay",
description: "Seconds to wait while idle before compacting",
options: [
{ value: "60", label: "1 minute" },
{ value: "120", label: "2 minutes" },
{ value: "300", label: "5 minutes" },
{ value: "600", label: "10 minutes" },
{ value: "1800", label: "30 minutes" },
{ value: "3600", label: "1 hour" },
],
},
},
// Branch summaries
"branchSummary.enabled": {
type: "boolean",
default: false,
ui: { tab: "context", label: "Branch Summaries", description: "Prompt to summarize when leaving a branch" },
},
"branchSummary.reserveTokens": { type: "number", default: 16384 },
// Memories
// Legacy local-memory enable flag kept only for back-compat migration.
// Hidden from UI — users should use `memory.backend` instead.
"memories.enabled": {
type: "boolean",
default: false,
},
"memories.maxRolloutsPerStartup": { type: "number", default: 64 },
"memories.maxRolloutAgeDays": { type: "number", default: 30 },
"memories.minRolloutIdleHours": { type: "number", default: 12 },
"memories.threadScanLimit": { type: "number", default: 300 },
"memories.maxRawMemoriesForGlobal": { type: "number", default: 200 },
"memories.stage1Concurrency": { type: "number", default: 8 },
"memories.stage1LeaseSeconds": { type: "number", default: 120 },
"memories.stage1RetryDelaySeconds": { type: "number", default: 120 },
"memories.phase2LeaseSeconds": { type: "number", default: 180 },
"memories.phase2RetryDelaySeconds": { type: "number", default: 180 },
"memories.phase2HeartbeatSeconds": { type: "number", default: 30 },
"memories.rolloutPayloadPercent": { type: "number", default: 0.7 },
"memories.phase1InputTokenLimit": { type: "number", default: 4000 },
"memories.fallbackTokenLimit": { type: "number", default: 16000 },
"memories.summaryInjectionTokenLimit": { type: "number", default: 5000 },
// Memory backend selector — picks between local memories pipeline,
// Hindsight remote memory, or off. Legacy `memories.enabled` keeps gating
// the local backend; see config/settings.ts migration for details.
"memory.backend": {
type: "enum",
values: ["off", "local", "hindsight"] as const,
default: "off",
ui: {
tab: "memory",
label: "Memory Backend",
description: "Off, local memory pipeline, or Hindsight remote memory",
options: [
{ value: "off", label: "Off", description: "No memory subsystem runs" },
{ value: "local", label: "Local", description: "Local rollout summarisation pipeline (memory_summary.md)" },
{ value: "hindsight", label: "Hindsight", description: "Vectorize Hindsight remote memory service" },
],
},
},
// Hindsight (https://hindsight.vectorize.io)
"hindsight.apiUrl": {
type: "string",
default: "http://localhost:8888",
ui: {
tab: "memory",
label: "Hindsight API URL",
description: "Hindsight server URL (Cloud or self-hosted)",
condition: "hindsightActive",
},
},
"hindsight.apiToken": { type: "string", default: undefined },
"hindsight.bankId": {
type: "string",
default: undefined,
ui: {
tab: "memory",
label: "Hindsight Bank ID",
description: "Memory bank identifier (default: project name)",
condition: "hindsightActive",
},
},
"hindsight.bankIdPrefix": { type: "string", default: undefined },
"hindsight.scoping": {
type: "enum",
values: ["global", "per-project", "per-project-tagged"] as const,
default: "per-project-tagged",
ui: {
tab: "memory",
label: "Hindsight Scoping",
description:
"global = one shared bank; per-project = isolated bank per cwd; per-project-tagged = shared bank with project tags so global + project memories merge on recall",
options: [
{
value: "global",
label: "Global",
description: "One shared bank — every project sees the same memories",
},
{
value: "per-project",
label: "Per project",
description: "Isolated bank per cwd basename — projects cannot see each other's memories",
},
{
value: "per-project-tagged",
label: "Per project (tagged)",
description:
"Shared bank, retains tagged with project:<cwd>. Recall surfaces project + untagged global memories together",
},
],
condition: "hindsightActive",
},
},
"hindsight.bankMission": { type: "string", default: undefined },
"hindsight.retainMission": { type: "string", default: undefined },
"hindsight.autoRecall": {
type: "boolean",
default: true,
ui: {
tab: "memory",
label: "Hindsight Auto Recall",
description: "Recall memories on the first turn of each session",
condition: "hindsightActive",
},
},
"hindsight.autoRetain": {
type: "boolean",
default: true,
ui: {
tab: "memory",
label: "Hindsight Auto Retain",
description: "Retain transcript every N turns and at session boundaries",
condition: "hindsightActive",
},
},
"hindsight.retainMode": {
type: "enum",
values: ["full-session", "last-turn"] as const,
default: "full-session",
ui: {
tab: "memory",
label: "Hindsight Retain Mode",
description: "full-session = upsert one document per session, last-turn = chunked",
options: [
{
value: "full-session",
label: "Full session",
description: "Upsert one document per session (recommended)",
},
{ value: "last-turn", label: "Last turn", description: "Chunked retention sliced by turn boundaries" },
],
condition: "hindsightActive",
},
},
"hindsight.retainEveryNTurns": { type: "number", default: 3 },
"hindsight.retainOverlapTurns": { type: "number", default: 2 },
"hindsight.retainContext": { type: "string", default: "omp" },
"hindsight.recallBudget": {
type: "enum",
values: ["low", "mid", "high"] as const,
default: "mid",
},
"hindsight.recallMaxTokens": { type: "number", default: 1024 },
"hindsight.recallContextTurns": { type: "number", default: 1 },
"hindsight.recallMaxQueryChars": { type: "number", default: 800 },
"hindsight.recallTypes": { type: "array", default: HINDSIGHT_RECALL_TYPES_DEFAULT },
"hindsight.debug": { type: "boolean", default: false },
"hindsight.mentalModelsEnabled": {
type: "boolean",
default: true,
ui: {
tab: "memory",
label: "Hindsight Mental Models",
description:
"Read curated reflect summaries (mental models) into developer instructions at boot. Loads existing models on the bank — does not write. Pair with hindsight.mentalModelAutoSeed to also auto-create the built-in seed set.",
condition: "hindsightActive",
},
},
"hindsight.mentalModelAutoSeed": {
type: "boolean",
default: true,
ui: {
tab: "memory",
label: "Hindsight Mental Model Auto-Seed",
description:
"At session start, create any built-in mental models (project-conventions, project-decisions, user-preferences) that do not yet exist on the bank.",
condition: "hindsightActive",
},
},
"hindsight.mentalModelRefreshIntervalMs": { type: "number", default: 5 * 60 * 1000 },
"hindsight.mentalModelMaxRenderChars": { type: "number", default: 16_000 },
// TTSR
"ttsr.enabled": {
type: "boolean",
default: true,
ui: {
tab: "context",
label: "TTSR",
description: "Time Traveling Stream Rules: interrupt agent when output matches patterns",
},
},
"ttsr.contextMode": {
type: "enum",
values: ["discard", "keep"] as const,
default: "discard",
ui: {
tab: "context",
label: "TTSR Context Mode",
description: "What to do with partial output when TTSR triggers",
},
},
"ttsr.interruptMode": {
type: "enum",
values: ["never", "prose-only", "tool-only", "always"] as const,
default: "always",
ui: {
tab: "context",
label: "TTSR Interrupt Mode",
description: "When to interrupt mid-stream vs inject warning after completion",
options: [
{ value: "always", label: "always", description: "Interrupt on prose and tool streams" },
{ value: "prose-only", label: "prose-only", description: "Interrupt only on reply/thinking matches" },
{ value: "tool-only", label: "tool-only", description: "Interrupt only on tool-call argument matches" },
{ value: "never", label: "never", description: "Never interrupt; inject warning after completion" },
],
},
},
"ttsr.repeatMode": {
type: "enum",
values: ["once", "after-gap"] as const,
default: "once",
ui: {
tab: "context",
label: "TTSR Repeat Mode",
description: "How rules can repeat: once per session or after a message gap",
},
},
"ttsr.repeatGap": {
type: "number",
default: 10,
ui: {
tab: "context",
label: "TTSR Repeat Gap",
description: "Messages before a rule can trigger again",
options: [
{ value: "5", label: "5 messages" },
{ value: "10", label: "10 messages" },
{ value: "15", label: "15 messages" },
{ value: "20", label: "20 messages" },
{ value: "30", label: "30 messages" },
],
},
},
// ────────────────────────────────────────────────────────────────────────
// Editing
// ────────────────────────────────────────────────────────────────────────
// Edit tool
"edit.mode": {
type: "enum",
values: EDIT_MODES,
default: "hashline",
ui: {
tab: "editing",
label: "Edit Mode",
description: "Select the edit tool variant (replace, patch, hashline, vim, or apply_patch)",
},
},
"edit.fuzzyMatch": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Fuzzy Match",
description: "Accept high-confidence fuzzy matches for whitespace differences",
},
},
"edit.fuzzyThreshold": {
type: "number",
default: 0.95,
ui: {
tab: "editing",
label: "Fuzzy Match Threshold",
description: "Similarity threshold for fuzzy matches",
options: [
{ value: "0.85", label: "0.85", description: "Lenient" },
{ value: "0.90", label: "0.90", description: "Moderate" },
{ value: "0.95", label: "0.95", description: "Default" },
{ value: "0.98", label: "0.98", description: "Strict" },
],
},
},
"edit.streamingAbort": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Abort on Failed Preview",
description: "Abort streaming edit tool calls when patch preview fails",
},
},
"edit.hashlineAutoDropPureInsertDuplicates": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Hashline Duplicate Insert Drop",
description: "Drop 2+ pure-insert payload lines that duplicate adjacent file context",
},
},
"edit.blockAutoGenerated": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Block Auto-Generated Files",
description: "Prevent editing of files that appear to be auto-generated (protoc, sqlc, swagger, etc.)",
},
},
readLineNumbers: {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Line Numbers",
description: "Prepend line numbers to read tool output by default",
},
},
readHashLines: {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Hash Lines",
description: "Include line hashes in read output for hashline edit mode (LINE+ID|content)",
},
},
"read.defaultLimit": {
type: "number",
default: 300,
ui: {
tab: "editing",
label: "Default Read Limit",
description: "Default number of lines returned when agent calls read without a limit",
options: [
{ value: "200", label: "200 lines" },
{ value: "300", label: "300 lines" },
{ value: "500", label: "500 lines" },
{ value: "1000", label: "1000 lines" },
{ value: "5000", label: "5000 lines" },
],
},
},
"read.summarize.enabled": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Read Summaries",
description: "Return structural code summaries when read is called without an explicit selector",
},
},
"read.summarize.prose": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Prose Summaries",
description: "Return structural summaries for Markdown and plain text reads",
},
},
"read.summarize.minBodyLines": {
type: "number",
default: 4,
ui: {
tab: "editing",
label: "Read Summary Body Lines",
description: "Minimum multiline body or literal length before read summaries collapse it",
},
},
"read.summarize.minCommentLines": {
type: "number",
default: 6,
ui: {
tab: "editing",
label: "Read Summary Comment Lines",
description: "Minimum multiline block comment length before read summaries collapse it",
},
},
"read.toolResultPreview": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Inline Read Previews",
description: "Render read tool results inline in the transcript instead of summary rows",
},
},
// LSP
"lsp.enabled": {
type: "boolean",
default: true,
ui: { tab: "editing", label: "LSP", description: "Enable the lsp tool for language server protocol" },
},
"lsp.formatOnWrite": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Format on Write",
description: "Automatically format code files using LSP after writing",
},
},
"lsp.diagnosticsOnWrite": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Diagnostics on Write",
description: "Return LSP diagnostics after writing code files",
},
},
"lsp.diagnosticsOnEdit": {
type: "boolean",
default: false,
ui: {
tab: "editing",
label: "Diagnostics on Edit",
description: "Return LSP diagnostics after editing code files",
},
},
// Bash interceptor
"bashInterceptor.enabled": {
type: "boolean",
default: false,
ui: { tab: "editing", label: "Bash Interceptor", description: "Block shell commands that have dedicated tools" },
},
"bashInterceptor.patterns": { type: "array", default: DEFAULT_BASH_INTERCEPTOR_RULES },
"bash.stripTrailingHeadTail": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Strip Trailing head/tail",
description:
"Silently drop trailing `| head`/`| tail` pipes from single-line bash commands. Output is already truncated automatically.",
},
},
// Shell output minimizer
"shellMinimizer.enabled": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Shell Minimizer",
description: "Compress verbose shell output (git, npm, cargo, etc.) before returning it to the agent",
},
},
"shellMinimizer.settingsPath": {
type: "string",
default: undefined,
},
"shellMinimizer.only": { type: "array", default: EMPTY_STRING_ARRAY },
"shellMinimizer.except": { type: "array", default: EMPTY_STRING_ARRAY },
"shellMinimizer.maxCaptureBytes": {
type: "number",
default: 4 * 1024 * 1024,
},
// Eval (per-backend toggles; add more as new backends ship, e.g. eval.ts)
"eval.py": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Eval: Python backend",
description: "Allow the eval tool to dispatch to the IPython kernel",
},
},
"eval.js": {
type: "boolean",
default: true,
ui: {
tab: "editing",
label: "Eval: JavaScript backend",
description: "Allow the eval tool to dispatch to the in-process JavaScript runtime",
},
},
// Python kernel knobs (consumed by the eval py backend and the /python slash command)
"python.kernelMode": {
type: "enum",
values: ["session", "per-call"] as const,
default: "session",
ui: {
tab: "editing",
label: "Python Kernel Mode",
description: "Whether to keep IPython kernel alive across calls",
},
},
// ────────────────────────────────────────────────────────────────────────
// Tools
// ────────────────────────────────────────────────────────────────────────
// Todo tool
"todo.enabled": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Todos", description: "Enable the todo_write tool for task tracking" },
},
"todo.reminders": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Todo Reminders", description: "Remind agent to complete todos before stopping" },
},
"todo.reminders.max": {
type: "number",
default: 3,
ui: {
tab: "tools",
label: "Todo Reminder Limit",
description: "Maximum reminders to complete todos before giving up",
options: [
{ value: "1", label: "1 reminder" },
{ value: "2", label: "2 reminders" },
{ value: "3", label: "3 reminders" },
{ value: "5", label: "5 reminders" },
],
},
},
"todo.eager": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Create Todos Automatically",
description: "Automatically create a comprehensive todo list after the first message",
},
},
// Search and AST tools
"find.enabled": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Find", description: "Enable the find tool for file searching" },
},
"search.enabled": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Search", description: "Enable the search tool for content searching" },
},
"search.contextBefore": {
type: "number",
default: 1,
ui: {
tab: "tools",
label: "Search Context Before",
description: "Lines of context before each search match",
options: [
{ value: "0", label: "0 lines" },
{ value: "1", label: "1 line" },
{ value: "2", label: "2 lines" },
{ value: "3", label: "3 lines" },
{ value: "5", label: "5 lines" },
],
},
},
"search.contextAfter": {
type: "number",
default: 3,
ui: {
tab: "tools",
label: "Search Context After",
description: "Lines of context after each search match",
options: [
{ value: "0", label: "0 lines" },
{ value: "1", label: "1 line" },
{ value: "2", label: "2 lines" },
{ value: "3", label: "3 lines" },
{ value: "5", label: "5 lines" },
{ value: "10", label: "10 lines" },
],
},
},
"astGrep.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "AST Grep",
description: "Enable the ast_grep tool for structural AST search",
},
},
"astEdit.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "AST Edit",
description: "Enable the ast_edit tool for structural AST rewrites",
},
},
"irc.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "IRC",
description: "Enable agent-to-agent IRC messaging via the irc tool",
},
},
// Optional tools
"renderMermaid.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Render Mermaid",
description: "Enable the render_mermaid tool for Mermaid-to-ASCII rendering",
},
},
"debug.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "Debug",
description: "Enable the debug tool for DAP-based debugging",
},
},
"calc.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Calculator",
description: "Enable the calculator tool for basic calculations",
},
},
"recipe.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "Recipe",
description:
"Enable the recipe tool when a justfile / package.json / Cargo.toml / Makefile / Taskfile is present",
},
},
"inspect_image.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Inspect Image",
description: "Enable the inspect_image tool, delegating image understanding to a vision-capable model",
},
},
"checkpoint.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Checkpoint/Rewind",
description: "Enable the checkpoint and rewind tools for context checkpointing",
},
},
// Fetching and browser
"fetch.enabled": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Read URLs", description: "Allow the read tool to fetch and process URLs" },
},
"github.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "GitHub CLI",
description:
"Enable the github tool (op-based dispatch for repository, issue, pull request, diff, search, checkout, push, and Actions watch workflows)",
},
},
"github.cache.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "GitHub view cache",
description: "Cache rendered issue/PR view output in ~/.omp/cache/github-cache.db so repeated reads are free",
},
},
"github.cache.softTtlSec": {
type: "number",
default: 300,
ui: {
tab: "tools",
label: "GitHub cache soft TTL (seconds)",
description: "Within this window, cached issue/PR view rows are returned directly. Default 5 minutes.",
},
},
"github.cache.hardTtlSec": {
type: "number",
default: 604800,
ui: {
tab: "tools",
label: "GitHub cache hard TTL (seconds)",
description:
"Past soft TTL but within hard TTL, the tool returns the cached row and refreshes it in the background. Past hard TTL, the row is dropped. Default 7 days.",
},
},
"web_search.enabled": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "Web Search", description: "Enable the web_search tool for web searching" },
},
"browser.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "Browser",
description: "Enable the browser tool (Ulixee Hero)",
},
},
"browser.headless": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "Headless Browser",
description: "Launch browser in headless mode (disable to show browser UI)",
},
},
"browser.screenshotDir": {
type: "string",
default: undefined,
ui: {
tab: "tools",
label: "Screenshot directory",
description:
"Directory to save screenshots. If unset, screenshots go to a temp file. Supports ~. Examples: ~/Downloads, ~/Desktop, /sdcard/Download (Android)",
},
},
// Tool execution
"tools.intentTracing": {
type: "boolean",
default: true,
ui: {
tab: "tools",
label: "Intent Tracing",
description: "Ask the agent to describe the intent of each tool call before executing it",
},
},
"tools.maxTimeout": {
type: "number",
default: 0,
ui: {
tab: "tools",
label: "Max Tool Timeout",
description: "Maximum timeout in seconds the agent can set for any tool (0 = no limit)",
options: [
{ value: "0", label: "No limit" },
{ value: "30", label: "30 seconds" },
{ value: "60", label: "60 seconds" },
{ value: "120", label: "120 seconds" },
{ value: "300", label: "5 minutes" },
{ value: "600", label: "10 minutes" },
],
},
},
// Async jobs
"async.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Async Execution",
description: "Enable async bash commands and background task execution",
},
},
"async.maxJobs": {
type: "number",
default: 100,
},
"async.pollWaitDuration": {
type: "enum",
values: ["5s", "10s", "30s", "1m", "5m"] as const,
default: "30s",
ui: {
tab: "tools",
label: "Poll Wait Duration",
description: "How long the poll tool waits for background job updates before returning the current state",
options: [
{ value: "5s", label: "5 seconds" },
{ value: "10s", label: "10 seconds" },
{ value: "30s", label: "30 seconds", description: "Default" },
{ value: "1m", label: "1 minute" },
{ value: "5m", label: "5 minutes" },
],
},
},
"bash.autoBackground.enabled": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Bash Auto-Background",
description: "Automatically background long-running bash commands and deliver the result later",
},
},
"bash.autoBackground.thresholdMs": {
type: "number",
default: 60_000,
},
// Tool Discovery
"tools.discoveryMode": {
type: "enum",
values: ["off", "mcp-only", "all"] as const,
default: "off",
ui: {
tab: "tools",
label: "Tool Discovery",
description:
"Hide tools behind a search tool to save tokens. 'mcp-only' hides MCP tools; 'all' hides all non-essential built-ins too.",
},
},
"tools.essentialOverride": {
type: "array",
default: [] as string[],
ui: {
tab: "tools",
label: "Essential Tools Override",
description:
"Override the always-loaded built-in tools (default: read, bash, edit). Leave empty to use defaults.",
},
},
// MCP
"mcp.enableProjectConfig": {
type: "boolean",
default: true,
ui: { tab: "tools", label: "MCP Project Config", description: "Load .mcp.json/mcp.json from project root" },
},
"mcp.discoveryMode": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "MCP Tool Discovery",
description: "Hide MCP tools by default and expose them through a tool discovery tool",
},
},
"mcp.discoveryDefaultServers": {
type: "array",
default: [] as string[],
ui: {
tab: "tools",
label: "MCP Discovery Default Servers",
description: "Keep MCP tools from these servers visible while discovery mode hides other MCP tools",
},
},
"mcp.notifications": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "MCP Update Injection",
description: "Inject MCP resource updates into the agent conversation",
},
},
"mcp.notificationDebounceMs": {
type: "number",
default: 500,
ui: {
tab: "tools",
label: "MCP Notification Debounce",
description: "Debounce window for MCP resource update notifications before injecting into conversation",
},
},
// ────────────────────────────────────────────────────────────────────────
// Tasks
// ────────────────────────────────────────────────────────────────────────
// Plan mode
"plan.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tasks",
label: "Plan Mode",
description: "Enable plan mode for read-only exploration and planning before execution",
},
},
"goal.enabled": {
type: "boolean",
default: true,
ui: {
tab: "tasks",
label: "Goal Mode",
description: "Enable per-session goal mode and the hidden goal tool",
},
},
"goal.statusInFooter": {
type: "boolean",
default: true,
ui: {
tab: "tasks",
label: "Goal Status In Footer",
description: "Show token budget alongside the goal indicator in the status line",
},
},
"goal.continuationModes": {
type: "array",
default: ["interactive"],
ui: {
tab: "tasks",
label: "Goal Continuation Modes",
description: "Run modes where active goals may auto-continue between turns",
},
},
// Delegation
"task.isolation.mode": {
type: "enum",
values: [
"none",
"auto",
"apfs",
"btrfs",
"zfs",
"reflink",
"overlayfs",
"projfs",
"block-clone",
"rcopy",
] as const,
default: "none",
ui: {
tab: "tasks",
label: "Isolation Mode",
description:
'Isolation backend for subagents. "auto" lets the native PAL pick the best available backend (CoW-aware filesystems, then overlayfs/ProjFS, then a git worktree / recursive-copy fallback).',
options: [
{ value: "none", label: "None", description: "No isolation" },
{ value: "auto", label: "Auto", description: "Let the PAL pick the best available backend" },
{ value: "apfs", label: "APFS", description: "macOS clonefile reflink (APFS)" },
{ value: "btrfs", label: "btrfs", description: "btrfs subvolume snapshot" },
{ value: "zfs", label: "ZFS", description: "ZFS snapshot + clone" },
{ value: "reflink", label: "Reflink", description: "Linux FICLONE per-file reflink" },
{
value: "overlayfs",
label: "Overlayfs",
description: "Linux kernel overlay (or fuse-overlayfs fallback)",
},
{ value: "projfs", label: "ProjFS", description: "Windows Projected File System" },
{
value: "block-clone",
label: "Block clone",
description: "Windows FSCTL_DUPLICATE_EXTENTS_TO_FILE (NTFS/ReFS)",
},
{
value: "rcopy",
label: "Recursive copy",
description: "git worktree if available, otherwise recursive copy",
},
],
},
},
"task.isolation.merge": {
type: "enum",
values: ["patch", "branch"] as const,
default: "patch",
ui: {
tab: "tasks",
label: "Isolation Merge Strategy",
description: "How isolated task changes are integrated (patch apply or branch merge)",
options: [
{ value: "patch", label: "Patch", description: "Combine diffs and git apply" },
{ value: "branch", label: "Branch", description: "Commit per task, merge with --no-ff" },
],
},
},
"task.isolation.commits": {
type: "enum",
values: ["generic", "ai"] as const,
default: "generic",
ui: {
tab: "tasks",
label: "Isolation Commit Style",
description: "Commit message style for nested repo changes (generic or AI-generated)",
options: [
{ value: "generic", label: "Generic", description: "Static commit message" },
{ value: "ai", label: "AI", description: "AI-generated commit message from diff" },
],
},
},
"task.eager": {
type: "boolean",
default: false,
ui: {
tab: "tasks",
label: "Prefer Task Delegation",
description: "Encourage the agent to delegate work to subagents unless changes are trivial",
},
},
"task.simple": {
type: "enum",
values: TASK_SIMPLE_MODES,
default: "default",
ui: {
tab: "tasks",
label: "Task Input Mode",
description: "How much shared structure the task tool accepts (default, schema-free, or independent)",
options: [
{
value: "default",
label: "Default",
description: "Shared context and custom task schema are available",
},
{
value: "schema-free",
label: "Schema-free",
description: "Shared context stays available, but custom task schema is disabled",
},
{
value: "independent",
label: "Independent",
description: "No shared context or custom task schema; each task must stand alone",
},
],
},
},
"task.maxConcurrency": {
type: "number",
default: 32,
ui: {
tab: "tasks",
label: "Max Concurrent Tasks",
description: "Concurrent limit for subagents",
options: [
{ value: "0", label: "Unlimited" },
{ value: "1", label: "1 task" },
{ value: "2", label: "2 tasks" },
{ value: "4", label: "4 tasks" },
{ value: "8", label: "8 tasks" },
{ value: "16", label: "16 tasks" },
{ value: "32", label: "32 tasks" },
{ value: "64", label: "64 tasks" },
],
},
},
"task.maxRecursionDepth": {
type: "number",
default: 2,
ui: {
tab: "tasks",
label: "Max Task Recursion",
description: "How many levels deep subagents can spawn their own subagents",
options: [
{ value: "-1", label: "Unlimited" },
{ value: "0", label: "None" },
{ value: "1", label: "Single" },
{ value: "2", label: "Double" },
{ value: "3", label: "Triple" },
],
},
},
"task.maxRuntimeMs": {
type: "number",
default: 0,
ui: {
tab: "tasks",
label: "Max Subagent Runtime",
description:
"Hard wall-clock limit per subagent (ms). 0 disables it. Defense-in-depth against provider-side stream hangs that escape the inference-layer watchdog; triggers a normal subagent abort with a 'timed out' reason.",
options: [
{ value: "0", label: "Unlimited", description: "Default" },
{ value: "300000", label: "5 minutes" },
{ value: "900000", label: "15 minutes" },
{ value: "1800000", label: "30 minutes" },
{ value: "3600000", label: "1 hour" },
],
},
},
"task.disabledAgents": {
type: "array",
default: [] as string[],
},
"task.agentModelOverrides": {
type: "record",
default: {} as Record<string, string>,
},
"tasks.todoClearDelay": {
type: "number",
default: 60,
ui: {
tab: "tasks",
label: "Todo auto-clear delay",
description: "How long to wait before removing completed/abandoned tasks from the list",
options: [
{ value: "0", label: "Instant" },
{ value: "60", label: "1 minute", description: "Default" },
{ value: "300", label: "5 minutes" },
{ value: "900", label: "15 minutes" },
{ value: "1800", label: "30 minutes" },
{ value: "3600", label: "1 hour" },
{ value: "-1", label: "Never" },
],
},
},
// Skills
"skills.enabled": { type: "boolean", default: true },
"skills.enableSkillCommands": {
type: "boolean",
default: true,
ui: { tab: "tasks", label: "Skill Commands", description: "Register skills as /skill:name commands" },
},
"skills.enableCodexUser": { type: "boolean", default: true },
"skills.enableClaudeUser": { type: "boolean", default: true },
"skills.enableClaudeProject": { type: "boolean", default: true },
"skills.enablePiUser": { type: "boolean", default: true },
"skills.enablePiProject": { type: "boolean", default: true },
"skills.customDirectories": { type: "array", default: [] as string[] },
"skills.ignoredSkills": { type: "array", default: [] as string[] },
"skills.includeSkills": { type: "array", default: [] as string[] },
// Commands
"commands.enableClaudeUser": {
type: "boolean",
default: true,
ui: { tab: "tasks", label: "Claude User Commands", description: "Load commands from ~/.claude/commands/" },
},
"commands.enableClaudeProject": {
type: "boolean",
default: true,
ui: { tab: "tasks", label: "Claude Project Commands", description: "Load commands from .claude/commands/" },
},
"commands.enableOpencodeUser": {
type: "boolean",
default: true,
ui: {
tab: "tasks",
label: "OpenCode User Commands",
description: "Load commands from ~/.config/opencode/commands/",
},
},
"commands.enableOpencodeProject": {
type: "boolean",
default: true,
ui: { tab: "tasks", label: "OpenCode Project Commands", description: "Load commands from .opencode/commands/" },
},
// ────────────────────────────────────────────────────────────────────────
// Providers
// ────────────────────────────────────────────────────────────────────────
// Secret handling
"secrets.enabled": {
type: "boolean",
default: false,
ui: { tab: "providers", label: "Hide Secrets", description: "Obfuscate secrets before sending to AI providers" },
},
// Provider selection
"providers.webSearch": {
type: "enum",
values: [
"auto",
"exa",
"brave",
"jina",
"kimi",
"zai",
"perplexity",
"anthropic",
"gemini",
"codex",
"tavily",
"kagi",
"synthetic",
"parallel",
"searxng",
] as const,
default: "auto",
ui: {
tab: "providers",
label: "Web Search Provider",
description: "Provider for web search tool",
options: [
{
value: "auto",
label: "Auto",
description: "Preferred web-search provider",
},
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP" },
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
{ value: "kimi", label: "Kimi", description: "Requires MOONSHOT_SEARCH_API_KEY or MOONSHOT_API_KEY" },
{
value: "perplexity",
label: "Perplexity",
description: "Requires PERPLEXITY_COOKIES or PERPLEXITY_API_KEY",
},
{ value: "anthropic", label: "Anthropic", description: "Uses Anthropic web search" },
{ value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
{ value: "tavily", label: "Tavily", description: "Requires TAVILY_API_KEY" },
{ value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
{ value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
{ value: "searxng", label: "SearXNG", description: "Requires SEARXNG_ENDPOINT or searxng.endpoint" },
],
},
},
"providers.image": {
type: "enum",
values: ["auto", "openai", "gemini", "openrouter"] as const,
default: "auto",
ui: {
tab: "providers",
label: "Image Provider",
description: "Provider for image generation tool",
options: [
{
value: "auto",
label: "Auto",
description: "Priority: GPT model image tool > Antigravity > OpenRouter > Gemini",
},
{ value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
{ value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
{ value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
],
},
},
"providers.kimiApiFormat": {
type: "enum",
values: ["openai", "anthropic"] as const,
default: "anthropic",
ui: {
tab: "providers",
label: "Kimi API Format",
description: "API format for Kimi Code provider",
options: [
{ value: "openai", label: "OpenAI", description: "api.kimi.com" },
{ value: "anthropic", label: "Anthropic", description: "api.moonshot.ai" },
],
},
},
"providers.openaiWebsockets": {
type: "enum",
values: ["auto", "off", "on"] as const,
default: "auto",
ui: {
tab: "providers",
label: "OpenAI WebSockets",
description: "Websocket policy for OpenAI Codex models (auto uses model defaults, on forces, off disables)",
options: [
{ value: "auto", label: "Auto", description: "Use model/provider default websocket behavior" },
{ value: "off", label: "Off", description: "Disable websockets for OpenAI Codex models" },
{ value: "on", label: "On", description: "Force websockets for OpenAI Codex models" },
],
},
},
"providers.parallelFetch": {
type: "boolean",
default: true,
ui: {
tab: "providers",
label: "Parallel Fetch",
description: "Use Parallel extract API for URL fetching when credentials are available",
},
},
// Exa
"exa.enabled": {
type: "boolean",
default: true,
ui: { tab: "providers", label: "Exa", description: "Master toggle for all Exa search tools" },
},
"exa.enableSearch": {
type: "boolean",
default: true,
ui: { tab: "providers", label: "Exa Search", description: "Basic search, deep search, code search, crawl" },
},
"exa.enableResearcher": {
type: "boolean",
default: false,
ui: { tab: "providers", label: "Exa Researcher", description: "AI-powered deep research tasks" },
},
"exa.enableWebsets": {
type: "boolean",
default: false,
ui: { tab: "providers", label: "Exa Websets", description: "Webset management and enrichment tools" },
},
// SearXNG
"searxng.endpoint": {
type: "string",
default: undefined,
ui: {
tab: "providers",
label: "SearXNG Endpoint",
description: "Self-hosted search base URL",
},
},
"searxng.token": {
type: "string",
default: undefined,
},
"searxng.basicUsername": {
type: "string",
default: undefined,
},
"searxng.basicPassword": {
type: "string",
default: undefined,
},
"searxng.categories": {
type: "string",
default: undefined,
},
"searxng.language": {
type: "string",
default: undefined,
},
"commit.mapReduceEnabled": { type: "boolean", default: true },
"commit.mapReduceMinFiles": { type: "number", default: 4 },
"commit.mapReduceMaxFileTokens": { type: "number", default: 50000 },
"commit.mapReduceTimeoutMs": { type: "number", default: 120000 },
"commit.mapReduceMaxConcurrency": { type: "number", default: 5 },
"commit.changelogMaxDiffChars": { type: "number", default: 120000 },
"dev.autoqa": {
type: "boolean",
default: false,
ui: {
tab: "tools",
label: "Auto QA",
description: "Enable automated tool issue reporting (report_tool_issue) for all agents",
},
},
"thinkingBudgets.minimal": { type: "number", default: 1024 },
"thinkingBudgets.low": { type: "number", default: 2048 },
"thinkingBudgets.medium": { type: "number", default: 8192 },
"thinkingBudgets.high": { type: "number", default: 16384 },
"thinkingBudgets.xhigh": { type: "number", default: 32768 },
} as const;
// ═══════════════════════════════════════════════════════════════════════════
// Type Inference
// ═══════════════════════════════════════════════════════════════════════════
type Schema = typeof SETTINGS_SCHEMA;
/** All valid setting paths */
export type SettingPath = keyof Schema;
/** Infer the value type for a setting path */
export type SettingValue<P extends SettingPath> = Schema[P] extends { type: "boolean" }
? boolean
: Schema[P] extends { type: "string" }
? string | undefined
: Schema[P] extends { type: "number" }
? number
: Schema[P] extends { type: "enum"; values: infer V }
? V extends readonly string[]
? V[number]
: never
: Schema[P] extends { type: "array"; default: infer D }
? D
: Schema[P] extends { type: "record"; default: infer D }
? D
: never;
/** Get the default value for a setting path */
export function getDefault<P extends SettingPath>(path: P): SettingValue<P> {
return SETTINGS_SCHEMA[path].default as SettingValue<P>;
}
/** Check if a path has UI metadata (should appear in settings panel) */
export function hasUi(path: SettingPath): boolean {
return "ui" in SETTINGS_SCHEMA[path];
}
/** Get UI metadata for a path (undefined if no UI) */
export function getUi(path: SettingPath): AnyUiMetadata | undefined {
const def = SETTINGS_SCHEMA[path];
return "ui" in def ? (def.ui as AnyUiMetadata) : undefined;
}
/** Get all paths for a specific tab */
export function getPathsForTab(tab: SettingTab): SettingPath[] {
return (Object.keys(SETTINGS_SCHEMA) as SettingPath[]).filter(path => {
const ui = getUi(path);
return ui?.tab === tab;
});
}
/** Get the type of a setting */
export function getType(path: SettingPath): SettingDef["type"] {
return SETTINGS_SCHEMA[path].type;
}
/** Get enum values for an enum setting */
export function getEnumValues(path: SettingPath): readonly string[] | undefined {
const def = SETTINGS_SCHEMA[path];
return "values" in def ? (def.values as readonly string[]) : undefined;
}
// ═══════════════════════════════════════════════════════════════════════════
// Derived Types from Schema
// ═══════════════════════════════════════════════════════════════════════════
/** Status line preset - derived from schema */
export type StatusLinePreset = SettingValue<"statusLine.preset">;
/** Status line separator style - derived from schema */
export type StatusLineSeparatorStyle = SettingValue<"statusLine.separator">;
/** Tree selector filter mode - derived from schema */
export type TreeFilterMode = SettingValue<"treeFilterMode">;
// ═══════════════════════════════════════════════════════════════════════════
// Typed Group Definitions
// ═══════════════════════════════════════════════════════════════════════════
export interface CompactionSettings {
enabled: boolean;
strategy: "context-full" | "handoff" | "off";
thresholdPercent: number;
thresholdTokens: number;
reserveTokens: number;
keepRecentTokens: number;
handoffSaveToDisk: boolean;
autoContinue: boolean;
remoteEnabled: boolean;
remoteEndpoint: string | undefined;
idleEnabled: boolean;
idleThresholdTokens: number;
idleTimeoutSeconds: number;
}
export interface ContextPromotionSettings {
enabled: boolean;
}
export interface RetrySettings {
enabled: boolean;
maxRetries: number;
baseDelayMs: number;
}
export interface MemoriesSettings {
enabled: boolean;
maxRolloutsPerStartup: number;
maxRolloutAgeDays: number;
minRolloutIdleHours: number;
threadScanLimit: number;
maxRawMemoriesForGlobal: number;
stage1Concurrency: number;
stage1LeaseSeconds: number;
stage1RetryDelaySeconds: number;
phase2LeaseSeconds: number;
phase2RetryDelaySeconds: number;
phase2HeartbeatSeconds: number;
rolloutPayloadPercent: number;
fallbackTokenLimit: number;
summaryInjectionTokenLimit: number;
}
export interface TodoCompletionSettings {
enabled: boolean;
maxReminders: number;
}
export interface BranchSummarySettings {
enabled: boolean;
reserveTokens: number;
}
export interface SkillsSettings {
enabled?: boolean;
enableSkillCommands?: boolean;
enableCodexUser?: boolean;
enableClaudeUser?: boolean;
enableClaudeProject?: boolean;
enablePiUser?: boolean;
enablePiProject?: boolean;
customDirectories?: string[];
ignoredSkills?: string[];
includeSkills?: string[];
disabledExtensions?: string[];
}
export interface CommitSettings {
mapReduceEnabled: boolean;
mapReduceMinFiles: number;
mapReduceMaxFileTokens: number;
mapReduceTimeoutMs: number;
mapReduceMaxConcurrency: number;
changelogMaxDiffChars: number;
}
export interface TtsrSettings {
enabled: boolean;
contextMode: "discard" | "keep";
interruptMode: "never" | "prose-only" | "tool-only" | "always";
repeatMode: "once" | "after-gap";
repeatGap: number;
}
export interface ExaSettings {
enabled: boolean;
enableSearch: boolean;
enableResearcher: boolean;
enableWebsets: boolean;
}
export interface StatusLineSettings {
preset: StatusLinePreset;
separator: StatusLineSeparatorStyle;
showHookStatus: boolean;
leftSegments: StatusLineSegmentId[];
rightSegments: StatusLineSegmentId[];
segmentOptions: Record<string, unknown>;
}
export interface ThinkingBudgetsSettings {
minimal: number;
low: number;
medium: number;
high: number;
xhigh: number;
}
export interface SttSettings {
enabled: boolean;
language: string | undefined;
modelName: string;
whisperPath: string | undefined;
modelPath: string | undefined;
}
export interface BashInterceptorRule {
pattern: string;
flags?: string;
tool: string;
message: string;
allowSubcommands?: string[];
}
export interface ShellMinimizerSettings {
enabled: boolean;
settingsPath: string | undefined;
only: string[];
except: string[];
maxCaptureBytes: number;
}
/** Map group prefix -> typed settings interface */
export interface GroupTypeMap {
compaction: CompactionSettings;
contextPromotion: ContextPromotionSettings;
retry: RetrySettings;
memories: MemoriesSettings;
branchSummary: BranchSummarySettings;
skills: SkillsSettings;
commit: CommitSettings;
ttsr: TtsrSettings;
exa: ExaSettings;
statusLine: StatusLineSettings;
thinkingBudgets: ThinkingBudgetsSettings;
stt: SttSettings;
modelRoles: Record<string, string>;
modelTags: ModelTagsSettings;
cycleOrder: string[];
shellMinimizer: ShellMinimizerSettings;
}
export type GroupPrefix = keyof GroupTypeMap;