Conversation
`astrbot_file_edit_tool` ran `old` and `new` through `_decode_escaped_text` unconditionally, so a backslash-n in the arguments always became a real line break. The read, write and grep tools pass their strings through unchanged, so a file written by `astrbot_file_write_tool` can hold a literal "\n" that the edit tool could then never match: any Python or JavaScript source with "\n" in a string literal, a regex such as r"\number", or a Windows path such as C:\new\temp. Inserting such text was corrupted the same way. Try the arguments as they were given first and fall back to the decoded form only when that finds nothing to replace, so a model that escapes its arguments keeps working.
Contributor
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/tools/computer_tools/fs.py" line_range="618-621" />
<code_context>
+ # unchanged, so a file may legitimately contain a literal "\n".
+ # Use the arguments as they were given and only fall back to
+ # decoding escape sequences when that finds nothing to replace.
+ attempts = [(old, new)]
+ decoded = (_decode_escaped_text(old), _decode_escaped_text(new))
+ if decoded != (old, new):
+ attempts.append(decoded)
sb = await get_booter(
context.context.context,
context.context.event.unified_msg_origin,
</code_context>
<issue_to_address>
**issue (bug_risk):** The fallback decodes `old` and `new` as an inseparable pair, so an edit that needs legacy decoding for `old` but contains a literal escape in `new` writes a decoded, corrupted replacement instead of the literal text supplied by the caller. For example, when the file contains an actual newline and `old` is sent as `alpha\nbeta`, a literal `new` value such as `C:\new\temp` is decoded to a newline-containing path during the fallback.
**Triggers:** When the old text requires the legacy escape-decoding fallback but the replacement text is intended to remain literal.
**Suggested fix:** Try the original and decoded forms with an explicit per-argument policy, or provide an unambiguous tool contract rather than decoding both arguments together.
```suggestion
attempts = [(old, new)]
decoded_old = _decode_escaped_text(old)
if decoded_old != old:
attempts.append((decoded_old, new))
```
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and the edit tool now persists whichever literal or decoded match succeeds first, so an ambiguous or unintended match could write the wrong contents to a file. Reverting the code would not undo files already changed, but the impact is bounded and the file can generally be restored or corrected.
Blocking findings: astrbot/core/tools/computer_tools/fs.py:621
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
astrbot_file_edit_toolruns both of its arguments through_decode_escaped_textunconditionally (fs.py#L614-L615), so a backslash-n inoldornewalways becomes a real line break.The other three filesystem tools do not do this.
astrbot_file_read_toolreturns the file verbatim,astrbot_file_write_toolwritescontentverbatim, andastrbot_grep_toolpassespatternthrough untouched —_decode_escaped_texthas exactly one caller in the whole repository. So a file the model just wrote withastrbot_file_write_toolcan legitimately contain a literal\n, andastrbot_file_edit_toolcan then never match it.Running AstrBot's own
_decode_escaped_textover realistic edit arguments:old/newas the model sends itprint("hello\n")print("hello+ newline +")pattern = re.compile(r"\number\t(\d+)")pattern = re.compile(r"+ newline +umber+ tab +(\d+)")LOG = r"C:\new\temp\report.txt"LOG = r"C:+ newline +ew+ tab +emp\report.txt""\t".join(cols)"+ tab +".join(cols)None of the four round-trip. Because this is a code editing tool, writing
\nor\tliterally is the common case, not the exotic one: any Python or JavaScript source with"\n"in a string literal, any regex, any Windows path. The failure is also silent in the worst direction — when onlynewcontains the escape, the replacement succeeds and writes corrupted content into the file.Modifications / 改动点
astrbot/core/tools/computer_tools/fs.py—FileEditTool.callnow triesold/newas they were given and only falls back to the decoded form when the first attempt finds nothing to replace. The decode is kept, so a model that escapes its arguments keeps working exactly as before; it just stops being applied to text that matches the file as written. Both the plain and the descriptor-basededit_filepaths go through the same loop._decode_escaped_textitself is unchanged.该工具原先无条件对
old和new调用_decode_escaped_text,导致源码里的\n、正则表达式和 Windows 路径都会被破坏;而读取、写入和 grep 三个工具都原样传递字符串。现在先按原样匹配,只有匹配不到时才回退到解码后的形式,因此原有行为仍然保留。Screenshots or Test Results / 运行截图或测试结果
Four tests added to
tests/test_computer_fs_tools.py, covering both the plain and the descriptor-based edit path.Against the unfixed source (
git stashonfs.pyonly):The one that passes on both sides is
test_file_edit_still_decodes_escapes_when_the_text_does_not_match: it pins the existing escaped-argument behaviour, so the fallback is covered by a test that was green before the change too.With the fix:
Both remaining failures are
tests/test_fastapi_v1_dashboard.py::test_config_update_revokes_only_affected_shell_sessions; they fail the same way on a cleanorigin/mastercheckout and are unrelated to this change.ruff checkandruff format --checkare clean on both files.Checklist / 检查清单
😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
/ 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。 (Bug fix, no new feature.)
👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
/ 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
📚 I checked the affected WebUI instructions and screenshots in
docs/zhanddocs/enagainst the changed navigation, page structure, and labels, and updated them in this PR (or explained why no documentation update is needed)./ 我已对照变化后的 WebUI 入口、页面结构和术语,核对并在本 PR 中更新
docs/zh和docs/en的相关操作说明与截图(或说明无需更新文档的原因)。 (No WebUI entry point, page or label changes: this only affects howastrbot_file_edit_toolmatches its arguments.)🤓 I have ensured that no new dependencies are introduced.
/ 我确保没有引入新依赖库。
😮 My changes do not introduce malicious code.
/ 我的更改没有引入恶意代码。
Summary by Sourcery
Fix file editing so it matches and writes literal escape sequences correctly without breaking existing escaped-argument support.
Bug Fixes:
Tests: