fix(agent): prevent safety guard false positives and streamed message drop

Three independent fixes for issues exposed by PR #3493:

1. shell.py: allow /dev/* paths in workspace guard
   Commands like `rm file.txt 2>/dev/null` were blocked because
   _extract_absolute_paths captured /dev/null as a path outside
   the workspace. Allow /dev like media_path is already allowed.

2. shell.py: remove | from home_paths regex prefix
   Loki query operator `|~` was misinterpreted as pipe + home
   directory, causing false workspace violation errors.

3. loop.py: change _streamed from blacklist to whitelist
   stop_reason "tool_error" was not in the exclusion set
   {"ask_user", "error"}, so _streamed=True was set on fatal
   errors. channel manager then skipped channel.send() because
   it assumed the content was already streamed — but it never
   was. Whitelist to only {"stop", "end_turn", "max_tokens"}.

Also fixes a pre-existing Windows bug in _spawn where
create_subprocess_exec + list2cmdline breaks commands with
paths containing spaces (e.g. D:\Program Files\python.exe).

Closes: #3599, #3605
This commit is contained in:
chengyongru
2026-05-04 01:25:52 +08:00
committed by Xubin Ren
parent 2a7433b7ec
commit d3689d143c
4 changed files with 45 additions and 20 deletions
+21
View File
@@ -315,6 +315,27 @@ def test_exec_guard_blocks_windows_drive_root_outside_workspace(monkeypatch) ->
assert "hard policy boundary" in error
def test_exec_guard_allows_dev_null_redirect(tmp_path) -> None:
tool = ExecTool(restrict_to_workspace=True)
ws = tmp_path / "workspace"
ws.mkdir()
(ws / "file.txt").write_text("ok", encoding="utf-8")
error = tool._guard_command(f'rm "{ws / "file.txt"}" 2>/dev/null', str(ws))
assert error is None
def test_exec_guard_allows_dev_urandom(tmp_path) -> None:
tool = ExecTool(restrict_to_workspace=True)
error = tool._guard_command("cat /dev/urandom | head -c 16 > random.bin", str(tmp_path))
assert error is None
def test_exec_extract_absolute_paths_ignores_pipe_tilde() -> None:
cmd = "python query.py --query '{job=\"app\"} |~ \"error\"'"
paths = ExecTool._extract_absolute_paths(cmd)
assert not any(p.startswith("~") for p in paths)
# --- cast_params tests ---