Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions openjiuwen/core/sys_operation/cwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ def set_workspace(path: str) -> None:
_state().workspace = _resolve(path)


def get_agent_history_base_dir() -> str:
"""Base directory for per-session tool history (``.agent_history/``).

Prefers the agent workspace; when none is configured, falls back to a
user-level directory (``~/.openjiuwen/agent_history``) instead of the
project CWD, so history files never pollute the user's project tree
(#1490).
"""
workspace = get_workspace()
if workspace:
return workspace
home = os.path.expanduser("~")
return os.path.join(home, ".openjiuwen", "agent_history")


# ---- Team workspace: shared across team members ---------------------------

def get_team_workspace() -> str | None:
Expand Down
6 changes: 3 additions & 3 deletions openjiuwen/harness/tools/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from openjiuwen.core.common.logging import logger
from openjiuwen.core.foundation.tool.base import Tool
from openjiuwen.core.sys_operation import SysOperation
from openjiuwen.core.sys_operation.cwd import get_cwd, get_workspace
from openjiuwen.core.sys_operation.cwd import get_agent_history_base_dir, get_cwd, get_workspace
from openjiuwen.harness.prompts.tools import build_tool_card
from openjiuwen.harness.tools.base_tool import ToolOutput

Expand Down Expand Up @@ -904,7 +904,7 @@ def _detect_encoding(raw: bytes) -> str:
return "utf-16-le" if raw[:2] == b"\xff\xfe" else "utf-8"

def _build_history_path(self, session: Any) -> str:
base_dir = get_workspace() or str(pathlib.Path(get_cwd()).expanduser().resolve())
base_dir = get_agent_history_base_dir()
agent_id = (
session.agent_id() if hasattr(session, "agent_id")
else session.get_agent_id() if hasattr(session, "get_agent_id")
Expand Down Expand Up @@ -1221,7 +1221,7 @@ def _try_quote_variants(self, content: str, old_str: str) -> Optional[str]:
return content[index:index + len(old_str)]

def _build_history_path(self, session: Any) -> str:
base_dir = get_workspace() or str(pathlib.Path(get_cwd()).expanduser().resolve())
base_dir = get_agent_history_base_dir()
agent_id = (
session.agent_id() if hasattr(session, "agent_id")
else session.get_agent_id() if hasattr(session, "get_agent_id")
Expand Down
4 changes: 2 additions & 2 deletions openjiuwen/harness/tools/shell/bash/_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def _parse_inputs(inputs: Dict[str, Any]) -> _BashInputs:
# ── invoke ────────────────────────────────────────────────

def _build_history_path(self, session: Any) -> str:
from openjiuwen.core.sys_operation.cwd import get_cwd, get_workspace
base_dir = get_workspace() or str(pathlib.Path(get_cwd()).expanduser().resolve())
from openjiuwen.core.sys_operation.cwd import get_agent_history_base_dir
base_dir = get_agent_history_base_dir()
agent_id = (
session.agent_id() if hasattr(session, "agent_id")
else session.get_agent_id() if hasattr(session, "get_agent_id")
Expand Down
4 changes: 2 additions & 2 deletions openjiuwen/harness/tools/shell/powershell/_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ def _parse_inputs(inputs: Dict[str, Any]) -> _PowerShellInputs:
)

def _build_history_path(self, session: Any) -> str:
from openjiuwen.core.sys_operation.cwd import get_cwd, get_workspace
base_dir = get_workspace() or str(pathlib.Path(get_cwd()).expanduser().resolve())
from openjiuwen.core.sys_operation.cwd import get_agent_history_base_dir
base_dir = get_agent_history_base_dir()
return os.path.join(
base_dir, ".agent_history",
f"file_ops_{self._agent_id}_{session.get_session_id()}.json",
Expand Down
42 changes: 42 additions & 0 deletions tests/unit_tests/core/sys_operation/test_cwd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# coding: utf-8
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.

"""Unit tests for the per-agent CWD state helpers in cwd.py.

Focus: ``get_agent_history_base_dir`` — the tool-history base directory
that must prefer the agent workspace and, when none is configured, fall
back to a user-level directory instead of the project CWD (#1490).
"""

import os
import tempfile

from openjiuwen.core.sys_operation.cwd import (
get_agent_history_base_dir,
init_cwd,
set_workspace,
)


def _reset_cwd_state() -> None:
"""Reinitialize the per-context CWD state without a workspace."""
init_cwd(os.getcwd())


class TestGetAgentHistoryBaseDir:
def test_returns_workspace_when_set(self):
workspace = tempfile.mkdtemp()
try:
set_workspace(workspace)
assert get_agent_history_base_dir() == os.path.realpath(workspace)
finally:
_reset_cwd_state()

def test_falls_back_to_user_level_dir_when_workspace_unset(self):
_reset_cwd_state()
base = get_agent_history_base_dir()
assert ".openjiuwen" in base
assert "agent_history" in base
assert not base.startswith(os.getcwd()), (
"history base must NOT be the project CWD (#1490)"
)