4.5 KiB
6. State Management
A single immutable store with 50+ fields — how Claude Code manages application state.
Architecture
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
graph TB
subgraph Store["AppState — Single Immutable Store"]
direction TB
subgraph Core_S["Core Session State"]
Model["mainLoopModel"]
Think["thinkingEnabled"]
Fast["fastMode"]
Effort["effortValue"]
Settings["settings: SettingsJson"]
end
subgraph Perm_S["Permission State"]
TPC["toolPermissionContext"]
Mode["mode: default / plan / auto / bypass"]
Allow["alwaysAllowRules"]
Deny["alwaysDenyRules"]
end
subgraph MCP_S["MCP State"]
MCPCli["clients: MCPServerConnection array"]
MCPTool["tools: Tool array"]
MCPCmd["commands: Command array"]
end
subgraph Task_S["Background Tasks"]
Tasks["tasks: taskId to TaskState map"]
AgentReg["agentNameRegistry"]
FG["foregroundedTaskId"]
end
subgraph UI_S["UI State"]
Spec["speculation: predictive execution"]
Suggest["promptSuggestion: autocomplete"]
Notif["notifications queue"]
Bridge["replBridge: remote control state"]
end
subgraph History_S["History and Tracking"]
FH["fileHistory: snapshots for rewind"]
Attr["attribution: commit metadata"]
Todos["todos: per-agent lists"]
end
end
REPL_C["REPL.tsx<br/>reads + subscribes"]:::consumer
QE_C["QueryEngine<br/>reads via getAppState"]:::consumer
Tools_C["Tools<br/>reads via ToolUseContext"]:::consumer
SET["setAppState<br/>functional update"]:::mutator
ONCHANGE["onChangeAppState<br/>side effect reactions"]:::mutator
Store --> REPL_C
Store --> QE_C
Store --> Tools_C
SET --> Store
ONCHANGE --> Store
classDef consumer fill:#1b3a1b,stroke:#28a745,color:#e0e0e0,stroke-width:2px
classDef mutator fill:#2d1b4e,stroke:#e83e8c,color:#e0e0e0,stroke-width:2px
Core Concepts
Immutability via DeepImmutable<T>
The AppState type is wrapped in DeepImmutable<T> — TypeScript enforces that no consumer can mutate state in place:
export type AppState = DeepImmutable<{
settings: SettingsJson
mainLoopModel: ModelSetting
toolPermissionContext: ToolPermissionContext
// ... 50+ fields
}>
Functional Updates
State is updated via setAppState(prev => newState):
setAppState(prev => ({
...prev,
toolPermissionContext: {
...prev.toolPermissionContext,
mode: 'plan',
},
}))
Side Effects via onChangeAppState
After state changes, onChangeAppState.ts fires reactive side effects — persisting settings, updating UI, notifying remote sessions, etc.
Key State Groups
Session State
mainLoopModel, thinkingEnabled, fastMode, effortValue — control how the model behaves each turn.
Permission State
toolPermissionContext — contains mode, allow/deny rules, and bypass availability. See Guide 4.
MCP State
mcp.clients, mcp.tools, mcp.commands — dynamically connected MCP servers and their exposed tools/commands.
Background Tasks
tasks — a map of taskId → TaskState for background agent tasks. foregroundedTaskId controls which task's messages appear in the main view.
UI State
speculation — predictive execution state for pre-computing responses. promptSuggestion — autocomplete suggestions. notifications — queued UI notifications.
History
fileHistory — snapshots for /rewind. attribution — commit metadata for git attribution. todos — per-agent task lists.
Key Files
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#1a1a2e', 'primaryTextColor': '#e0e0e0', 'lineColor': '#4a9eff', 'primaryBorderColor': '#4a9eff'}}}%%
graph LR
subgraph StateDir["src/state/"]
AS["AppState.tsx<br/>React context + hooks"]
ASS["AppStateStore.ts<br/>Type definition + defaults"]
OC["onChangeAppState.ts<br/>Side effect reactions"]
SEL["selectors.ts<br/>Derived state"]
ST["store.ts<br/>Store type"]
end
Previous: ← Context Management · Next: Extension Model →