Skip to content

fix(file_tools): strip duplicated prefix for cwd-shaped relative paths (#67185) - #67218

Closed
dsitmilis wants to merge 0 commit into
NousResearch:mainfrom
dsitmilis:fix/write-file-cwd-shaped-path
Closed

dsitmilis wants to merge 0 commit into
NousResearch:mainfrom
dsitmilis:fix/write-file-cwd-shaped-path

Conversation

@dsitmilis

Copy link
Copy Markdown

Summary

Fixes #67185 — a model that emits a relative path mirroring the working directory (an absolute path missing its leading '/') was silently written to a doubled location.

Root cause

tools/file_tools.py::_resolve_path_for_task joined any non-absolute input onto the base dir. When the input already begins with the base's own components (e.g. base /home/user/dev, input home/user/dev/notes/x.md), the result was /home/user/dev/home/user/dev/notes/x.md. The write succeeded (parents created), so the failure was silent — the digest just never appeared where expected.

Fix

In the Linux host branch, detect when the relative input starts with the base directory's own components and strip that duplicated prefix so the file lands at the intended location. The guard is wrapped in try/except (ValueError when the base is not under '/', e.g. Windows drive roots), so it is a no-op on platforms where it cannot apply.

Changes

  • tools/file_tools.py: cwd-shaped prefix guard in _resolve_path_for_task.
  • tests/tools/test_resolve_path.py: regression test asserting the resolved path contains the base exactly once (no doubling).

Validation

  • python3 -m pytest tests/tools/test_resolve_path.py tests/tools/test_file_tools.py -> 7 + 51 passed.

Closes #67185.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/file File tools (read, write, patch, search) tool/skills Skills system (list, view, manage) platform/qqbot QQ Bot adapter P2 Medium — degraded but workaround exists needs-decision Awaiting maintainer decision before any implementation duplicate This issue or pull request already exists labels Jul 19, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

The QQ prompt hunk is duplicate of #66976. The independent cwd-path repair is related to #67185, #67216, and #67220; please split the unrelated fixes for review.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for isolating the cwd-prefix failure; the reported doubling is still present on current main at tools/file_tools.py:397.

Problems

  • The new prefix check in tools/file_tools.py:410-414 silently rewrites an ambiguous relative path. A real subtree whose relative name begins with the workspace components is indistinguishable from a missing leading slash. The #67185 discussion instead proposes warning the model while preserving the path semantics.
  • Native Windows remains on the unmodified ntpath.join route at tools/file_tools.py:386-392; #67185 reports the same doubling there. The added test at tests/tools/test_resolve_path.py:90 covers only the POSIX path route.
  • This branch also includes 51cd2733f590, the same QQBot-only commit that is the full change in open #66976. The member comment here requested splitting it.

Suggested changes

  • Split out 51cd2733f590.
  • Preserve ambiguous relative inputs and surface a doubled-path warning with the likely absolute target.
  • Cover the intended behavior on POSIX, native Windows, and container resolution paths.

Automated hermes-sweeper review.

Comment thread tools/file_tools.py Outdated
base_rel = base.relative_to("/")
base_rel_str = str(base_rel)
input_str = p.as_posix()
if base_rel_str and (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition cannot distinguish a missing leading slash from a legitimate relative subtree whose name repeats the workspace components. Rewriting it silently changes valid relative-path semantics; please emit a doubled-path warning instead, as proposed in the #67185 discussion.

Comment thread tests/tools/test_resolve_path.py Outdated

# Input mirrors tmp_path's components with the leading '/' missing.
cwd_shaped = str(tmp_path.relative_to("/"))
result = _resolve_path(f"{cwd_shaped}/notes/x.md")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers only the POSIX Path route. Native Windows still takes the unmodified ntpath.join branch in tools/file_tools.py:391; add coverage there before treating the bug class as fixed.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 19, 2026
@PRATHAMESH75

Copy link
Copy Markdown
Contributor

Reviewed against issue #67185 and current main. The core _resolve_path_for_task change is a plausible fix, but the PR bundles unrelated changes and the added tests look malformed. (Sibling PRs: #67216, #67220, #67232.)

Core change (tools/file_tools.py) — strips the base-directory prefix from a relative input that replays it, then joins:

base_rel_str = str(base.relative_to("/"))
if base_rel_str and (input_str == base_rel_str or input_str.startswith(base_rel_str + "/")):
    stripped = input_str[len(base_rel_str):].lstrip("/")
    p = Path(stripped) if stripped else Path(".")

This lands the file at the intended location for the reported case, and is scoped to an exact base-echo (so it doesn't have the broad root-dir false-positive that #67220's second branch does). Reasonable as far as it goes. Two caveats: (a) it silently rewrites rather than warning — the issue explicitly prefers surfacing a warning (like _path_resolution_warning) because the echo is ambiguous; #67216 takes that non-destructive route. (b) POSIX-only; the Windows ntpath.join branch is untouched though the issue notes the doubling reproduces on native Windows.

Blocking concerns:

  1. Unrelated scope. The diff also rewrites the qqbot platform hint in agent/prompt_builder.py (referencing fix: qqbot platform hint lacks agent-identity framing, causing weaker models to skip skill_view() #66959 — skill_view / "AI Agent" framing) and adds TestQqbotHint in test_system_prompt.py. That has nothing to do with write_file path resolution and shouldn't ride along in a write_file: cwd-shaped relative path (absolute path missing leading /) silently resolves to doubled path #67185 fix — please split it out.

  2. Tests appear malformed. The new blocks read as docstrings/statements appended into the wrong scope rather than real test functions:

    • tests/tools/test_resolve_path.py: the #67185 docstring and assertions are appended after the existing assert inside test_relative_path_prefers_recorded_session_cwd, not in a new def test_....
    • tests/agent/test_system_prompt.py: class TestQqbotHint: contains bare agent = _make_agent(...) / stable = _stable_prompt(...) statements with no test method and no assertions — that executes at class-definition/collection time and asserts nothing.
    • tests/agent/test_prompt_builder.py: hint = PLATFORM_HINTS["qqbot"] is appended with no assertion.
      Please confirm these actually run and fail without the fix — as written they don't look like they exercise anything.

Recommendation: drop the prompt_builder/qqbot changes, turn the additions into real def test_* functions with assertions, and consider warning instead of silently rewriting (per the issue and #67216).

Verdict: Core idea is workable but silent-rewrite + unrelated prompt_builder scope + non-functional tests block it as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists platform/qqbot QQ Bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows tool/file File tools (read, write, patch, search) tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

write_file: cwd-shaped relative path (absolute path missing leading /) silently resolves to doubled path

4 participants