Claude Code 源码解析
返回目录
界面与状态18

状态管理

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+ 字段的全局状态

Core
会话/模型
Permissions
权限控制
MCP
连接/工具
Tasks
任务列表
Agents
代理状态
Speculation
推测执行
Remote
远程连接
Plugins
插件配置
Kairos
集成状态
ComputerUse
屏幕操控

Store 模式

createStore<T>
get / set / subscribe
AppStateStore
全局单例
DeepImmutable
类型级不可变

副作用 & 派生

onChangeAppState
model → token 重算
permissions → 缓存更新
mcp → 工具重注册
selectors.ts
派生状态计算
teammateViewHelpers
bootstrap/state.ts

SpeculationState

idle
active
abort | startTime | boundary

源码

3 个关键代码示例

01
createStore 与 AppState 定义
TypeScript
// 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);
}
02
AppState 类型定义(精简版)
TypeScript
// 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;
    };
03
onChangeAppState 副作用注册
TypeScript
// 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

相关源文件

state/bootstrap/state.ts