Skip to content

会话级单例

概述

除了 React 驱动的 AppState,Claude Code 还在 bootstrap/state.ts 中维护模块级单例状态。这些状态在整个进程生命周期内存在,不通过 React 上下文传递。

Source: src/bootstrap/state.ts (900+ 行)

两层状态的关系

AppStatebootstrap/state
生命周期组件挂载期间进程启动到退出
访问方式useAppState() Hook全局 getter/setter
驱动重渲染
包含UI 状态、工具配置会话元数据、遥测

State 类型

Source: src/bootstrap/state.ts:45-257

typescript
type State = {
  // 会话标识
  sessionId: SessionId           // UUID
  parentSessionId?: SessionId    // 父会话(计划→实现)
  
  // 路径上下文
  originalCwd: string            // 原始工作目录
  projectRoot: string            // 项目根目录
  cwd: string                    // 当前工作目录(可变)
  
  // 费用追踪
  totalCostUSD: number
  totalAPIDuration: number
  turnToolDuration: number
  modelUsage: { [modelName: string]: ModelUsage }
  
  // 遥测
  meter?: Meter
  sessionCounter?: Counter
  costCounter?: Counter
  tokenCounter?: Counter
  loggerProvider?: LoggerProvider
  eventLogger?: Logger
  meterProvider?: MeterProvider
  tracerProvider?: TracerProvider
  
  // 代理
  agentColorMap: Map<string, AgentColorName>
  
  // 权限
  sessionBypassPermissionsMode: boolean
  sessionTrustAccepted: boolean
  
  // 缓存
  planSlugCache: Map<string, string>
  invokedSkills: Map<string, SkillInfo>
  
  // Prompt Cache 锁存
  promptCache1hAllowlist: boolean
  afkModeHeaderLatched: boolean
  fastModeHeaderLatched: boolean
}

全局访问器

状态通过 getter/setter 函数对暴露:

typescript
// Source: src/bootstrap/state.ts:431+

// 会话 ID
export function getSessionId(): SessionId { return STATE.sessionId; }
export function regenerateSessionId(options?) {
  STATE.sessionId = randomUUID();
  STATE.planSlugCache.clear();
}

// 工作目录
export function getCwd(): string { return STATE.cwd; }
export function setCwd(cwd: string) { STATE.cwd = cwd; }

// 费用追踪
export function getTotalCostUSD(): number { return STATE.totalCostUSD; }
export function addCost(cost: number) { STATE.totalCostUSD += cost; }

// 遥测
export function getMeter(): Meter | undefined { return STATE.meter; }
export function setMeter(meter: Meter, factory: CounterFactory) {
  STATE.meter = meter;
  STATE.sessionCounter = factory('session');
  STATE.costCounter = factory('cost');
  STATE.tokenCounter = factory('token');
}

Prompt Cache 锁存

一个有趣的优化机制:

typescript
// Source: src/bootstrap/state.ts:221-242

// 问题:切换 Fast Mode 时系统提示词变化,导致 prompt cache 失效
// 解决:锁存(latch)— 一旦设为 true,本会话内不再回退为 false

promptCache1hAllowlist: boolean   // 1h cache 允许列表
afkModeHeaderLatched: boolean     // AFK 模式头部
fastModeHeaderLatched: boolean    // Fast 模式头部

这确保了模式切换不会破坏已建立的 prompt cache,节省 API 成本。

代理颜色映射

typescript
agentColorMap: Map<string, AgentColorName>

每个子代理分配一个唯一的颜色,在多代理输出中区分不同代理的消息。颜色在首次创建时分配,整个会话保持不变。

技能追踪

typescript
invokedSkills: Map<string, {
  name: string
  invocationCount: number
  lastInvoked: number
}>

跨代理追踪技能调用情况,防止重复调用同一技能。

与 AppState 的交互

某些操作需要同时更新两层状态:

typescript
// 例如:切换权限模式
// 1. 更新 AppState(驱动 UI 重渲染)
setAppState(prev => ({
  ...prev,
  toolPermissionContext: { ...prev.toolPermissionContext, mode: newMode }
}));

// 2. 更新模块单例(服务层访问)
setSessionBypassPermissionsMode(newMode === 'bypassPermissions');

下一步