




















@@ -88,6 +88,95 @@ describe("tool mutation helpers", () => {
8888).toBe(false);
8989});
909091+it("recognizes cross-tool file-mutation recovery on the same target (#79024)", () => {
92+expect(
93+isSameToolMutationAction(
94+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
95+{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/a" },
96+),
97+).toBe(true);
98+expect(
99+isSameToolMutationAction(
100+{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/a" },
101+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
102+),
103+).toBe(true);
104+expect(
105+isSameToolMutationAction(
106+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
107+{ toolName: "apply_patch", actionFingerprint: "tool=apply_patch|path=/tmp/a" },
108+),
109+).toBe(true);
110+});
111+112+it("does not cross-recover file mutations on different targets (#79024)", () => {
113+expect(
114+isSameToolMutationAction(
115+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
116+{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/b" },
117+),
118+).toBe(false);
119+});
120+121+it("does not cross-recover when the recovery tool is not file-mutating (#79024)", () => {
122+expect(
123+isSameToolMutationAction(
124+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
125+{ toolName: "bash", actionFingerprint: "tool=bash|meta=cat /tmp/a" },
126+),
127+).toBe(false);
128+expect(
129+isSameToolMutationAction(
130+{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },
131+{ toolName: "exec", actionFingerprint: "tool=exec|meta=touch /tmp/a" },
132+),
133+).toBe(false);
134+});
135+136+it("ignores call-specific noise when comparing the cross-tool target (#79024)", () => {
137+// `id=...` and `meta=...` segments must not block recovery when the
138+// stable `path=...` target still matches.
139+expect(
140+isSameToolMutationAction(
141+{
142+toolName: "edit",
143+actionFingerprint: "tool=edit|path=/tmp/a|id=42|meta=edit /tmp/a",
144+},
145+{
146+toolName: "write",
147+actionFingerprint: "tool=write|path=/tmp/a|id=99|meta=write /tmp/a",
148+},
149+),
150+).toBe(true);
151+});
152+153+it("requires `oldpath` to agree across cross-tool recovery (#79024)", () => {
154+expect(
155+isSameToolMutationAction(
156+{
157+toolName: "apply_patch",
158+actionFingerprint: "tool=apply_patch|path=/tmp/a|oldpath=/tmp/old",
159+},
160+{
161+toolName: "write",
162+actionFingerprint: "tool=write|path=/tmp/a|oldpath=/tmp/old",
163+},
164+),
165+).toBe(true);
166+expect(
167+isSameToolMutationAction(
168+{
169+toolName: "apply_patch",
170+actionFingerprint: "tool=apply_patch|path=/tmp/a|oldpath=/tmp/old",
171+},
172+{
173+toolName: "apply_patch",
174+actionFingerprint: "tool=apply_patch|path=/tmp/a|oldpath=/tmp/different",
175+},
176+),
177+).toBe(false);
178+});
179+91180it("keeps legacy name-only mutating heuristics for payload fallback", () => {
92181expect(isLikelyMutatingToolName("sessions_spawn")).toBe(true);
93182expect(isLikelyMutatingToolName("sessions_send")).toBe(true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。