Skip to content

第 11 章:会话、持久化与恢复 ​

目标:理解 Orca 持久化了什么、为什么分成多种存储,以及怎样在 schema 演进和崩溃后恢复而不复活幽灵资源。

1. 持久化不是一个 JSON 文件就够了 ​

Orca 的状态按语义分散:

存储内容一致性需求
主 PersistedState JSONrepo、settings、UI、workspace sessions原子写、迁移、备份
Agent hook last-status最近 Agent 状态debounce + age bound
Browser session partition/metacookie、profile、browser stateChromium 管理 + registry
AI Vault parse cache会话索引解析结果可丢弃缓存
Orchestration SQLiterun/task/message/dispatch/receipt事务与幂等
Terminal history/modelscrollback/snapshot/cwd有界、provider/runtime 权威

2. PersistedState 的骨架 ​

主类型包含:

ts
type PersistedState = {
  schemaVersion: number
  repos: Repo[]
  projects: Project[]
  projectHostSetups: ProjectHostSetup[]
  projectGroups: ProjectGroup[]
  folderWorkspaces: FolderWorkspace[]
  worktreeMeta: Record<string, WorktreeMeta>
  worktreeLineageById: Record<string, WorktreeLineage>
  settings: GlobalSettings
  ui: PersistedUIState
  workspaceSession: WorkspaceSessionState
  workspaceSessionsByHostId?: Record<ExecutionHostId, WorkspaceSessionState>
  sshTargets: SshTarget[]
  automations: Automation[]
  automationRuns: AutomationRun[]
}

完整定义在 src/shared/types.ts。

3. Store 的 load 流程 ​

Store 的 load 不只按 schemaVersion 执行一次性迁移,还用字段级 guard 保留用户后续选择。例如某个默认值从 false 改 true,只对“尚未迁移过的旧用户”翻转一次,之后用户手动关闭必须继续生效。

4. 原子写、hash 去重与备份 ​

Store 的写入策略:

  • debounce 普通更新;
  • 内容 hash 相同则跳过;
  • 临时文件 + rename;
  • 轮换多个 .bak;
  • backup 有最小时间间隔,避免 300ms debounce 生成一组几乎相同的备份;
  • quit/关键资源退休时同步 flush。

加密字段使用 Electron safeStorage。由于每次加密随机 IV 不同,hash 去重必须按解密后的语义 payload 计算,不能直接 hash ciphertext。

5. 为什么 migration 代码很长 ​

桌面应用的数据会跨数百个版本原地升级,不能靠清库。迁移涉及:

  • retired setting 和 default 变化;
  • worktree card/schema;
  • SSH target id;
  • local session 拆分为 host partitions;
  • terminal numeric leaf 转 UUID;
  • sidekick 重命名为 pet;
  • automation target 与 output;
  • Windows/WSL runtime 默认;
  • source-control provider identity。

迁移原则是“只改可证明的旧默认,不改用户自定义”。因此常见独立 _xxxMigrated 标记。

6. Workspace session 保存什么 ​

session 不是完整运行时 dump,主要保存 presentation 与恢复线索:

  • tab groups、tab order、active tab;
  • terminal pane split layout;
  • tab title、color、pin;
  • terminal binding / saved cwd / startup config;
  • sleeping Agent session metadata;
  • editor open files 与 mode;
  • browser tab projection。

它不能证明 PTY 活着。恢复时必须向 provider/runtime 重新验证。

7. 按执行主机分区 ​

历史上只有 workspaceSession,现在 local 保留在这个 legacy 字段以支持降级,非本机 host 放在 workspaceSessionsByHostId。

text
workspaceSession                       -> local
workspaceSessionsByHostId['ssh:a']     -> SSH A
workspaceSessionsByHostId['runtime:b'] -> Runtime B

host partition 避免相同 repo/worktree id 在不同执行机上的 terminal 布局互相覆盖。

8. Pane identity 迁移 ​

旧 terminal layout 使用数字 leaf,无法在跨重排/重启时稳定识别。load 阶段会:

  • 为 layout leaf 分配稳定 UUID;
  • remap pane-keyed records;
  • 保留可证明的 legacy alias;
  • 将无法安全迁移的 live PTY 标记为 unsupported;
  • 防止旧 renderer 写回数字 leaf 复活旧格式。

迁移不是简单字符串替换,因为同一数字 leaf 在多个 tab/host 里可能碰撞。

9. Terminal membership revision ​

当 PTY exit 时,host 必须从 session 中移除 terminal binding。若 renderer 稍后用旧 snapshot 保存 UI metadata,不能把 leaf 加回来。

解决方案是每个 repo/host 的单调 topology revision:

  1. host 退休 terminal;
  2. revision 递增并同步 flush;
  3. renderer 旧写入只能更新仍存在对象的 metadata;
  4. terminal membership 以 host revision 为准。

这比永久保存每个 deleted pane tombstone 更有界。

10. Agent hook 状态的独立文件 ​

Agent status 更新频率高、生命周期与 UI settings 不同,因此保存为 last-status.json:

  • 250ms trailing debounce;
  • quit-time flush;
  • 只保留 7 天内记录;
  • launch token 只存 hash;
  • hydrate 时严格校验 pane/tab/worktree/connection;
  • idle subagent 等无法证明的 liveness 不跨重启恢复。

11. AI Vault 缓存与 transcript ​

会话原文仍属于各 Agent。Orca 保存的 parse cache 可随时重建,因此设计成:

  • 有文件 version/mtime/size identity;
  • 限制 entry 与总字节;
  • SQLite parser 可放 worker;
  • remote scan 通过 runtime;
  • title/preview 在读取时做长度和隐藏块过滤。

Native Chat watcher 则增量读取原 transcript,不把整个对话复制进主 PersistedState。

12. Orchestration 为何用 SQLite ​

run/task/message/dispatch 需要:

  • 原子状态转换;
  • sequence 与 cursor;
  • 幂等 mutation receipt;
  • worker report settlement;
  • inbox delivery / acknowledgment;
  • federation relay ack。

这些是关系与事务,不适合频繁重写一个大 JSON。OrchestrationDb 使用 schema version 和 hardened DB files,详见第 12 章。

13. Crash consistency ​

需要考虑四个典型崩溃点:

文件临时写了一半 ​

主文件解析失败,尝试备份环。

PTY 已退出,session 尚未写盘 ​

退休操作同步 flush,减少幽灵 leaf。

spawn 已物理提交,响应尚未返回 ​

physical commit callback 保留 operation fence,禁止安全重试假设。

renderer crash,PTY 继续输出 ​

main-owned terminal model 与 daemon 存活;renderer 重启后请求 snapshot。

14. 数据最小化 ​

持久化代码反复区分:

  • renderer 可读写的 UI state;
  • main-owned telemetry/credit ledger;
  • secret;
  • live-only token/interaction key;
  • 可重建 cache;
  • 真实 resource authority。

不是所有内存字段都应该进入磁盘;也不是所有磁盘字段都应该通过 preload 暴露给 renderer。

15. 源码导航 ​