Repository navigation
Description
这条 Issue 要解决什么
Agent 会直接操作用户的真实项目。当前系统能在任务开始和结束时比较文件变化,也能在文件没有被再次修改时恢复旧内容,但它还不能准确管理一次任务中所有副作用。
真正需要解决的是:
- 哪个文件、哪一段内容是哪个 Agent 或工具改的;
- 用户原来未提交的修改是否被保护;
- Agent 和用户同时改一个文件时如何处理;
- Shell 命令改了很多文件或启动后台进程时如何记录;
- 任务崩溃后,哪些操作能撤销,哪些只能人工确认。
用一个实际场景说明
用户的 app.py 原本就有未提交修改。Agent 又修改了同一个文件,并运行 ruff --fix 改动另外 20 个文件。任务随后失败。
用户点击“撤销 Agent 修改”时,系统必须做到:
- 保留用户原来的修改;
- 只撤销 Agent 新增的部分;
- 识别
ruff --fix修改的 20 个文件; - 如果用户之后又编辑了其中一个文件,不用旧快照直接覆盖;
- 明确说明是否存在无法撤销的外部操作。
当前首尾快照无法完整回答这些问题。
当前实现的具体限制
WorkspaceChangeTracker只在任务开始和结束时扫描整个目录,无法记录每次工具调用分别改了什么;- 单文件超过 1 MB、总快照超过 32 MB,以及被忽略目录中的内容会被跳过,但最终记录没有明确告诉用户“本次记录不完整”;
write_file和edit_file直接写入真实文件,没有统一的修改事务;- Bash 在真实工作区执行,可以修改大量文件、启动子进程或访问外部服务;
- 当前回退会检查文件是否仍和任务结束时一致,这个保护是正确的,但只能按整个文件恢复,不能处理同一文件中的用户修改和 Agent 修改;
- Git 暂存区、未跟踪文件、重命名、文件权限、符号链接和二进制文件缺少统一规则。
主要入口:
src/sztu_code/core/changes.pysrc/sztu_code/core/tools/builtin/write_file.pysrc/sztu_code/core/tools/builtin/edit_file.pysrc/sztu_code/core/tools/builtin/bash.pysrc/sztu_code/core/tools/invocation.pysrc/sztu_code/core/workspace/manager.py
修改后应该是什么样
1. 每次任务有独立的修改账本
对每次可能产生副作用的工具调用,记录:
- 准备做什么;
- 得到了什么权限;
- 执行前文件是什么版本;
- 实际改了哪些文件;
- 能不能撤销;
- 如果进程中断,结果是否确定。
最终 Diff 应该由这些记录组成,而不是只在结束时扫描一次目录。
2. 保护用户原有修改
任务开始时保存用户工作区的基线,包括:
- Git HEAD;
- 暂存区;
- 未暂存修改;
- 未跟踪文件。
Agent 的改动应叠加在这个基线上。接受或撤销 Agent 改动后,用户原来的状态必须保持不变。
不能使用 git reset --hard、git clean 或自动 stash 来简化实现。
3. 尽可能在隔离工作区执行
Git 项目可以使用临时 worktree 或覆盖层;非 Git 项目可以使用按需复制的临时工作区。Agent、测试和 Shell 优先操作隔离版本,用户接受后再合并回真实项目。
需要注意:只从 Git HEAD 创建 worktree 不够,因为那会漏掉用户当前未提交的修改。隔离工作区必须包含用户开始任务时看到的真实基线。
对于无法隔离、必须直接操作真实项目的任务,要明确提示风险并使用严格的版本检查。
4. 写入前检查文件有没有变化
Agent 读取文件后准备修改时,如果文件已经被用户或另一个 Agent 改过,旧修改不能直接覆盖新内容。
系统应:
- 能自动合并时进行三方合并;
- 同一段内容冲突时暂停并展示冲突;
- 记录冲突来自用户还是另一个子 Agent。
5. Shell 命令也必须进入同一套管理
Shell 不能因为是一个通用工具就绕过修改账本:
- 执行前记录工作区版本和命令范围;
- 执行后实际检测文件变化;
- 子进程属于当前任务,取消时清理整棵进程树;
- 写出工作区、访问网络、全局安装或发布等能力单独授权;
- 命令是否执行成功无法确认时,标记为“结果未知”,不能自动重试。
6. 撤销本身也要留下记录
撤销不是删除历史。系统要保留原修改、撤销操作和撤销结果。如果文件已经变化,撤销进入冲突状态,不能悄悄覆盖。
必须保证的规则
- 用户原有修改永远不能被 Agent 回退操作覆盖。
- 每个 Agent 修改都能追溯到具体 run、子 Agent 和工具调用。
- Diff、测试结果和用户点击接受必须针对同一个文件版本。
- 文件又被修改后,旧测试结果和旧审核结论自动失效。
- 无法确认是否执行成功的外部操作,必须显示“结果未知”。
- 无法完整记录的大文件或外部目录不能被静默忽略。
- 子 Agent 可以分别工作,但合并时必须检测冲突。
建议分阶段实现
第一阶段:修改账本和文件版本检查
- 将首尾快照升级为按工具调用记录的修改账本;
- write/edit 工具统一通过 WorkspaceTransaction 写入;
- 保存用户脏工作区基线;
- 支持安全的文件级接受和撤销。
第二阶段:隔离工作区和 Shell 管理
- 实现 Git worktree/覆盖层和非 Git 临时工作区;
- 监控 Shell 实际产生的文件变化;
- 管理子进程树、越界写入和结果未知状态;
- daemon 重启后恢复未结束的修改记录。
第三阶段:局部审核和并发合并
- 支持按文件、按修改片段接受或撤销;
- 支持用户与 Agent、多个子 Agent 的三方合并;
- 把测试和审核结果绑定到具体工作区版本;
- 在桌面端和 TUI 显示修改来源、冲突和不可撤销操作。
验收标准
- 在同时存在 staged、unstaged 和 untracked 文件的项目中运行 Agent,接受或撤销后,用户原有状态逐字节不变。
- write/edit、Shell、子 Agent 产生的每个文件变化都能找到来源。
- 用户在 Agent 完成后修改同一文件,再点击撤销时,系统提示冲突,不覆盖新内容。
- 在文件创建、删除、重命名和写入过程中强制结束 daemon,重启后能明确判断已完成、未执行、冲突或结果未知。
- 大文件、二进制文件、文件权限和符号链接不会被静默漏记。
- Shell 启动的后台子进程在取消后被清理;无法清理时明确报告。
- 任意新修改都会让旧版本的测试证据失效。
- 10 万文件仓库有性能测试,不能每次写文件都重新扫描整个项目。
维护者备注
底层可以把一次工具副作用分为“准备、执行、观察、确认、补偿”几个状态。这里不要求对网络发布或数据库写入实现万能回滚;正确做法是承认哪些操作不可逆,并通过权限、查询接口和人工确认管理风险。
Activity
[-][Harness Epic] 构建工作区事务层:隔离 Agent 修改、保护用户改动并支持安全回滚[/-][+][Harness Epic] 建立 Effect Kernel:工作区事务、外部副作用账本与补偿恢复[/+][-][Harness Epic] 建立 Effect Kernel:工作区事务、外部副作用账本与补偿恢复[/-][+][Harness Epic] 分清哪些改动是 Agent 做的,并能安全撤销或处理冲突[/+]