diff --git a/agent/utils/github_token.py b/agent/utils/github_token.py index 6b2006ee..b44353eb 100644 --- a/agent/utils/github_token.py +++ b/agent/utils/github_token.py @@ -28,15 +28,20 @@ class GitHubAuthError(Exception): def repo_cache_key(repo: Any) -> str | None: - """Normalize a repo dict/string to ``owner/name`` (None when unknown).""" + """Normalize a repo dict/string to a casefolded ``owner/name`` (None if unknown). + + GitHub owner/name are case-insensitive, so the key is casefolded on both the + write (binding) and read (compare) sides to keep ``Org/Repo`` and ``org/repo`` + a single repo and avoid spurious cross-repo mismatches. + """ if isinstance(repo, str): cleaned = repo.strip() - return cleaned or None + return cleaned.casefold() or None if isinstance(repo, Mapping): owner = repo.get("owner") name = repo.get("name") if isinstance(owner, str) and isinstance(name, str) and owner and name: - return f"{owner}/{name}" + return f"{owner}/{name}".casefold() return None diff --git a/agent/utils/sandbox_state.py b/agent/utils/sandbox_state.py index 90581273..70764344 100644 --- a/agent/utils/sandbox_state.py +++ b/agent/utils/sandbox_state.py @@ -19,6 +19,7 @@ from deepagents.backends.protocol import ( ) from langgraph.config import get_config +from .github_token import repo_cache_key from .sandbox import create_sandbox logger = logging.getLogger(__name__) @@ -167,7 +168,9 @@ async def get_bound_repo_from_metadata(thread_id: str) -> str | None: if not isinstance(metadata, dict): return None bound_repo = metadata.get("bound_repo") - return bound_repo if isinstance(bound_repo, str) and bound_repo else None + # Casefold on read so legacy metadata written before normalization (e.g. + # ``Org/Repo``) still compares equal to the casefolded current repo key. + return repo_cache_key(bound_repo) if isinstance(bound_repo, str) and bound_repo else None def clear_sandbox_backend(thread_id: str) -> None: