检查点
每次运行都是事件溯源的:会话以 SessionEvent 的只追加日志形式,通过 Checkpointer 契约持久化。这带来两个收益:会话可持久(重启后恢复),以及时间回溯——把会话回滚到任意早期检查点,同时撤销对话内容和 agent 做过的文件改动。持久化默认开启(fileCheckpointer);当多个进程需要共享会话时,换成 SQLite 后端。
用法
使用默认文件后端时无需任何配置——createLiteAgent 会持久化每个会话,并由 LiteAgent 持有当前会话:
LiteAgent 上的会话管理方法:
设 sessions: false 可完全关闭持久化(会话方法将拒绝调用)。
时间回溯
检查点就是回滚锚点——每个用户 prompt 一个。restore(id, seq) 把会话回滚到某个检查点之前:还原快照文件和/或截断对话,然后把当前会话设为 id。
时间回溯之所以可行,是因为文件工具在修改前会为每个文件做快照:restore 重放这些快照撤销磁盘改动,然后截断事件日志。
切换 SQLite 后端
默认的 fileCheckpointer 是单进程的。@lite-agent/checkpoint-sqlite 包提供 sqliteCheckpointer——一个 SQLite(WAL)后端,面向单机多进程场景:server 或 worker 池中多个进程恢复并追加同一批会话,用乐观并发取代静默覆盖。
依赖 better-sqlite3——安装时需要编译的原生模块。运行时需要 @lite-agent/core。
把 checkpointer 传给 createLiteAgent(或 query),它会覆盖默认文件存储:
选项
SqliteCheckpointerOptions:
并发模型
- WAL 日志 —— 打开时设置
journal_mode = WAL:并发读者从不阻塞,同一时刻只有一个写者。 BEGIN IMMEDIATE写入 ——append和truncate先拿写锁,竞争写者会在busy_timeout内等待而不是报SQLITE_BUSY_SNAPSHOT,然后读到新的 head 并干净地冲突。- 原子 seq 分配 —— 每次
append是一个事务:读会话 head,插入seq = head + 1…n;数据库串行化并发 append,seq 绝不交错。 - 读不加锁 ——
read({ sinceSeq })是纯前向扫描;它也是 server 层用 SSE 推送事件的底层原语。
冲突处理
当两个进程持有同一会话时,第二个写者的 append 会以 CheckpointConflictError 快速失败——乐观并发,冲突永远被抛出、不被吞掉:
多机并发(网络文件系统、分布式写者)超出 SQLite 的能力范围。Checkpointer 接口与后端无关,未来的 Postgres 后端可以在不动内核的情况下覆盖该场景。
Checkpointer 契约
SqliteCheckpointer 实现了完整的 core Checkpointer 接口,并通过 core 的 checkpointerConformance 测试套件——与默认 fileCheckpointer 跑的是同一套:
运维接口:checkIntegrity(): { ok, detail } 按需运行 PRAGMA quick_check;close() 关闭数据库句柄(退出前调用——关闭后的 checkpointer 不可复用)。数据库带有 user_version schema 标记:打开由更新、不兼容版本写入的文件会立即抛错,而不是误读数据。