- 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.
2881 lines
84 KiB
TypeScript
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;
|