mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-06 09:47:44 +02:00
[eric] agents: FIX latent NameError on cwd sessions (ensure_cwd_git_repo import mismatch) + clean workspace_git + regression test
This commit is contained in:
@@ -71,7 +71,7 @@ from backend.apps.agents.manager.MessagingMixin import MessagingMixin
|
||||
from backend.apps.agents.manager.AgentLaunchMixin import AgentLaunchMixin
|
||||
from backend.apps.agents.manager.RunSupportMixin import RunSupportMixin
|
||||
from backend.apps.agents.manager.permissions import gate_hooks
|
||||
from backend.apps.agents.manager.session.workspace_git import _detect_git_identity as detect_git_identity, _ensure_cwd_git_repo
|
||||
from backend.apps.agents.manager.session.workspace_git import detect_git_identity, ensure_cwd_git_repo
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import (
|
||||
FULL_TOOLS,
|
||||
get_all_known_tool_names,
|
||||
|
||||
@@ -21,8 +21,8 @@ from backend.apps.settings.settings import load_settings
|
||||
from backend.apps.agents.manager.session.session_store import _load_session_data as load_session_data
|
||||
from backend.apps.agents.manager.session.apply_context_window import apply_context_window
|
||||
from backend.apps.agents.manager.session.workspace_git import (
|
||||
_detect_git_identity as detect_git_identity,
|
||||
_ensure_cwd_git_repo as ensure_cwd_git_repo,
|
||||
detect_git_identity,
|
||||
ensure_cwd_git_repo,
|
||||
)
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import get_all_tool_names
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_mode
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from typeguard import typechecked
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _ensure_cwd_git_repo(cwd: str, home: str | None = None) -> None:
|
||||
@typechecked
|
||||
def ensure_cwd_git_repo(cwd: str, home: Optional[str] = None) -> None:
|
||||
"""Idempotently make `cwd` into a git repo with a valid HEAD.
|
||||
|
||||
The CLI's built-in Agent tool uses `isolation: "worktree"` to spawn
|
||||
@@ -31,35 +35,35 @@ def _ensure_cwd_git_repo(cwd: str, home: str | None = None) -> None:
|
||||
if not os.path.isdir(cwd):
|
||||
return
|
||||
|
||||
import subprocess as _sp_git
|
||||
import subprocess as sp_git
|
||||
# Case A: cwd is inside some git repo (possibly parent). Verify
|
||||
# HEAD resolves. If the enclosing repo is broken (e.g. a stray
|
||||
# `.git` in $HOME with no commits, which makes workspaces
|
||||
# under ~/.openswarm/workspaces/ inherit a broken HEAD), we
|
||||
# need to init a fresh repo AT cwd so it shadows the parent.
|
||||
_inside = _sp_git.run(
|
||||
inside = sp_git.run(
|
||||
["git", "rev-parse", "--is-inside-work-tree"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.PIPE, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
stdout=sp_git.PIPE, stderr=sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _inside.returncode == 0 and b"true" in _inside.stdout:
|
||||
if inside.returncode == 0 and b"true" in inside.stdout:
|
||||
# Check HEAD resolves (has at least one commit).
|
||||
_head = _sp_git.run(
|
||||
head = sp_git.run(
|
||||
["git", "rev-parse", "--verify", "HEAD"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=5,
|
||||
stdout=sp_git.DEVNULL, stderr=sp_git.DEVNULL, timeout=5,
|
||||
)
|
||||
if _head.returncode == 0:
|
||||
if head.returncode == 0:
|
||||
return # parent repo is healthy, leave it alone
|
||||
# Parent repo exists but HEAD is broken.
|
||||
if os.path.isdir(os.path.join(cwd, ".git")):
|
||||
# .git is directly here, commit to fix it.
|
||||
_sp_git.run(
|
||||
sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
stdout=sp_git.DEVNULL, stderr=sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
return
|
||||
# .git is in a parent dir (broken home-dir repo, etc.).
|
||||
@@ -68,23 +72,24 @@ def _ensure_cwd_git_repo(cwd: str, home: str | None = None) -> None:
|
||||
|
||||
# Case B: cwd is not a git repo at all (or parent is broken):
|
||||
# init + empty commit here.
|
||||
_sp_git.run(
|
||||
sp_git.run(
|
||||
["git", "init", "-q", "-b", "main"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
stdout=sp_git.DEVNULL, stderr=sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
_sp_git.run(
|
||||
sp_git.run(
|
||||
["git", "-c", "user.email=openswarm@local",
|
||||
"-c", "user.name=OpenSwarm",
|
||||
"commit", "--allow-empty", "-q", "-m", "openswarm init"],
|
||||
cwd=cwd,
|
||||
stdout=_sp_git.DEVNULL, stderr=_sp_git.DEVNULL, timeout=10,
|
||||
stdout=sp_git.DEVNULL, stderr=sp_git.DEVNULL, timeout=10,
|
||||
)
|
||||
except Exception as _e:
|
||||
logger.info(f"[agent-cwd] git init skipped: {_e}")
|
||||
except Exception as exc:
|
||||
logger.info(f"[agent-cwd] git init skipped: {exc}")
|
||||
|
||||
|
||||
def _detect_git_identity(cwd: str) -> tuple[str | None, str | None]:
|
||||
@typechecked
|
||||
def detect_git_identity(cwd: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Resolve the origin remote and current branch for `cwd`.
|
||||
|
||||
Used to label sessions in the session list ("Agent on owner/repo
|
||||
@@ -97,12 +102,12 @@ def _detect_git_identity(cwd: str) -> tuple[str | None, str | None]:
|
||||
if not cwd or not os.path.isdir(cwd):
|
||||
return (None, None)
|
||||
try:
|
||||
import subprocess as _sp
|
||||
url_proc = _sp.run(
|
||||
import subprocess as sp
|
||||
url_proc = sp.run(
|
||||
["git", "remote", "get-url", "origin"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
cwd=cwd, stdout=sp.PIPE, stderr=sp.DEVNULL, timeout=3,
|
||||
)
|
||||
repo_url: str | None = None
|
||||
repo_url: Optional[str] = None
|
||||
if url_proc.returncode == 0:
|
||||
raw = url_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw:
|
||||
@@ -113,11 +118,11 @@ def _detect_git_identity(cwd: str) -> tuple[str | None, str | None]:
|
||||
repo_url = f"{scheme}://{rest}"
|
||||
else:
|
||||
repo_url = raw
|
||||
branch_proc = _sp.run(
|
||||
branch_proc = sp.run(
|
||||
["git", "branch", "--show-current"],
|
||||
cwd=cwd, stdout=_sp.PIPE, stderr=_sp.DEVNULL, timeout=3,
|
||||
cwd=cwd, stdout=sp.PIPE, stderr=sp.DEVNULL, timeout=3,
|
||||
)
|
||||
branch_name: str | None = None
|
||||
branch_name: Optional[str] = None
|
||||
if branch_proc.returncode == 0:
|
||||
raw_b = branch_proc.stdout.decode("utf-8", errors="replace").strip()
|
||||
if raw_b:
|
||||
|
||||
@@ -197,6 +197,38 @@ def test_loop_builds_direct_anthropic_key_env(monkeypatch):
|
||||
assert env == {"ANTHROPIC_API_KEY": "sk-ant-test123"} # direct key, no 9router proxy
|
||||
|
||||
|
||||
def test_loop_with_session_cwd_runs_workspace_git_init(monkeypatch):
|
||||
# Regression: a session WITH a cwd hits the workspace git-init call in the loop. Harness
|
||||
# sessions normally have no cwd, which masked a NameError (the call said ensure_cwd_git_repo
|
||||
# while only _ensure_cwd_git_repo was imported). raising=True here would fail if the name were
|
||||
# missing again; the assertions confirm the cwd path actually runs and the turn completes.
|
||||
import backend.apps.agents.agent_manager as am
|
||||
called = {}
|
||||
|
||||
def fake_ensure(cwd, home=None):
|
||||
called["cwd"] = cwd
|
||||
|
||||
monkeypatch.setattr(am, "ensure_cwd_git_repo", fake_ensure, raising=True)
|
||||
|
||||
events = []
|
||||
|
||||
async def fake_send(session_id, event, data):
|
||||
events.append((event, data))
|
||||
|
||||
monkeypatch.setattr(ws_mod.ws_manager, "send_to_session", fake_send, raising=True)
|
||||
monkeypatch.setattr(claude_agent_sdk, "query", _mock_query_yielding(
|
||||
_assistant([TextBlock(text="done")]), _result()), raising=True)
|
||||
|
||||
mgr = AgentManager()
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
session = AgentSession(name="t", model="sonnet", dashboard_id="d", cwd="/tmp/openswarm-test-ws")
|
||||
mgr.sessions[session.id] = session
|
||||
asyncio.run(mgr.p_run_agent_loop(session.id, "hi"))
|
||||
|
||||
assert called.get("cwd") == "/tmp/openswarm-test-ws" # the git-init path ran (no NameError)
|
||||
assert session.status == "completed"
|
||||
|
||||
|
||||
def test_full_streaming_turn_drives_the_complete_ws_contract(monkeypatch):
|
||||
# The closest in-repo proxy for a live streaming run: drive the REAL loop with the exact
|
||||
# SDK sequence the live provider emits, partial StreamEvents (block start -> text deltas ->
|
||||
|
||||
Reference in New Issue
Block a user