状态管理
DeepImmutable 状态树
DeepImmutable 类型约束 + 函数式更新,在无框架依赖下实现可预测状态管理
学习
概念讲解与核心设计分析
18.1 AppState 全局状态类型
Claude Code 的状态管理核心是 state/ 目录中定义的 AppState 类型。这是一个包含 300+ 个字段的巨型状态对象,按功能域组织为多个区块:
- Core — 核心状态:conversationId, messages, model, currentTool, isLoading, error 等基础运行状态
- Permissions — 权限状态:grantedPermissions, deniedPermissions, permissionMode, autoApprovePatterns
- MCP — MCP 连接状态:mcpClients, mcpTools, mcpResources, mcpConnectionStatus
- Plugins — 插件状态:installedPlugins, pluginConfigs, pluginErrors
- Tasks — 任务状态:taskList, activeTask, taskDependencies
- Agents — 代理状态:activeAgents, coordinatorState, swarmBackend
- Speculation — 推测执行状态:speculationState, specBoundary, writtenPaths
- Remote — 远程连接状态:remoteSession, bridgeStatus, jwtToken
- Kairos — Kairos 集成状态:kairosEnabled, kairosSession
- Computer Use — 计算机操控状态:screenState, appDetection, inputMode
18.2 DeepImmutable 不可变包装
AppState 通过 DeepImmutable<T> 类型包装器在类型层面强制不可变性。这是一个递归的 TypeScript 工具类型,将所有属性、嵌套对象和数组都标记为 readonly:
- 防止直接修改状态字段(编译时报错)
- 强制所有状态更新通过 setAppState 函数进行
- 数组变为 ReadonlyArray,Map 变为 ReadonlyMap
18.3 Store 模式
createStore<T> 提供了一个极简的 Store 实现,是整个状态管理系统的基础原语:
- get() — 获取当前状态快照
- set(updater) — 通过更新函数设置新状态。接受
(prev: T) => T或直接值 - subscribe(listener) — 订阅状态变更,返回取消订阅函数
AppStateStore.ts 基于 createStore 创建了全局唯一的应用状态存储实例。
18.4 状态变更副作用
onChangeAppState.ts 注册了一组状态变更监听器,当特定字段变化时触发副作用:
- 当 model 变更时,重新计算 token 限制和工具可用性
- 当 permissions 变更时,更新工具的权限检查缓存
- 当 mcpClients 变更时,重新注册 MCP 工具
- 当 speculationState 变更时,触发推测执行的启动或中止
18.5 SpeculationState 推测执行状态
推测执行使用专门的状态结构追踪其生命周期:
- idle — 空闲状态,等待触发条件
- active — 活跃状态,包含 abort() 中止函数、startTime 启动时间、messagesRef 消息引用、writtenPathsRef 已写入路径集合、boundary 推测边界
18.6 bootstrap/state.ts 与 selectors
bootstrap/state.ts 提供全局单例状态,不依赖 React 上下文。这使得非 UI 代码(如工具执行、MCP 处理)也能访问应用状态。
selectors.ts 提供派生状态计算,将原始状态转换为业务视图。teammateViewHelpers.ts 则为队友显示提供专门的视图状态转换。
架构
模块关系与设计决策
状态管理架构图
AppState — 300+ 字段的全局状态
Store 模式
副作用 & 派生
SpeculationState
源码
共 3 个关键代码示例
// DeepImmutable: 递归不可变类型包装
type DeepImmutable<T> =
T extends Map<infer K, infer V>
? ReadonlyMap<DeepImmutable<K>, DeepImmutable<V>>
: T extends Set<infer S>
? ReadonlySet<DeepImmutable<S>>
: T extends Array<infer E>
? ReadonlyArray<DeepImmutable<E>>
: T extends object
? { readonly [K in keyof T]: DeepImmutable<T[K]> }
: T;
// createStore: 极简 Store 原语
interface Store<T> {
get(): DeepImmutable<T>;
set(updater: T | ((prev: T) => T)): void;
subscribe(listener: (state: DeepImmutable<T>) => void): () => void;
}
function createStore<T>(initialState: T): Store<T> {
let state = initialState;
const listeners = new Set<
(state: DeepImmutable<T>) => void
>();
return {
get() {
return state as DeepImmutable<T>;
},
set(updater) {
const next = typeof updater === "function"
? (updater as (prev: T) => T)(state)
: updater;
if (next !== state) {
state = next;
const immutable = state as DeepImmutable<T>;
for (const listener of listeners) {
listener(immutable);
}
}
},
subscribe(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
}
};
}
// AppStateStore.ts: 全局状态存储
const appStateStore = createStore<AppState>(
initialAppState
);
// 导出便捷函数
function getAppState(): DeepImmutable<AppState> {
return appStateStore.get();
}
function setAppState(
updater: (prev: AppState) => AppState
): void {
appStateStore.set(updater);
}// AppState: 300+ 字段的全局状态类型(精简展示)
interface AppState {
// === Core 核心 ===
conversationId: string;
messages: Message[];
model: ModelName;
currentTool: string | null;
isLoading: boolean;
error: string | null;
apiKey: string;
tokenUsage: TokenUsage;
// === Permissions 权限 ===
grantedPermissions: Map<string, Permission>;
deniedPermissions: Set<string>;
permissionMode: "default" | "auto" | "manual";
autoApprovePatterns: string[];
// === MCP ===
mcpClients: Map<string, McpClient>;
mcpTools: ToolDefinition[];
mcpResources: Resource[];
mcpConnectionStatus: Map<string, ConnectionStatus>;
// === Tasks 任务 ===
taskList: Task[];
activeTask: Task | null;
// === Agents 代理 ===
activeAgents: Agent[];
coordinatorState: CoordinatorState | null;
swarmBackend: "iterm" | "tmux" | "in-process";
// === Speculation 推测执行 ===
speculationState: SpeculationState;
// === Remote 远程 ===
remoteSession: RemoteSession | null;
bridgeStatus: BridgeStatus;
jwtToken: string | null;
// ... 更多字段 (Kairos, ComputerUse, Plugins 等)
}
// SpeculationState: 推测执行的精确状态
type SpeculationState =
| { status: "idle" }
| {
status: "active";
abort: () => void;
startTime: number;
messagesRef: { current: Message[] };
writtenPathsRef: { current: Set<string> };
boundary: SpeculationBoundary;
};// onChangeAppState.ts: 状态变更副作用
function registerStateEffects(
store: Store<AppState>
): void {
let prevState = store.get();
store.subscribe((nextState) => {
// model 变更 -> 重算 token 限制
if (nextState.model !== prevState.model) {
const limits = getModelLimits(nextState.model);
store.set(prev => ({
...prev,
tokenUsage: {
...prev.tokenUsage,
maxTokens: limits.maxOutputTokens,
contextWindow: limits.contextWindow
}
}));
}
// permissions 变更 -> 清除权限缓存
if (
nextState.grantedPermissions
!== prevState.grantedPermissions
) {
clearPermissionCache();
rebuildToolPermissions(
nextState.grantedPermissions
);
}
// mcpClients 变更 -> 重新注册工具
if (
nextState.mcpClients !== prevState.mcpClients
) {
const newTools = collectMcpTools(
nextState.mcpClients
);
store.set(prev => ({
...prev,
mcpTools: newTools
}));
}
// speculationState 变更 -> 执行控制
if (
nextState.speculationState
!== prevState.speculationState
) {
if (nextState.speculationState.status === "active") {
console.log(
"推测执行启动 at "
+ nextState.speculationState.startTime
);
}
}
prevState = nextState;
});
}
// selectors.ts: 派生状态
function selectAvailableTools(
state: DeepImmutable<AppState>
): ToolDefinition[] {
const builtinTools = getBuiltinTools();
const mcpTools = state.mcpTools;
const pluginTools = state.installedPlugins
.flatMap(p => p.tools || []);
return [...builtinTools, ...mcpTools, ...pluginTools]
.filter(
t => isToolPermitted(t, state.grantedPermissions)
);
}
// bootstrap/state.ts: 全局单例(非 React)
let globalState: AppState | null = null;
function getGlobalState(): AppState {
if (!globalState) {
throw new Error("State not initialized");
}
return globalState;
}
function initGlobalState(state: AppState): void {
globalState = state;
}互动
步进式流程演示
状态管理交互式演练
Step 1: 理解 AppState 结构
AppState 是一个拥有 300+ 字段的巨型对象。通过 DeepImmutable 包装,所有字段在类型层面都是只读的。直接修改会导致 TypeScript 编译错误。
// 编译错误! DeepImmutable 阻止直接修改
const state = getAppState();
state.model = "claude-4"; // Error: readonly
// 正确方式: 通过 setAppState 更新
setAppState(prev => ({
...prev,
model: "claude-4"
}));
Step 2: Store 的 subscribe 机制
createStore 的 subscribe 方法允许注册监听器。当状态变更时,所有监听器同步执行。返回的函数用于取消订阅。
const unsub = appStateStore.subscribe(state => {
console.log("Model:", state.model);
console.log("Tools:", state.mcpTools.length);
});
// 触发状态变更
setAppState(prev => ({ ...prev, model: "claude-4" }));
// 输出: Model: claude-4, Tools: 42
// 取消订阅
unsub();
Step 3: 副作用链式触发
状态变更会触发 onChangeAppState 中注册的副作用。例如切换模型会连锁触发 token 限制重算,这又会更新 UI 中的剩余 token 显示。
用户切换模型: claude-3.5 → claude-4
onChangeAppState 检测 model 变更:
1. 获取 claude-4 的 token 限制
2. 更新 tokenUsage.maxTokens: 4096 → 16384
3. 更新 tokenUsage.contextWindow: 200k → 1M
4. UI 自动刷新显示新的限制值
Step 4: SpeculationState 生命周期
推测执行的状态在 idle 和 active 之间切换。active 状态携带 abort 函数和路径追踪引用,用于在用户输入不匹配时中止推测。
推测执行状态流转:
idle → 检测到用户可能的下一步操作
active {
startTime: 1711900000,
writtenPaths: {"src/api.ts", "src/types.ts"},
boundary: { maxTokens: 2000 }
}
→ 用户确认 → 提交推测结果
→ 或用户否认 → abort() → 回滚 → idle