feat: 路径约定体系 & 自优化管道落地

This commit is contained in:
oliver 2026-05-10 23:28:41 +08:00
parent 08df5d580a
commit df30b5902f
37 changed files with 239 additions and 324 deletions

View file

@ -4,7 +4,7 @@ import tempfile
from pathlib import Path
from oclaw.runtime.tools.catalog import default_registry
from oclaw.runtime.tools.local_sdk.adapter import LocalAdapter
from oclaw.runtime.tools.public.local_sdk import LocalAdapter
from oclaw.runtime.tools.public.edit_file_tool import edit_file_tool
from oclaw.runtime.tools.public.run_command_tool import run_command_tool
from oclaw.runtime.tools.public.read_file_tool import read_file_tool
@ -13,7 +13,6 @@ from oclaw.runtime.tools.public_registry import clear_public_tool_cache
from oclaw.runtime.tools.public.list_directory_tool import list_directory_tool
from oclaw.runtime.tools.public.search_files_tool import search_files_tool
from oclaw.runtime.tools.public.get_cwd_tool import get_cwd_tool
from oclaw.runtime.tools.public.cd_tool import cd_tool
from oclaw.runtime.tools.public.get_env_tool import get_env_tool
from oclaw.runtime.tools.public.set_env_tool import set_env_tool
@ -33,7 +32,6 @@ def test_local_public_read_tool_visible_by_default() -> None:
assert "mkdir" not in names
assert "delete_file" not in names
assert "move_file" not in names
assert "cd" not in names
assert "set_env" not in names
assert "kill_process" not in names
@ -54,7 +52,6 @@ def test_local_public_high_risk_tools_visible_when_enabled(monkeypatch) -> None:
assert "mkdir" in names
assert "delete_file" in names
assert "move_file" in names
assert "cd" in names
assert "set_env" in names
assert "kill_process" in names
@ -160,14 +157,10 @@ def test_p1_p2_read_tools_smoke(tmp_path: Path, monkeypatch) -> None:
out_s = search_files_tool().handler({"pattern": "hell", "root": "d", "regex": False})
assert out_s.get("ok") is True
assert (out_s.get("count") or 0) >= 1
# get_cwd / cd
# get_cwd
out_cwd0 = get_cwd_tool().handler({})
assert out_cwd0.get("ok") is True
out_cd = cd_tool().handler({"cwd": "d"})
assert out_cd.get("ok") is True
out_cwd1 = get_cwd_tool().handler({})
assert out_cwd1.get("ok") is True
assert str(out_cwd1.get("cwd") or "").replace("\\", "/").endswith("/d")
assert str(out_cwd0.get("cwd") or "").replace("\\", "/").rstrip("/") == str(tmp_path).replace("\\", "/").rstrip("/")
# get_env / set_env
out_get0 = get_env_tool().handler({"key": "LOCAL_PUBLIC_TOOLS_TEST_KEY", "default": "x"})
assert out_get0.get("ok") is True
@ -178,6 +171,34 @@ def test_p1_p2_read_tools_smoke(tmp_path: Path, monkeypatch) -> None:
assert out_get1.get("value") == "y"
def test_get_cwd_returns_workspace_root_when_at_data_workspace(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmp_path))
(tmp_path / "data" / "workspace").mkdir(parents=True, exist_ok=True)
adapter = LocalAdapter()
out_cd = adapter.cd(cwd="data/workspace")
assert out_cd.get("ok") is True
out_cwd = adapter.get_cwd()
assert out_cwd.get("ok") is True
norm = str(out_cwd.get("cwd") or "").replace("\\", "/").rstrip("/")
assert norm == str(tmp_path).replace("\\", "/").rstrip("/")
def test_get_cwd_returns_workspace_root_even_after_cd(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmp_path))
(tmp_path / "subdir").mkdir(parents=True, exist_ok=True)
adapter = LocalAdapter()
out_cd = adapter.cd(cwd="subdir")
assert out_cd.get("ok") is True
out_cwd = adapter.get_cwd()
assert out_cwd.get("ok") is True
norm = str(out_cwd.get("cwd") or "").replace("\\", "/").rstrip("/")
assert norm == str(tmp_path).replace("\\", "/").rstrip("/")
def test_run_command_does_not_follow_cd_state(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setenv("OPS_WORKSPACE_ROOT", str(tmp_path))
monkeypatch.setenv("AIA_ENABLE_RUN_COMMAND", "1")
@ -194,8 +215,8 @@ def test_run_command_does_not_follow_cd_state(tmp_path: Path, monkeypatch) -> No
out_run = adapter.run_command(command='python -c "import os; print(os.path.basename(os.getcwd()))"', timeout=20)
assert out_run.get("ok") is True, out_run
# If run_command follows cd state this would be "subdir"; default should be data/workspace.
assert str(out_run.get("cwd") or "").replace("\\", "/").rstrip("/").endswith("/data/workspace")
# If run_command follows cd state this would be "subdir"; default should be workspace root.
assert str(out_run.get("cwd") or "").replace("\\", "/").rstrip("/") == str(tmp_path).replace("\\", "/").rstrip("/")
def test_run_command_reads_db_toggle_without_ops_db_env(tmp_path: Path, monkeypatch) -> None:

View file

@ -14,3 +14,13 @@ def test_memory_tools_only_expose_canonical_names() -> None:
assert all(not str(x).startswith("memory_curator_") for x in names)
def test_generalist_also_exposes_memory_wiki_tools() -> None:
specs = materialize_tool_specs(expert="generalist")
names = {str(x.name or "") for x in specs}
assert "memory_wiki_status" in names
assert "memory_wiki_get" in names
assert "memory_wiki_search" in names
assert "memory_wiki_lint" in names
assert "memory_wiki_apply" in names

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path
from oclaw.platform.config.paths import PROJECT_ROOT
from oclaw.runtime.tools.experts.memory.wiki_tools import (
from oclaw.runtime.tools.public.memory_wiki_tools import (
memory_wiki_apply_tool,
memory_wiki_get_tool,
memory_wiki_lint_tool,

View file

@ -0,0 +1,61 @@
from __future__ import annotations
import importlib.util
from pathlib import Path
from types import SimpleNamespace
def _load_handler_module():
module_path = (
Path(__file__).resolve().parents[1]
/ "skills"
/ "session-bootstrap"
/ "hooks"
/ "runtime"
/ "handler.py"
)
spec = importlib.util.spec_from_file_location("test_session_bootstrap_handler", str(module_path))
assert spec is not None and spec.loader is not None
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def test_build_bootstrap_reads_skills_root_and_user_name(tmp_path: Path) -> None:
mod = _load_handler_module()
repo = tmp_path / "repo"
ws = repo / "workspace"
(repo / "skills" / "session-bootstrap").mkdir(parents=True)
(repo / "data" / "wiki" / "users").mkdir(parents=True)
ws.mkdir(parents=True)
(repo / "skills" / "session-bootstrap" / "SOUL.md").write_text("SOUL_OK", encoding="utf-8")
(repo / "skills" / "session-bootstrap" / "IDENTITY.md").write_text("IDENT_OK", encoding="utf-8")
(repo / "data" / "wiki" / "users" / "current.md").write_text("name: Alice", encoding="utf-8")
mod._repo_root = lambda: repo # type: ignore[attr-defined]
event = SimpleNamespace(context={"workspaceDir": str(ws), "agentId": "generalist"})
content = mod._build_bootstrap_content(event) # type: ignore[attr-defined]
assert "SOUL_OK" in content
assert "IDENT_OK" in content
assert "欢迎回来,Alice" in content
def test_handle_sets_bootstrap_diag(tmp_path: Path) -> None:
mod = _load_handler_module()
repo = tmp_path / "repo"
(repo / "skills" / "session-bootstrap").mkdir(parents=True)
(repo / "skills" / "session-bootstrap" / "SOUL.md").write_text("x", encoding="utf-8")
(repo / "skills" / "session-bootstrap" / "IDENTITY.md").write_text("y", encoding="utf-8")
mod._repo_root = lambda: repo # type: ignore[attr-defined]
event = SimpleNamespace(
type="agent",
action="bootstrap",
sessionKey="agent:main:main",
context={"workspaceDir": str(tmp_path / "ws"), "bootstrapFiles": [], "agentId": "generalist"},
)
mod.handle(event)
diag = event.context.get("sessionBootstrapDiag")
assert isinstance(diag, dict)
assert diag.get("soul_found") is True
assert diag.get("identity_found") is True