2026-07-30 针对 Agent Island v1.7.1 核实。稳定的 macOS 版本用 CoreServices 的 FSEvents,Windows 版本用 FileSystemWatcher。两边都保留周期扫描
轮询会造出一个看得见的延迟下限
只靠轮询的监控要到下一个 tick 才知道多了一行记录。六秒的间隔意味着正确状态可能迟到六秒,哪怕解析只花几毫秒。缩短间隔能减少这段延迟,但会增加每个记录根目录上的空转开销
文件系统事件改变了触发方式。操作系统告诉监控「被监视的某个根下面有东西变了」,监控立刻安排一次扫描。事件本身不需要携带应用状态,它只需要把分类器叫醒
macOS:一条覆盖已知根目录的 FSEvents 流
已发布的 macOS 监听器覆盖三个已存在的位置:Claude 的项目记录、Codex 的会话,以及 Claude Desktop 的会话元数据。它请求文件级事件,延迟 50 毫秒,不做延后投递
FSEventStreamCreate(
nil,
callback,
&context,
roots as CFArray,
FSEventStreamEventId(kFSEventStreamEventIdSinceNow),
0.05,
kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer
)
回调过滤出 .jsonl 文件,以及名字以 local_ 开头的 Claude Desktop 记录。一个相关路径只调用一个变更处理函数,解析与状态归约都留在监听器之外
Windows:每个已存在的根一个 FileSystemWatcher
Windows 实现递归监视同样这几类根目录。它订阅变更、创建和重命名事件,并请求最后写入、文件名和大小三种通知
var watcher = new FileSystemWatcher(root) {
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.Size,
InternalBufferSize = 64 * 1024,
};
一次文件保存可能产生不止一个事件,一次重命名可能是原子替换的一部分。因此处理函数把每一个相关事件都当成「请重扫当前状态」的请求,而不是「请应用某个假定的状态迁移」的命令
文件系统通知可能是不完整的
原生监听器是被优化过的通知通道,它们不是应用的账本。FSEvents 会合并相关变更;FileSystemWatcher 用的是有限缓冲,一次突发可能把它冲爆。根目录可能在启动之后才出现,权限错误可能挡住监听器,进程也可能替换文件而不是追加
假设「一次记录写入对应一次回调」的代码,迟早会握着过期状态。把缓冲调大只是改变了暴露同一个设计错误所需要的突发量
让每个事件幂等,并保留一次全扫
对任何通知,安全的反应都一样:检视当前的文件集合、从变化的候选里读取有界证据、跑语义分类器。重复回调于是只是重复一次幂等读取,而不是复制出一个闹钟
在 Windows 上,监听器的错误回调安排的是与普通文件事件同一次重扫。周期轮询在两个平台上都保持开启,它覆盖那些监视不了的根,并在通知被合并、丢失,或者 App 挂起期间到达时修复状态
合并的是工作,不是证据
一次逻辑写入可能带来好几个文件系统事件。调度层面一个很短的合并窗口可以把扫描请求并起来。但不要根据这个窗口丢弃记录 —— 哪个有序事件最新,仍然由解析器决定
这种分离让调参保持安全。改动事件延迟或扫描调度,都不能改变 turn/completed、end_turn 或一条用户消息的含义
给恢复路径上度量
记录扫描请求数、实际执行的扫描数、变化文件数、读取字节数、解析失败数、监听器错误数,以及最新语义事件的年龄。测三种情况:一次普通追加、一次会产生重复通知的突发,以及一次强制的监听器失败后由轮询成功补回
目标是有界的检测延迟,且没有永久的过期状态。只有在恢复仍然正确的前提下,更快的回调才有意义
产品边界
Agent Island v1.7.1 在 macOS 与 Windows 上用本地文件系统证据更新 Claude Code 与 Codex 的状态。监听器不从事件负载里读密钥,不把会话数据上传到 Agent Island,也不判断生成的代码是否正确。它只是叫醒一个本地分类器