Claude Code 的会话记录是有用的本地证据,但它不是一串长得一样的聊天消息。每一行都是一个独立的 JSON 对象,一行的含义由它的顶层类型和嵌套字段共同决定
先看信封
一次读一行。空行、残行或畸形行在本轮应当被忽略,而不是把整次扫描搞崩。记录可能正在被写的同时被读,所以一个不完整的末行在下一次文件变化时可能就变成合法的了
Agent Island 在分类 Claude 活动时用到的字段包括 type、uuid、timestamp、message.stop_reason、isSidechain、isApiErrorMessage 和 toolEndsTurn。不是每一行都含有每一个字段,而某个字段缺失并不构成编造状态的许可
for each line:
if blank: continue
event = parse JSON
if parsing fails: continue
classify from the fields that are actually present
把用户活动和助手输出分开
一个用户事件是「之后有一个回合开始了」的证据。助手输出是「Agent 产出了工作」的证据,但它不自动等于整个会话已经结束。解析器必须保住事件顺序,好让之后的用户或开始活动能取代更早的完成标记
uuid 和时间戳帮助保住身份与排序。它们不该被文件修改时间取代:一个文件时间戳描述的是容器,而事件时间戳描述的是一条条记录
把 sidechain 当成另一个语境
isSidechain 标记标出的是不该被当成主要用户交接的活动。一个后台子 Agent 可能在父会话继续跑的时候结束。对两者发同一种闹钟,会在主任务还不需要人的时候就把用户拽回来
在产生前台的「需要你」状态之前先过滤掉 sidechain 事件。它们在诊断上仍然有用,但不等价于一次主线程交接
别把 API 错误变成完成
一个长得像助手输出的信封,可能装的是 API 或限流错误。所以 isApiErrorMessage 是一道闸门,不是装饰。错误输出应当进入对应的错误状态,绝不能被解读成一次成功完成的回合
同理,message.stop_reason 和 toolEndsTurn 需要上下文。一个终止标记可以描述某一次模型响应,但它证明不了之后不存在别的工具、用户或开始事件
重建状态,而不是 grep 一个词
稳健的读取器会把有序事件归约成一个会话状态:
- 解析合法记录,且不让整个文件失败
- 保住事件身份与语义时间戳
- 把 sidechain 交接排除在前台提醒之外
- 单独归类 API 错误
- 只有在之后没有活动取代它时,才接受一个完成候选
这就是为什么在记录里搜 stop_reason 或 completed 是不够的。一次词匹配没有回合身份,没有排序策略,也没有错误闸门
把隐私声称说窄一点
Agent Island 在本地读取这些会话记录来重建状态,不把会话内容上传到 Agent Island 的服务。实际发布的行为可以在开源的 v1.7.1 代码里审计;本地解析并不意味着所有可能的 Claude Code 记录版本都有完全一样的 schema
