- Introduced TodoListMiddleware to the middleware stack for better task management.
- Updated HITL interrupt configuration to include 'delete' operations requiring approval.
- Implemented error handling for delete operations in read-only and memory backends.
- Enhanced approval prompt formatting to display file paths for delete actions.
- Added tests to ensure delete operations are correctly blocked or prompted for approval.
- Updated dependencies to use deepagents 0.7.0 and langchain 1.5.3 for improved functionality.
* feat(backends): use _platform_quote for Windows cmd.exe compatibility
Resolves the 3 skipped E2E tests in test_backends.py that exercised
the /skills/... mount path. The path-rewriter was wrapping resolved
absolute paths via shlex.quote (POSIX single-quote style); cmd.exe
doesn't strip single quotes, so the literal ' characters ended up in
the subprocess argv and the python script failed to find its file.
Replace the 3 shlex.quote call sites in _resolve_virtual_mount_path
with _platform_quote, a thin platform dispatcher:
- POSIX: shlex.quote (unchanged)
- Windows: _cmd_quote uses cmd.exe-compatible double-quote wrapping
and properly escapes embedded " and percent signs
Adds:
- backends.py: _is_windows, _cmd_quote, _platform_quote (~40 lines)
- test_backends.py: 6 TestPlatformQuote unit tests + _split_cmd
cross-platform tokenizer helper to replace shlex.split in the 8
sites that tokenize convert_virtual_paths_in_command results
(POSIX shlex strips backslashes from bare Windows paths, which
broke the 5 TestVirtualMountResolution assertions on Windows)
Removes:
- 3 @pytest.mark.skipif(sys.platform == "win32") markers on the
E2E tests for /skills/... mount resolution
Refs #274.
* fix: escape % as %% in _cmd_quote instead of relying on double-quoting
cmd.exe expands %VAR% before processing quotes, so double-quoting
cannot neutralize percent signs. Escape bare % as %% (the cmd.exe
idiom for a literal percent) before any other quoting logic.
Also updates _cmd_quote docstring and _resolve_virtual_mount_path
docstring to reflect the actual quoting strategy.
* style: fix ruff format (single → double quotes)
* fix: treat % as regular char in _cmd_quote, document limitation
%% escaping only collapses in .bat/.cmd files, not via cmd /c.
Since virtual-mount paths should never contain % in practice,
simpler to leave % alone and document the caveat.
* fix(backends): rewrite quoted virtual paths containing whitespace
The `convert_virtual_paths_in_command` regex
`(?<=\s)/[^\s;|&<>'"`]*` stopped at the first whitespace or quote,
so:
- `python "/skills/my skill/main.py"` was left completely
unchanged (the `(?<=\s)` lookbehind failed after the opening
`"`), and the shell then broke the inner unquoted path at the
embedded space.
- `python /skills/my skill/main.py` was truncated to
`python ./skills/my skill/main.py` (only `/skills/my` rewritten).
Replace the regex with `shlex.shlex(command, posix=True,
punctuation_chars=";|&<>")` so quoted regions stay whole, then
splice the rewrite back into the original command — extending the
splice span to include any matching quote chars around the path so
the fresh `shlex.quote` of the replacement isn't double-wrapped.
`_resolve_virtual_mount_path` now returns the unquoted path; the
caller owns shell-quoting, which avoids the previous
`shlex.quote` inside original `"…"` leaving literal `'` chars in
the argument value.
Unquoted paths with embedded whitespace remain a known limitation
(shlex has no way to know the user meant one path) — the
workaround of avoiding spaces in skill directory names still
applies, as flagged in the original issue.
Closes#237
* fix(backends): backslash-escaped paths, multi-path per token, subshell paths
- Fix backslash-escape handling: use unescape before rewriting
- Fix re.search→re.finditer: all /-paths in a token are rewritten
- Keep ( ) and backticks inside word tokens so paths spanning
\ or wrapped in backticks are matched correctly
- Add _try_rewrite helper with URL detection and unescape logic
- Add 10 contract tests pinning the din0s review cases
* fix(backends): restore () and backtick as shell operators for validate_command
- Restore ( ) and backtick to the operator set in _shell_token_spans.
Removing them caused a security regression: commands like (sudo ls)
would not detect sudo as a blocked command because (sudo became one
word token. With operators restored, validate_command correctly
catches blocked commands inside subshells and command substitutions.
- Fix _value_span_to_raw_span: the 'quoted' flag from the tokenizer
means the token *contains* a quoted segment (not necessarily starts
with a quote). Replace raw[0] assumption with a forward scan for
the first quote char, consuming unquoted prefix chars 1:1.
- Update test_system_path_with_shell_expansion: paths are now
partially rewritten because () are operators. Test updated to
reflect this known limitation (security > path rewriting).
* fix(test): cross-platform compatibility for pre-existing Windows failures
- python3 -> python in execute() calls (python is on PATH in any activated venv)
- sleep 10 -> _sleep_cmd(10) cross-platform helper
- str().endswith() -> Path().parts assertions (backslash-safe on Windows)
- shlex.quote exact-match assertions -> 'in' assertions (Windows quotes paths differently)
- mkdir -p E2E test -> preprocessor boundary test
- Skip 3 E2E tests on Windows: shlex.quote produces POSIX quoting incompatible with cmd.exe
141 passed, 3 skipped on Windows.
* fix: update docstring + strengthen shell-expansion test assertion
- Fix _value_span_to_raw_span docstring: no longer assumes raw[0] is
the opening quote, scans forward for first quote char
- Strengthen test_system_path_with_shell_expansion: verify
./workspace/notes is rewritten, not just notes in result
* style: ruff format backends.py + test_backends.py
* refactor(backends): simplify quoted virtual path rewriting
Replace 500+ line shlex tokenizer with 12-line pre-process step. Match quoted args via regex, unescape, rewrite via _rewrite_quoted_path, substitute with shlex.quote. 133 passed, 3 skipped.
* fix: guard bare absolute paths from double-rewrite by post-process regex
On POSIX, shlex.quote returns bare paths (e.g. /tmp/memories/note.md).
The pre-process substitutes these into the command, then the post-process
regex re-matches and incorrectly rewrites them.
Fix: _guard_bare_absolute wraps bare /-paths in single quotes so the
post-process regex''s character class stops at the quote char.
* style: ruff format
* fix(backends): narrow pre-process to exclude system-prefixed paths
Only rewrite quoted paths that are NOT known system prefixes.
* fix: narrow quoted-path pre-process to virtual mounts only
Only rewrite quoted /... paths that resolve to actual virtual mounts (/skills/..., /memories/...) or workspace-prefixed system paths. Remove catch-all that incorrectly rewrote bare paths like echo /hi.
Addresses din0s review feedback on #269.
* docs: update docstring for narrower quoted-path rewrite scope
* feat(dangerous-mode): implement real-filesystem access with safety checks
- Introduced a 'dangerous mode' allowing the agent to operate on the real filesystem.
- Updated command validation to bypass path confinement while enforcing a blocklist for privileged commands.
- Added warnings and guidelines for users when operating in dangerous mode.
- Enhanced configuration to support dangerous mode and ensure it implies auto-approval.
- Updated tests to verify the behavior of commands and configurations in dangerous mode.
* feat(dangerous-mode): enhance logging and environment management for dangerous mode
* feat(dangerous-mode): improve handling of dangerous mode with environment flags and enhance test isolation
* ci: add windows-latest to test matrix + fix 11 cross-platform test bugs
The test workflow ran on ``ubuntu-latest`` only. Per the issue's
first bullet — the maintainer's explicit #1 priority — add
``windows-latest`` to the matrix so the manager and related
modules are exercised on Windows on every PR.
The matrix addition surfaces 18 pre-existing Windows-only test
failures. Without fixes the new leg would be 18+ reds from
day one and the matrix would just produce a wall of
``fail-fast`` noise. This PR fixes 11 of them; each fix is
a real (cross-platform) bug, not a Windows-specific hack —
most were already flagged by CodeRabbit on PR #236 but never
acted on. The remaining 4 failures need code refactors
(``os.killpg`` → ``psutil`` in ``background.py``,
``convert_virtual_paths_in_command`` Windows-aware quoting,
tilde expansion) that are documented as out-of-scope
follow-ups below.
## What changed
* ``.github/workflows/test.yml``
- ``os: [ubuntu-latest, windows-latest]`` → 2 OS × 2 Python
= 4 cells.
- ``fail-fast: false`` so one bad cell doesn't cancel the
rest while the Windows leg is being brought up. Removable
in a future PR once the suite is fully green.
* ``tests/test_backends.py``
- Hard-coded ``"python3"`` → ``{sys.executable}`` in 7
test commands. Windows has no ``python3`` on PATH; using
``sys.executable`` is portable and matches what CodeRabbit
flagged on PR #236.
- Strict string comparisons → ``shlex.split`` round-trip in
5 resolver tests. ``shlex.quote`` adds single quotes
around backslash paths on Windows, which broke the
direct ``==`` compare.
- Cross-platform suffix checks in 2 path-resolution tests
(``Path(resolved).parts[-2:]`` instead of
``str(resolved).endswith("src/main.py")``).
- ``mkdir -p`` → ``sys.executable -c "import os;
os.makedirs(...)"`` in the cwd-sanitization test.
- ``skipif(sys.platform == "win32")`` on 3 e2e tests that
hit the underlying ``shlex.quote`` + ``cmd.exe`` quoting
bug (real, separate issue).
* ``tests/test_sessions.py``
- ``test_uses_data_dir``: check ``.evoscientist`` in the
long path form (via ``Path.resolve()``) rather than the
short-path form ``get_db_path`` returns on Windows.
* ``tests/test_mcp_client.py``
- ``endswith("python")`` → ``Path(result).stem.lower()`` so
``python.EXE`` matches on Windows.
- ``endswith("npx")`` also accepts ``npx.cmd`` so the npm
shim on Windows matches.
## Out of scope (follow-up issues to file)
* ``os.killpg`` doesn't exist on Windows
(``EvoScientist/background.py:248``) — 3 background tests
fail. Real fix is the same ``psutil`` walk pattern PR #200
shipped in ``langgraph_dev/manager.py``.
* Tilde expansion in file mentions.
* Windows-aware shell quoting in
``convert_virtual_paths_in_command``.
* Path conventions (``~/.config/evoscientist/`` vs
``%APPDATA%\EvoScientist``) — needs design discussion +
``platformdirs`` migration.
* Cross-module audit of
``EvoScientist/tools/execute.py``,
``EvoScientist/ccproxy_manager.py``,
``EvoScientist/config/onboard.py``.
Closes#207 (step 1 only — CI matrix + the easy test
fixes; remaining bullets tracked separately).
* fix: cross-platform compatibility for Windows CI runners
- background.py: replace POSIX-only os.killpg/os.getpgid with
cross-platform _kill_process_tree() helper. On Windows falls back
to Popen.terminate()/Popen.kill() (TerminateProcess); on POSIX
keeps existing os.killpg logic.
- test_backends.py: replace mkdir -p shell execution in
test_literal_workspace_path_replaced with preprocessing-boundary
assertion (patch LocalShellBackend.execute, capture command,
assert workspace path was rewritten to ./). Avoids POSIX-only
mkdir -p on Windows runners.
- test_file_mentions.py: monkeypatch USERPROFILE on Windows so
ntpath.expanduser() resolves ~ to tmp_path even when HOME is
unset on CI runners.
* fix(test): cross-platform sleep/true commands for Windows CI
Replace POSIX-only sleep/true with module-level helpers that use
ping -n / cmd /c on Windows. Also fix python3 -> sys.executable
in the non-timeout recovery test.
- test_background.py: 7 sleep/true fixes
- test_background_middleware.py: 6 sleep/true fixes
- test_backends.py: 4 sleep fixes + 1 python3 fix
2318 passed, 0 failed on Windows.
* fix(test): use shell-portable double quotes for python -c on Windows
cmd.exe does not treat single quotes as string delimiters, so
-c 'raise SystemExit(1)' was passed with literal quotes on Windows.
Switch to double quotes which work on both cmd.exe and POSIX sh.
* fix: use psutil for Windows process tree kill + avoid sys.executable under uv
- background.py: replace Popen.terminate()/kill() with psutil-based
process tree walking on Windows. TerminateProcess does NOT cascade
to grandchildren; psutil.Process.children(recursive=True) ensures
the entire tree is signaled.
- test_backends.py: replace sys.executable with 'python' in sandbox
execute() calls. Under uv, sys.executable is under the workspace
and gets rewritten to ./ by prepare_sandbox_command, breaking
Linux CI. The plain 'python' command resolves correctly in any
activated venv.
* fix: broaden try/except in _kill_process_tree to cover proc.children()
If the process exits between Process(popen.pid) and children(recursive=True),
the children call raises an uncaught exception escaping stop(). Move it inside
the existing try/except block.
* fix: narrow exception to ProcessLookupError in POSIX _kill_process_tree
OSError is too broad — would silently swallow EPERM on SIGKILL, leaving
the process alive when we report it as stopped. Match original behavior
which only caught ProcessLookupError (process already gone).
* style: ruff format test_backends.py
* ci: trigger re-run for flaky prompt_toolkit test
* style: fix ruff check (import order + RUF005 unpacking)
---------
Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
* feat: implement configurable sandbox execute timeout and enhance recovery instructions
* feat: add background process management tools and middleware for sandbox execution
* feat: enhance background process management with completion notifications and deduplication
* feat: enhance sandbox execution timeout validation and update related messages
* feat: enhance background process management with thread-specific completion notifications and HITL approval handling
* test: assert completion notification waits for process finish timestamp
* feat(backends): implement tier-aware virtual mount resolution for skills and memories
* test: add end-to-end test for workspace tier shadowing global tier in CustomSandboxBackend
* feat(backends): enhance virtual mount resolution for skills and memories with tier paths and quoting
* fix(tests): update Python command in virtual mount resolution tests to use python3
- Added blank lines for better separation of test cases in multiple test files.
- Reformatted event handling in tests for clarity and consistency.
- Ensured consistent use of multi-line formatting for dictionary arguments in event handling.
- Improved assertions and test descriptions for better understanding.
- Updated test cases across various modules including test_stream_state, test_stream_utils, test_summarization, test_thread_selector, test_tool_error_handler, test_tui_widgets, test_ui_runtime, and test_wechat_channel.
- Introduced a new test file `test_rich_escape.py` to validate the safety of Rich markup escaping in the ToolResultFormatter.
- Added tests to ensure that tool names and error messages containing brackets do not cause crashes during formatting.
fix(utils): add skills support in load_subagents function
chore: update dependencies to latest versions
test: remove unused working_dir parameter in CustomSandboxBackend tests