目录
  1. 1. 一、状态分层架构
  2. 2. 二、Layer 1: 进程级全局状态 (bootstrap/state.ts)
    1. 2.1. 2.1 为什么需要进程级状态?
    2. 2.2. 2.2 核心字段
    3. 2.3. 2.3 访问模式
  3. 3. 三、Layer 2: AppState Store
    1. 3.1. 3.1 Store 实现
    2. 3.2. 3.2 状态变更监听
    3. 3.3. 3.3 选择器 (Selectors)
  4. 4. 四、Layer 3: React Hooks
    1. 4.1. 4.1 常用 hooks 分类
    2. 4.2. 4.2 useCanUseTool — 权限检查 Hook
  5. 5. 五、数据流图
  6. 6. 六、信号系统 (utils/signal.ts)
  7. 7. 七、配置系统 (utils/config.ts)
  8. 8. 八、文件状态缓存
  9. 9. 九、自研 Store 的架构分析
    1. 9.1. 11.1 为什么不用 Redux/Zustand?
    2. 9.2. 11.2 Store API 设计
    3. 9.3. 11.3 与 Zustand 的对比
    4. 9.4. 11.4 Store 的性能特征
  10. 10. 十、状态同步机制
    1. 10.1. 12.1 多源状态合并
    2. 10.2. 12.2 与 React 18 useSyncExternalStore 的集成
  11. 11. 十一、Signal 系统的补充
  12. 12. 十二、配置系统的状态管理
    1. 12.1. 14.1 配置的三层模型
    2. 12.2. 14.2 配置热更新
  13. 13. 十三、状态调试
  14. 14. 涉及源文件
【Claude Code源码剖析】07-状态管理系统

⚠️ 学习声明:本文档基于 Claude Code 2.1.88 源码分析整理,仅供个人学习研究使用,不做任何商业用途。

Claude Code 采用三层状态管理:进程级全局 → Store → React Hooks。


一、状态分层架构

┌─────────────────────────────────────────┐
│ Layer 3: React Hooks (组件级) │
│ useState, useEffect, custom hooks │
│ ↕ 单向数据流 │
│ │
│ Layer 2: AppState Store (应用级) │
│ createStore<AppState>() │
│ ↕ 发布/订阅 │
│ │
│ Layer 1: bootstrap/state.ts (进程级) │
│ 模块闭包中的全局单例 │
│ getter/setter 函数 │
└─────────────────────────────────────────┘

二、Layer 1: 进程级全局状态 (bootstrap/state.ts)

2.1 为什么需要进程级状态?

多个场景需要在 React 组件树 外部 访问状态:

  • SDK 模式(无 React)
  • 子 Agent 进程
  • API 调用层
  • 工具执行层
  • OpenTelemetry 遥测

2.2 核心字段

type State = {
// 会话标识
sessionId: SessionId;
originalCwd: string;
projectRoot: string;

// 成本统计
totalCostUSD: number;
totalAPIDuration: number;
totalInputTokens: number;
totalOutputTokens: number;
modelUsage: { [modelName: string]: ModelUsage };

// 模型配置
mainLoopModelOverride: ModelSetting | undefined;
initialMainLoopModel: ModelSetting;

// 运行模式
isInteractive: boolean;
kairosActive: boolean; // Assistant 模式
clientType: string; // 'cli' | 'sdk' | ...
sessionSource: string; // 触发来源

// 遥测
meter: Meter | null; // OpenTelemetry
sessionCounter: AttributedCounter | null;
costCounter: AttributedCounter | null;

// 功能开关
sdkBetas: string[];
allowedChannels: ChannelEntry[];

// Hook 系统
registeredHooks: Map<string, RegisteredHookMatcher[]>;
}

2.3 访问模式

// 只通过导出的 getter/setter 访问,不暴露 state 对象本身
export function getSessionId(): SessionId { return state.sessionId; }
export function getTotalCostUSD(): number { return state.totalCostUSD; }

export function addToTotalCostState(amount: number): void {
state.totalCostUSD += amount;
}

// 复合操作
export function switchSession(newSessionId: SessionId): void {
state.sessionId = newSessionId;
resetSettingsCache(); // 切换会话时重置缓存
}

三、Layer 2: AppState Store

3.1 Store 实现

// state/store.ts
export function createStore<T>(
initialState: T,
onChange?: OnChange<T>,
): Store<T> {
let state = initialState;
const listeners = new Set<Listener>();

return {
getState: () => state,

setState: (updater: (prev: T) => T) => {
const prev = state;
const next = updater(prev);
if (Object.is(next, prev)) return; // 性能优化:引用相等时跳过
state = next;
onChange?.({ newState: next, oldState: prev });
for (const listener of listeners) listener();
},

subscribe: (listener: Listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
},
};
}

3.2 状态变更监听

// state/onChangeAppState.ts
export function onChangeAppState({
newState,
oldState,
}: { newState: AppState; oldState: AppState }): void {
// 当特定字段变化时触发副作用

// 权限模式变化 → 更新全局配置
if (newState.toolPermissionContext.mode !== oldState.toolPermissionContext.mode) {
updatePermissionModeInConfig(newState.toolPermissionContext.mode);
}

// 模型变化 → 更新遥测属性
if (newState.mainLoopModel !== oldState.mainLoopModel) {
updateTelemetryModel(newState.mainLoopModel);
}

// 任务状态变化 → 通知系统
if (newState.tasks !== oldState.tasks) {
checkForCompletedTasks(newState.tasks, oldState.tasks);
}
}

3.3 选择器 (Selectors)

// state/selectors.ts
// 从 AppState 中提取派生数据

export function getActiveAgentCount(state: AppState): number {
return Array.from(state.tasks.values())
.filter(t => t.type === 'local_agent' && t.status === 'running')
.length;
}

export function getTotalRunningTasks(state: AppState): number {
return Array.from(state.tasks.values())
.filter(t => t.status === 'running')
.length;
}

四、Layer 3: React Hooks

4.1 常用 hooks 分类

数据获取类:

useSettings()               // 获取当前设置
useMainLoopModel() // 获取当前模型
useCostSummary() // 获取费用摘要
useTerminalSize() // 获取终端尺寸
useIdeConnectionStatus() // IDE 连接状态

交互类:

useTextInput()              // 文本输入处理
useVimInput() // Vim 模式输入
useSearchInput() // 搜索输入
useArrowKeyHistory() // 历史导航
useCopyOnSelect() // 选择复制
usePasteHandler() // 粘贴处理

生命周期类:

useExitOnCtrlCD()           // Ctrl+C/D 退出
useCancelRequest() // 取消当前请求
useAfterFirstRender() // 首次渲染后执行
useMinDisplayTime() // 最小显示时间(防闪烁)

集成类:

useReplBridge()             // 远程桥接
useIDEIntegration() // IDE 集成
useMergedClients() // 合并 MCP 客户端
useMergedTools() // 合并工具列表
useManagePlugins() // 插件管理

4.2 useCanUseTool — 权限检查 Hook

// hooks/useCanUseTool.tsx
export default function useCanUseTool(
toolPermissionContext: ToolPermissionContext,
setToolPermissionContext: (updater) => void,
): CanUseToolFn {
return useCallback(async (tool, input, assistantMessage) => {
// 1. 匹配权限规则
const ruleMatch = matchPermissionRules(tool, input, toolPermissionContext);
if (ruleMatch) return ruleMatch;

// 2. 根据权限模式决策
switch (toolPermissionContext.mode) {
case 'bypass': return { behavior: 'allow' };
case 'plan': return tool.isReadOnly() ? { behavior: 'allow' } : { behavior: 'deny' };
case 'auto': return await classifierDecision(tool, input);
case 'default': return { behavior: 'ask' };
}
}, [toolPermissionContext]);
}

五、数据流图

用户操作 (键盘/鼠标)


React Event Handler (REPL.tsx)

├─→ setAppState(prev => ...) → Store 更新
│ │
│ ├─→ onChange() → 副作用
│ └─→ listeners → React 重渲染

├─→ setMessages(prev => ...) → 消息列表更新

└─→ query() → Agentic Loop

├─→ 更新 bootstrap/state (成本/token)
├─→ yield events → 更新 messages
└─→ 工具执行 → 可能修改 AppState

六、信号系统 (utils/signal.ts)

// 轻量级的响应式信号(比 Store 更轻量)
export function createSignal<T>(initialValue: T) {
let value = initialValue;
const subscribers = new Set<(value: T) => void>();

return {
get: () => value,
set: (newValue: T) => {
value = newValue;
for (const sub of subscribers) sub(newValue);
},
subscribe: (fn: (value: T) => void) => {
subscribers.add(fn);
return () => subscribers.delete(fn);
},
};
}

用于不需要 React 重渲染的简单状态(如内部计数器、标志位)。


七、配置系统 (utils/config.ts)

配置来源 (优先级从高到低):
├─ 1. CLI 参数 (--model, --permission-mode, ...)
├─ 2. 环境变量 (ANTHROPIC_API_KEY, CLAUDE_CODE_*)
├─ 3. 项目配置 (.claude/settings.json)
├─ 4. 用户配置 (~/.claude/settings.json)
├─ 5. 企业策略 (MDM / 远程管理)
└─ 6. 默认值
// 配置读取示例
export function getGlobalConfig(): GlobalConfig {
return readJsonSync(GLOBAL_CONFIG_PATH);
}

export function getCurrentProjectConfig(): ProjectConfig {
return readJsonSync(join(cwd, '.claude/settings.json'));
}

// 配置写入
export function saveGlobalConfig(config: GlobalConfig): void {
writeJsonSync(GLOBAL_CONFIG_PATH, config);
}

八、文件状态缓存

// utils/fileStateCache.ts
// 缓存工具读取的文件内容,避免重复读取

type FileStateCache = Map<string, {
content: string;
modifiedTime: number;
encoding: string;
}>;

// 带大小限制的缓存
export function createFileStateCacheWithSizeLimit(
maxSize: number = READ_FILE_STATE_CACHE_SIZE // 默认 1000 个文件
): FileStateCache {
// LRU 策略:满了就淘汰最早的条目
}

// 跨 Agent 的缓存共享
export function mergeFileStateCaches(
parent: FileStateCache,
child: FileStateCache,
): FileStateCache {
// 子 Agent 的缓存合并回父级
}

九、自研 Store 的架构分析

11.1 为什么不用 Redux/Zustand?

Claude Code 的状态管理需求非常特殊:

需求 Redux Zustand CC 自研 Store
包大小 ~12KB ~2KB 200 行 (1KB)
React 无关 ❌(需 react-redux) ❌(需 React) ✅ 纯 JS
多实例 createStore<T>()
选择器 useSelector selector 无(手动 subscribe)
中间件 无(不需要)
TypeScript 复杂 优秀 简单泛型

11.2 Store API 设计

// CC 自研 Store 的核心 API(类似 Zustand 的精简版)
interface Store<T> {
get(): T; // 同步读取当前状态
set(partial: Partial<T>): void; // 浅合并更新
subscribe(listener: (state: T, prev: T) => void): () => void; // 订阅变化
}

// 使用示例
interface AppState {
session: Session | null;
messages: Message[];
isStreaming: boolean;
tokenUsage: Usage;
}

const store = createStore<AppState>({
session: null,
messages: [],
isStreaming: false,
tokenUsage: { input: 0, output: 0 },
});

// 订阅变化
const unsub = store.subscribe((state, prev) => {
if (state.isStreaming !== prev.isStreaming) {
updateSpinner(state.isStreaming);
}
});

// 更新状态
store.set({ isStreaming: true });

11.3 与 Zustand 的对比

// Zustand 风格 (React 绑定):
const useStore = create((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
}));
function Component() {
const count = useStore((s) => s.count); // React hook
}

// CC Store 风格 (框架无关):
const store = createStore({ count: 0 });
function inc() {
store.set({ count: store.get().count + 1 }); // 纯函数
}
// 在 React Ink 中通过 useSyncExternalStore 桥接
function useAppState<T>(selector: (s: AppState) => T): T {
return useSyncExternalStore(store.subscribe, () => selector(store.get()));
}

11.4 Store 的性能特征

操作       时间复杂度    说明
get() O(1) 直接返回引用(注意不可变性)
set() O(k) k = partial 的 key 数量
subscribe O(1) 添加到 listener 数组
notify O(n) n = 订阅者数量(典型 < 50)

十、状态同步机制

12.1 多源状态合并

Claude Code 的状态来自多个源,Store 作为单一真相来源进行合并:

状态源:
├─ API 响应 → store.set({ messages: [...], tokenUsage: ... })
├─ 用户输入 → store.set({ inputValue: ... })
├─ 工具执行 → store.set({ toolResults: ... })
├─ 配置变化 → store.set({ theme: ..., model: ... })
└─ 系统事件 → store.set({ connectionStatus: ... })

Store (内存)

┌─────────┼─────────┐
▼ ▼ ▼
REPL 侧边栏 Footer

12.2 与 React 18 useSyncExternalStore 的集成

// 自定义 hook 桥接 Store 到 React Ink
function useSelector<T>(selector: (s: AppState) => T): T {
return useSyncExternalStore(
store.subscribe,
() => selector(store.get()),
() => selector(store.get()) // SSR(终端不需要)
);
}

// 在组件中使用
function TokenUsage() {
const usage = useSelector(s => s.tokenUsage);
return <Text>Tokens: {usage.input + usage.output}</Text>;
}

十一、Signal 系统的补充

除 Store 外,CC 还有一个轻量的 Signal 系统用于一次性事件通知

// Signal: 多对多的事件总线
interface Signal<T> {
emit(data: T): void;
on(handler: (data: T) => void): () => void; // 返回取消订阅函数
once(handler: (data: T) => void): () => void;
}

// 使用场景:
const onToolExecuted = createSignal<ToolResult>();
onToolExecuted.on(result => auditLog.record(result));
onToolExecuted.on(result => costTracker.update(result));

Signal vs Store 的职责划分:

  • Store: 持久状态(当前 messages、session、config)
  • Signal: 瞬时事件(工具执行完毕、用户中断、网络恢复)

十二、配置系统的状态管理

14.1 配置的三层模型

Layer 1: 默认配置(代码内置)
└─ src/defaults.ts — 所有配置的默认值

Layer 2: 全局配置(用户级)
└─ ~/.claude/settings.json — 跨项目生效

Layer 3: 项目配置(项目级)
└─ <project>/.claude/settings.json — 项目级覆盖

优先级:项目配置 > 全局配置 > 默认配置。

14.2 配置热更新

// 监听配置文件变化,自动热更新
fs.watch(settingsPath, async () => {
const newConfig = await loadConfig();
store.set({ config: mergeConfig(store.get().config, newConfig) });
});

十三、状态调试

// 开发模式下可用的状态快照
if (process.env.CLAUDE_DEBUG) {
// /state 命令 → 打印完整 Store 状态
commands.register({
name: 'state',
handler: () => console.log(JSON.stringify(store.get(), null, 2)),
});

// 每次 set() 自动 diff
store.subscribe((state, prev) => {
const diff = deepDiff(prev, state);
if (Object.keys(diff).length > 0) {
logger.debug('State change:', diff);
}
});
}

涉及源文件

  • bootstrap/state.ts
  • utils/config.ts
  • utils/signal.ts
打赏
  • 微信
  • 支付宝

评论