01.3 启动期状态与上下文

关注源码

  • src/bootstrap/state.ts
  • src/context.ts
  • src/state/onChangeAppState.ts

两层状态模型

这套代码有两层不同职责的状态:

1. bootstrap/state.ts 管启动期和会话级全局事实

这层保存的是“整个进程/会话都要共享”的东西,例如:

  • originalCwdprojectRootsessionId
  • 当前模型、model strings、SDK betas
  • 统计计数器、成本、API duration、tool duration
  • prompt cache 相关 latch
  • 计划模式/auto mode 的一次性状态
  • session trust、session persistence、remote mode 等全局标记

这类状态不适合放进 React AppState,因为它们既服务 headless,也服务 TUI,还要在启动期早早可用。

2. AppState 管交互式前台会话状态

AppStatestate/AppStateStore.ts 中定义,更多承载:

  • 当前消息、任务、notifications、elicitation
  • 当前 tools/commands/MCP/plugin 视图
  • Prompt suggestion、speculation、footer、panel、selection
  • 交互层正在展示什么、聚焦什么

入口层负责把 bootstrap 层结果折叠成初始 AppState

上下文组装

getSystemContext()

context.ts 中的 system context 主要提供:

  • git status 快照
  • 当前分支与默认分支
  • recent commits
  • 某些调试/缓存打断注入

它是“会话开头的环境说明”,而不是每轮实时刷新状态。

getUserContext()

user context 主要提供:

  • CLAUDE.md 及其递归 memory 文件内容
  • 当前日期

其中 CLAUDE.md 会被缓存到 bootstrap state,供权限分类器等无法直接依赖 claudemd.ts 的路径复用。

为什么上下文在入口层准备

因为它们直接影响:

  • system prompt 组成
  • prompt cache key
  • 权限分类器输入
  • 启动后的第一轮 query

如果把这些信息拖到运行时临时补,会让 prompt 组成、缓存命中、权限行为都变得不稳定。

onChangeAppState 的位置很关键

state/onChangeAppState.ts 不是普通监听器,它是“交互状态写回系统状态”的统一出口。当前它会处理:

  • permission mode 变更时,向 CCR/SDK 同步 external metadata
  • model 变更时,写回 settings 和 session override
  • expanded view / verbose 等 UI 偏好持久化
  • settings 变化时,清理认证缓存并重新应用环境变量

这意味着 AppState 不是孤立的前端 store,而是会反向驱动:

  • session metadata
  • settings 持久化
  • 远端桥接元数据

这一层的设计意义

1. 启动期状态和前台状态被明确分层

如果把所有状态都塞进 React store,会让:

  • headless 模式难以复用
  • 启动前阶段无法访问关键状态
  • 进程级 latch 与 UI 状态混在一起

当前分层把“运行时事实”和“界面表现”区分得比较清楚。

2. 上下文是缓存敏感数据,不是普通 helper

context.ts 的输出会直接影响模型输入,所以它被做成 memoized provider,而不是 UI 侧随手拼装的对象。

3. onChangeAppState 是跨层同步阀门

所有 setAppState 造成的重要副作用都尽量汇总到这里,而不是散在各组件和 hooks 里。这是避免模式同步失真的关键。