# Settings reference

> `GlobalSettings` fields that control workspaces, terminals, agents, themes, notifications, and integration toggles—persisted by the main-process store.

- Repository: stablyai/orca
- GitHub: https://github.com/stablyai/orca
- Human docs: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c
- Complete Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/llms-full.txt

## Source Files

- `src/shared/types.ts`
- `src/main/persistence/index.ts`
- `src/renderer/src/components/settings/RepositoryHooksSection.tsx`
- `docs/STYLEGUIDE.md`
- `src/renderer/src/assets/main.css`
- `components.json`

---

---
title: "Settings reference"
description: "`GlobalSettings` fields that control workspaces, terminals, agents, themes, notifications, and integration toggles—persisted by the main-process store."
---

`GlobalSettings` is the typed user-preference object inside Orca's main-process `PersistedState`. On desktop it is written to `orca-data.json` under Electron `userData` (path captured at startup via `initDataPath()` in `src/main/persistence.ts`). Defaults come from `getDefaultSettings()` in `src/shared/constants.ts`; the canonical field list and inline semantics live on `GlobalSettings` in `src/shared/types.ts`. The renderer reads and writes through IPC (`settings:get`, `settings:set`) and the Zustand `settings` slice (`src/renderer/src/store/slices/settings.ts`).

## What is and is not `GlobalSettings`

`PersistedState` bundles several persistence domains. Only `settings` is `GlobalSettings`.

| Domain | Type | Examples |
| --- | --- | --- |
| `settings` | `GlobalSettings` | Theme, workspace directory, terminal font, default agent |
| `ui` | `PersistedUIState` | Sidebar width, sort mode, workspace statuses, window bounds, browser defaults |
| `repos[]` | `Repo` | Per-repo hooks (`hookSettings`), issue source, symlink paths |
| Other top-level keys | Various | `sshTargets`, `automations`, `workspaceSession`, caches |

<Note>
`rightSidebarOpenByDefault` on `GlobalSettings` is deprecated. New sidebar open/closed state belongs on `PersistedUIState.rightSidebarOpen`. Load-time migration copies the legacy value when `ui.rightSidebarOpen` is absent.
</Note>

Keyboard shortcut overrides are stored in `~/.orca/keybindings.json`. `GlobalSettings.keybindings` is a legacy field migrated once from disk; new writes go to the file (`src/main/keybindings/keybinding-file.ts`).

## Persistence flow

```mermaid
flowchart LR
  subgraph renderer["Renderer"]
    UI["Settings panes"]
    Store["Zustand settings slice"]
  end
  subgraph main["Main process"]
    IPC["settings:get / settings:set"]
    StoreMain["Store.updateSettings"]
    Disk["orca-data.json"]
  end
  UI --> Store
  Store --> IPC
  IPC --> StoreMain
  StoreMain --> Disk
  StoreMain -->|"debounced 300ms"| Disk
```

On load, `Store.load()` shallow-merges `{ ...defaults.settings, ...parsed.settings }` and runs normalizers (notifications, voice, terminal quick commands, task providers, floating-workspace paths, one-shot migrations). On save, `scheduleSave()` debounces writes by **300 ms** and encrypts `opencodeSessionCookie` (and `ui.browserKagiSessionLink`) with Electron `safeStorage` when available.

### Update semantics

`updateSettings(updates: Partial<GlobalSettings>)` shallow-merges top-level keys. Nested blocks are special-cased:

| Nested key | Merge behavior |
| --- | --- |
| `notifications` | Deep-merge, then `normalizeNotificationSettings()` (sound id aliases, volume clamped 0–100) |
| `telemetry` | Deep-merge so partial consent updates do not drop `installId` |
| `terminalQuickCommands` | Replaced list passed through `normalizeTerminalQuickCommands()` |
| `visibleTaskProviders` / `defaultTaskSource` | Co-normalized via `normalizeTaskProviderSettings()` |
| `workspaceDir` / `nestWorkspaces` | May append prior layout to `workspaceDirHistory` |

IPC `settings:set` strips `floatingTerminalTrustedCwds` from renderer input (only the main-process directory picker may grant trust), sanitizes `floatingTerminalCwd`, applies `nativeTheme.themeSource` when `theme` changes, and toggles agent awake / hook installers when related flags change.

## Defaults and optional fields

`getDefaultSettings(homedir)` supplies every required field. Several keys are optional on the type for backward compatibility; load merge and UI code treat them as present at runtime:

| Optional key | Default hydration |
| --- | --- |
| `voice` | `getDefaultVoiceSettings()` merged on load |
| `githubProjects` | Empty pinned/recent/active structure in defaults |
| `gitlabProjects` | Filled by merge when GitLab UI writes |
| `commitMessageAi` | Default-on with empty model maps in defaults |
| `telemetry` | Populated by first-launch migration in `Store.load()` |
| `ctrlTabOrderMode`, `showMobileButton`, etc. | Readers default (e.g. MRU tab order, show mobile button) |

Default workspace root: `` `${homedir}/orca/workspaces` `` with `nestWorkspaces: true`.

## Field reference by area

Settings UI panes map to fields below (see `buildSettingsNavigationMetadata()` in `src/renderer/src/hooks/useSettingsNavigationMetadata.ts`). Per-repo hook scripts and policies are **not** `GlobalSettings`; they live on `Repo.hookSettings` and are edited in repository settings.

### Workspace and worktrees

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `workspaceDir` | `string` | `~/orca/workspaces` | Root directory for Orca-created worktrees |
| `nestWorkspaces` | `boolean` | `true` | Nest each worktree under `workspaceDir/<repo>/...` |
| `workspaceDirHistory` | `OrcaWorkspaceLayout[]` | `[]` | Prior `(path, nestWorkspaces)` pairs when layout changes |
| `refreshLocalBaseRefOnWorktreeCreate` | `boolean` | `false` | Refresh local base ref when creating a worktree |
| `skipDeleteWorktreeConfirm` | `boolean` | `false` | Skip destructive worktree delete confirmation |
| `skipDeleteAutomationConfirm` | `boolean` | `false` | Skip automation delete confirmation |
| `setupScriptLaunchMode` | `'new-tab'` \| `'split-vertical'` \| `'split-horizontal'` | `'new-tab'` | Where repo setup script runs on create |
| `experimentalWorktreeSymlinks` | `boolean` | `false` | Opt-in symlink paths from primary checkout (also needs per-repo `symlinkPaths`) |
| `defaultTuiAgent` | `TuiAgent` \| `'blank'` \| `null` | `null` | Composer default agent (`null` = first detected) |

### Git, branches, and source control UI

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `branchPrefix` | `'git-username'` \| `'custom'` \| `'none'` | `'git-username'` | Branch naming prefix mode |
| `branchPrefixCustom` | `string` | `''` | Custom prefix when `branchPrefix === 'custom'` |
| `enableGitHubAttribution` | `boolean` | `false` | Co-author attribution on commits |
| `showGitIgnoredFiles` | `boolean` | `true` | Show ignored files in explorer (optional on type) |
| `sourceControlViewMode` | `'list'` \| `'tree'` | `'list'` | Changes view layout |
| `diffDefaultView` | `'inline'` \| `'side-by-side'` | `'inline'` | Diff viewer mode |
| `combinedDiffFileTreeVisibleByDefault` | `boolean` | `false` | Combined diff file tree |
| `commitMessageAi` | `CommitMessageAiSettings` | enabled, empty maps | AI commit message agent/model/prompt config |

`CommitMessageAiSettings` fields: `enabled`, `agentId` (`TuiAgent` \| `'custom'` \| `null`), `selectedModelByAgent`, `selectedModelByAgentByHost`, `discoveredModelsByAgent`, `discoveredModelsByAgentByHost`, `selectedThinkingByModel`, `customPrompt`, `customAgentCommand`.

### Appearance and editor

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `theme` | `'system'` \| `'dark'` \| `'light'` | `'system'` | App chrome + document theme |
| `appFontFamily` | `string` | product default font | UI font |
| `editorAutoSave` | `boolean` | `false` | Markdown/editor auto-save |
| `editorAutoSaveDelayMs` | `number` | constant default | Debounce for auto-save |
| `editorMinimapEnabled` | `boolean` | `false` | Editor minimap |
| `markdownReviewToolsEnabled` | `boolean` | `true` | Local markdown review UI |
| `showTitlebarAppName` | `boolean` | `true` | Titlebar branding |
| `showTasksButton` | `boolean` | `true` | Tasks sidebar entry |
| `showMobileButton` | `boolean` | `true` | Mobile shortcut in sidebar |
| `ctrlTabOrderMode` | `'mru'` \| `'sequential'` | `'mru'` | Ctrl+Tab ordering |
| `windowBackgroundBlur` | `boolean` | `false` | Window blur effect |
| `rightSidebarOpenByDefault` | `boolean` | `true` | **Deprecated** — use `PersistedUIState.rightSidebarOpen` |

### Input and clipboard

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `primarySelectionMiddleClickPaste` | `boolean` | platform-aware | Middle-click paste from primary selection |
| `terminalClipboardOnSelect` | `boolean` | `false` | Copy selection to system clipboard on select |
| `terminalAllowOsc52Clipboard` | `boolean` | `false` | Allow OSC 52 clipboard writes (SSH/TUI) |
| `terminalRightClickToPaste` | `boolean` | `true` | Windows right-click paste |
| `terminalMacOptionAsAlt` | `'auto'` \| `'true'` \| `'false'` \| `'left'` \| `'right'` | `'auto'` | macOS Option key as Meta |
| `terminalJISYenToBackslash` | `boolean` | `false` | JIS ¥ key → backslash |

### Terminal

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `terminalFontSize` | `number` | `14` | Font size |
| `terminalFontFamily` | `string` | platform default | Font family |
| `terminalFontWeight` | `number` | default weight | Font weight |
| `terminalLineHeight` | `number` | `1` | Line height multiplier |
| `terminalGpuAcceleration` | `'auto'` \| `'on'` \| `'off'` | `'auto'` | WebGL vs DOM (Linux `auto` → DOM in renderer) |
| `terminalLigatures` | `'auto'` \| `'on'` \| `'off'` | `'auto'` | Programming ligatures addon |
| `terminalCursorStyle` | `'bar'` \| `'block'` \| `'underline'` | `'bar'` | Cursor shape |
| `terminalCursorBlink` | `boolean` | `true` | Cursor blink |
| `terminalCursorOpacity` | `number` | optional | Cursor opacity |
| `terminalThemeDark` / `terminalThemeLight` | `string` | Ghostty/Tango names | Named terminal themes |
| `terminalUseSeparateLightTheme` | `boolean` | `true` | Separate light theme |
| `terminalDividerColorDark` / `Light` | `string` | hex colors | Pane divider colors |
| `terminalInactivePaneOpacity` / `terminalActivePaneOpacity` | `number` | `0.8` / `1` | Pane opacity |
| `terminalPaneOpacityTransitionMs` | `number` | `140` | Opacity transition |
| `terminalDividerThicknessPx` | `number` | `3` | Divider thickness |
| `terminalBackgroundOpacity` | `number` | optional | Background opacity |
| `terminalColorOverrides` | `TerminalColorOverrides` | optional | Per-color ANSI overrides |
| `terminalPaddingX` / `terminalPaddingY` | `number` | optional | Inner padding |
| `terminalMouseHideWhileTyping` | `boolean` | `false` | Hide pointer while typing |
| `terminalWordSeparator` | `string` | optional | Word boundary chars |
| `terminalFocusFollowsMouse` | `boolean` | `false` | Focus follows mouse |
| `terminalScrollbackBytes` | `number` | `10_000_000` | Scrollback limit |
| `terminalScopeHistoryByWorktree` | `boolean` | `true` | Per-worktree shell history |
| `terminalWindowsShell` | `string` | `'powershell.exe'` | Windows default shell binary |
| `terminalWindowsPowerShellImplementation` | `'auto'` \| `'powershell.exe'` \| `'pwsh.exe'` | `'auto'` | PowerShell 7 vs inbox |
| `terminalShortcutPolicy` | `'orca-first'` \| `'terminal-first'` | `'orca-first'` | Shortcut ownership vs TUIs |
| `terminalQuickCommands` | `TerminalQuickCommand[]` | built-in defaults | Saved commands (global or per-repo scope) |

### Floating workspace

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `floatingTerminalEnabled` | `boolean` | `true` | Global floating workspace surface |
| `floatingTerminalCwd` | `string` | `'~'` | Initial cwd for new floating terminal tabs |
| `floatingTerminalTrustedCwds` | `string[]` | `[]` | Picker-approved directories (main-only writes) |
| `floatingTerminalTriggerLocation` | `'floating-button'` \| `'status-bar'` | `'floating-button'` | Toggle placement |
| `floatingTerminalDefaultedForAllUsers` | `boolean` | migration flag | One-shot default-on migration |
| `floatingTerminalCwdMigratedToAppWorkspace` | `boolean` | migration flag | Legacy cwd trust migration |

### Agents and hooks

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `agentCmdOverrides` | `Partial<Record<TuiAgent, string>>` | `{}` | Override CLI binary per agent |
| `agentStatusHooksEnabled` | `boolean` | `true` | Install/manage global agent status hooks |
| `keepComputerAwakeWhileAgentsRun` | `boolean` | `false` | macOS awake assertions while agents work |
| `promptCacheTimerEnabled` | `boolean` | `false` | Show prompt-cache countdown UI |
| `promptCacheTtlMs` | `300_000` \| `3_600_000` | `300_000` | Cache TTL (5 min or 1 hr) |

`TuiAgent` is a large string union (`claude`, `codex`, `gemini`, `cursor`, `grok`, …) defined beside `GlobalSettings` in `src/shared/types.ts`.

### Tasks and integrations

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `defaultTaskViewPreset` | `TaskViewPresetId` | `'all'` | Default Tasks view filter |
| `defaultTaskSource` | `TaskProvider` | `'github'` | Last-used provider (`github`, `gitlab`, `linear`) |
| `visibleTaskProviders` | `TaskProvider[]` | all three | Providers shown in UI |
| `defaultRepoSelection` | `string[]` \| `null` | `null` | Sticky-all vs frozen repo id list |
| `defaultLinearTeamSelection` | `string[]` \| `null` | `null` | Sticky-all vs frozen team ids |
| `githubProjects` | `GitHubProjectSettings` | empty structure | Pinned/recent/active GitHub Project |
| `gitlabProjects` | `GitLabProjectSettings` | optional | Pinned/recent GitLab projects |
| `openLinksInApp` | `boolean` | `true` | Open links in Orca browser vs system |
| `openInApplications` | `OpenInApplication[]` | `[]` | Extra “Open in …” launcher rows |

### Managed provider accounts

| Field | Type | Role |
| --- | --- | --- |
| `codexManagedAccounts` | `CodexManagedAccount[]` | Codex account switching (shared `~/.codex` prep) |
| `activeCodexManagedAccountId` | `string` \| `null` | Active Codex account |
| `claudeManagedAccounts` | `ClaudeManagedAccount[]` | Claude auth material (no `CLAUDE_CONFIG_DIR` fork) |
| `activeClaudeManagedAccountId` | `string` \| `null` | Active Claude account |
| `opencodeSessionCookie` | `string` | OpenCode Go session (encrypted on disk) |
| `opencodeWorkspaceId` | `string` | Optional workspace override for usage API |
| `geminiCliOAuthEnabled` | `boolean` | `false` | Opt-in Gemini CLI OAuth extraction for rate limits |

### Notifications

`notifications` is a required `NotificationSettings` object:

<ParamField body="enabled" type="boolean" default="true">
Master switch for native desktop notifications.
</ParamField>

<ParamField body="agentTaskComplete" type="boolean" default="true">
Notify when an agent task completes.
</ParamField>

<ParamField body="terminalBell" type="boolean" default="false">
Notify on terminal bell.
</ParamField>

<ParamField body="suppressWhenFocused" type="boolean" default="true">
Suppress notifications while Orca is focused.
</ParamField>

<ParamField body="customSoundId" type="enum" default="system">
Built-in ids: `system`, `two-tone`, `bong`, `thump`, `blip`, `sonar`, `blop`, `ding`, `clack`, `beep`, or `custom`.
</ParamField>

<ParamField body="customSoundPath" type="string | null" default="null">
Filesystem path when `customSoundId === 'custom'`.
</ParamField>

<ParamField body="customSoundVolume" type="number" default="100">
Clamped to 0–100 on save.
</ParamField>

Dispatch uses `NotificationDispatchRequest` (`source`, optional `paneKey`, agent metadata, etc.) from the main notification pipeline.

### Runtime environments and mobile

| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `activeRuntimeEnvironmentId` | `string` \| `null` | `null` | Target for client-routed RPC (`null` = local desktop) |
| `experimentalMobile` | `boolean` | `false` | Mobile companion experimental features |
| `mobileAutoRestoreFitMs` | `number` \| `null` | `null` | Auto-restore desktop PTY size after mobile disconnect; clamped 5s–60m on read |

Switching `activeRuntimeEnvironmentId` clears renderer repo/worktree/terminal state and refetches from the new server (`switchRuntimeEnvironment` in the settings slice).

### Voice (dictation)

`voice` is optional on the type but merged to `VoiceSettings` on load:

| Field | Type | Default (via `getDefaultVoiceSettings`) |
| --- | --- | --- |
| `enabled` | `boolean` | `false` |
| `sttModel` | `string` | `''` |
| `modelsDir` | `string` | `''` |
| `language` | `string` | `'en'` |
| `dictationMode` | `'toggle'` \| `'hold'` | `'toggle'` |
| `terminalConfirmBeforeInsert` | `boolean` | `false` |
| `userModels` | `UserModelConfig[]` | `[]` |

### Privacy / telemetry

`telemetry` holds consent and identity only (no heartbeat counters):

| Field | Type | Role |
| --- | --- | --- |
| `optedIn` | `boolean` \| `null` | `true` new installs; `null` until banner for upgraded users |
| `installId` | `string` | Anonymous UUID v4 |
| `existedBeforeTelemetryRelease` | `boolean` | Cohort marker for first-launch banner |

Product telemetry emits `settings_changed` only for keys in `SETTINGS_CHANGED_WHITELIST` (`editorAutoSave`, `openLinksInApp`, `experimentalMobile`, `experimentalPet`, `experimentalActivity`, `experimentalWorktreeSymlinks`, `geminiCliOAuthEnabled`).

### Experimental flags

| Field | Default | UI surface |
| --- | --- | --- |
| `experimentalPet` | `false` | Animated pet overlay (visibility also in `PersistedUIState.petVisible`) |
| `experimentalActivity` | `false` | Left-sidebar Agents feed |
| `experimentalWorktreeSymlinks` | `false` | Worktree symlink creation |
| `experimentalSidekick` | legacy read | Migrated to `experimentalPet` on load |

Hidden experimental toggles (Shift-click Experimental in Settings) may expose additional controls; they still persist through the same `settings:set` path.

### One-shot migration guards

Several `*Migrated*` / `*Defaulted*` boolean fields exist only to preserve explicit user choices across default rollouts (`terminalMacOptionAsAltMigrated`, `experimentalActivityDefaultedOffForAllUsers`, `primarySelectionMiddleClickPasteDefaultedForTerminalDefaults`, etc.). Application code should not treat them as user-facing preferences; they are written during `Store.load()` when a migration runs.

## Web client storage

The paired web client (`src/renderer/src/web/web-preload-api.ts`) keeps a partial `GlobalSettings` blob in browser `localStorage`, merged with `getDefaultSettings('~')` and web-specific overrides (`floatingTerminalEnabled: false`, `activeRuntimeEnvironmentId` from the paired environment). It does not write `orca-data.json`; authoritative desktop state remains on the machine running the Orca main process.

## Programmatic access

```ts
// Renderer (after preload bridge)
const settings = await window.api.settings.get()
await window.api.settings.set({ theme: 'dark' })

// Main process
store.getSettings()
store.updateSettings({ agentStatusHooksEnabled: false })
```

Ghostty terminal import preview: `settings:previewGhosttyImport` returns a `GhosttyImportPreview` with `diff: Partial<GlobalSettings>` for applicable keys.

## Related pages

<CardGroup>
  <Card title="Worktrees and repos" href="/worktrees-and-repos">
    Workspace directory layout, repo registration, and per-repo fields outside `GlobalSettings`.
  </Card>
  <Card title="Repository hooks" href="/repository-hooks">
    `Repo.hookSettings`, `orca.yaml` scripts, and hook command-source policies.
  </Card>
  <Card title="Terminals and agents" href="/terminals-and-agents">
    PTY lifecycle, `TuiAgent` detection, env injection, and agent status hooks.
  </Card>
  <Card title="Runtime environments" href="/runtime-environments">
    `activeRuntimeEnvironmentId`, pairing, and remote RPC routing.
  </Card>
  <Card title="Source control integrations" href="/source-control-integrations">
    GitHub/GitLab project settings and review flows backed by `githubProjects` / `gitlabProjects`.
  </Card>
  <Card title="Telemetry and privacy" href="/telemetry-privacy">
    Consent banner, `telemetry` fields, and event boundaries.
  </Card>
</CardGroup>
