会话级单例
概述
除了 React 驱动的 AppState,Claude Code 还在 bootstrap/state.ts 中维护模块级单例状态。这些状态在整个进程生命周期内存在,不通过 React 上下文传递。
Source:
src/bootstrap/state.ts(900+ 行)
两层状态的关系
| AppState | bootstrap/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');下一步
- MCP 协议集成 — 扩展工具能力