惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
云风的 BLOG
云风的 BLOG
罗磊的独立博客
C
Check Point Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
爱范儿
爱范儿
I
InfoQ
Hugging Face - Blog
Hugging Face - Blog
T
Tailwind CSS Blog
B
Blog RSS Feed

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(agents): compare file-target structurally not via fin...
RenzoMXD · 2026-05-08 · via Recent Commits to openclaw:main

@@ -88,67 +88,162 @@ describe("tool mutation helpers", () => {

8888

).toBe(false);

8989

});

909091+

it("populates structured fileTarget for file-mutating calls (#79024)", () => {

92+

expect(buildToolMutationState("edit", { file_path: "/tmp/a" }).fileTarget).toEqual({

93+

path: "/tmp/a",

94+

});

95+

expect(buildToolMutationState("write", { path: "/tmp/Foo|bar" }).fileTarget).toEqual({

96+

path: "/tmp/foo|bar",

97+

});

98+

// Non-file-mutating tools never carry fileTarget, even with a path arg.

99+

expect(buildToolMutationState("bash", { command: "rm /tmp/a" }).fileTarget).toBeUndefined();

100+

expect(buildToolMutationState("exec", { command: "touch /tmp/a" }).fileTarget).toBeUndefined();

101+

// apply_patch is excluded from file-mutating set, so no fileTarget even

102+

// if a path-shaped arg is synthetically present.

103+

expect(

104+

buildToolMutationState("apply_patch", { input: "*** Update File: /tmp/a" }).fileTarget,

105+

).toBeUndefined();

106+

});

107+91108

it("recognizes cross-tool file-mutation recovery on the same target (#79024)", () => {

92109

expect(

93110

isSameToolMutationAction(

94-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

95-

{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/a" },

111+

{

112+

toolName: "edit",

113+

actionFingerprint: "tool=edit|path=/tmp/a",

114+

fileTarget: { path: "/tmp/a" },

115+

},

116+

{

117+

toolName: "write",

118+

actionFingerprint: "tool=write|path=/tmp/a",

119+

fileTarget: { path: "/tmp/a" },

120+

},

96121

),

97122

).toBe(true);

98123

expect(

99124

isSameToolMutationAction(

100-

{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/a" },

101-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

125+

{

126+

toolName: "write",

127+

actionFingerprint: "tool=write|path=/tmp/a",

128+

fileTarget: { path: "/tmp/a" },

129+

},

130+

{

131+

toolName: "edit",

132+

actionFingerprint: "tool=edit|path=/tmp/a",

133+

fileTarget: { path: "/tmp/a" },

134+

},

102135

),

103136

).toBe(true);

104137

// `apply_patch` is intentionally excluded from the file-mutating set

105138

// because production `apply_patch` calls only carry opaque `input` text,

106-

// so real fingerprints never have a `path=` segment to compare. Even a

107-

// synthetic path-bearing fingerprint must not unlock recovery.

139+

// so `extractFileTarget` returns `undefined` and the fail-closed branch

140+

// refuses cross-tool recovery.

108141

expect(

109142

isSameToolMutationAction(

110-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

111-

{ toolName: "apply_patch", actionFingerprint: "tool=apply_patch|path=/tmp/a" },

143+

{

144+

toolName: "edit",

145+

actionFingerprint: "tool=edit|path=/tmp/a",

146+

fileTarget: { path: "/tmp/a" },

147+

},

148+

{

149+

toolName: "apply_patch",

150+

actionFingerprint: "tool=apply_patch|path=/tmp/a",

151+

fileTarget: { path: "/tmp/a" },

152+

},

112153

),

113154

).toBe(false);

114155

});

115156116157

it("does not cross-recover file mutations on different targets (#79024)", () => {

117158

expect(

118159

isSameToolMutationAction(

119-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

120-

{ toolName: "write", actionFingerprint: "tool=write|path=/tmp/b" },

160+

{

161+

toolName: "edit",

162+

actionFingerprint: "tool=edit|path=/tmp/a",

163+

fileTarget: { path: "/tmp/a" },

164+

},

165+

{

166+

toolName: "write",

167+

actionFingerprint: "tool=write|path=/tmp/b",

168+

fileTarget: { path: "/tmp/b" },

169+

},

121170

),

122171

).toBe(false);

123172

});

124173174+

it("does not over-match paths containing the fingerprint delimiter (#79024)", () => {

175+

// The fingerprint string carries raw paths separated by `|`. A naive

176+

// `split("|")` parser would extract `path=/tmp/a` from both fingerprints

177+

// and incorrectly clear the prior failure. Structural fileTarget

178+

// comparison fails closed for these distinct paths.

179+

expect(

180+

isSameToolMutationAction(

181+

{

182+

toolName: "edit",

183+

actionFingerprint: "tool=edit|path=/tmp/a|left",

184+

fileTarget: { path: "/tmp/a|left" },

185+

},

186+

{

187+

toolName: "write",

188+

actionFingerprint: "tool=write|path=/tmp/a|right",

189+

fileTarget: { path: "/tmp/a|right" },

190+

},

191+

),

192+

).toBe(false);

193+

// Same delimiter-bearing path on both sides still matches.

194+

expect(

195+

isSameToolMutationAction(

196+

{

197+

toolName: "edit",

198+

actionFingerprint: "tool=edit|path=/tmp/a|shared",

199+

fileTarget: { path: "/tmp/a|shared" },

200+

},

201+

{

202+

toolName: "write",

203+

actionFingerprint: "tool=write|path=/tmp/a|shared",

204+

fileTarget: { path: "/tmp/a|shared" },

205+

},

206+

),

207+

).toBe(true);

208+

});

209+125210

it("does not cross-recover when the recovery tool is not file-mutating (#79024)", () => {

126211

expect(

127212

isSameToolMutationAction(

128-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

213+

{

214+

toolName: "edit",

215+

actionFingerprint: "tool=edit|path=/tmp/a",

216+

fileTarget: { path: "/tmp/a" },

217+

},

129218

{ toolName: "bash", actionFingerprint: "tool=bash|meta=cat /tmp/a" },

130219

),

131220

).toBe(false);

132221

expect(

133222

isSameToolMutationAction(

134-

{ toolName: "edit", actionFingerprint: "tool=edit|path=/tmp/a" },

223+

{

224+

toolName: "edit",

225+

actionFingerprint: "tool=edit|path=/tmp/a",

226+

fileTarget: { path: "/tmp/a" },

227+

},

135228

{ toolName: "exec", actionFingerprint: "tool=exec|meta=touch /tmp/a" },

136229

),

137230

).toBe(false);

138231

});

139232140233

it("ignores call-specific noise when comparing the cross-tool target (#79024)", () => {

141-

// `id=...` and `meta=...` segments must not block recovery when the

142-

// stable `path=...` target still matches.

234+

// `id=...` and `meta=...` segments differ between calls; structural

235+

// fileTarget comparison is unaffected.

143236

expect(

144237

isSameToolMutationAction(

145238

{

146239

toolName: "edit",

147240

actionFingerprint: "tool=edit|path=/tmp/a|id=42|meta=edit /tmp/a",

241+

fileTarget: { path: "/tmp/a" },

148242

},

149243

{

150244

toolName: "write",

151245

actionFingerprint: "tool=write|path=/tmp/a|id=99|meta=write /tmp/a",

246+

fileTarget: { path: "/tmp/a" },

152247

},

153248

),

154249

).toBe(true);