diff --git a/unsloth_cli/commands/start.py b/unsloth_cli/commands/start.py index 7b34bb9dd8..73465ee4ed 100644 --- a/unsloth_cli/commands/start.py +++ b/unsloth_cli/commands/start.py @@ -2808,9 +2808,13 @@ def _temporary_agent_config(prefix: str): yield path +# codex-subagent nests CODEX_HOME under /parent, so it needs the short root too. +_CODEX_SHORT_HOME_AGENTS = ("codex", "codex-subagent") + + def _ephemeral_session_parent(agent: str) -> Optional[Path]: """Return a non-system-temp parent when an agent needs one.""" - if os.name != "nt" or agent != "codex": + if os.name != "nt" or agent not in _CODEX_SHORT_HOME_AGENTS: return None # Codex creates a deeply nested curated-plugin checkout below CODEX_HOME. # A normal %TEMP%\unsloth-codex-* home can exceed legacy Windows path @@ -2824,7 +2828,9 @@ def _ephemeral_session_parent(agent: str) -> Optional[Path]: def _ephemeral_session_prefix(agent: str, parent: Optional[Path]) -> str: """Return the platform-specific prefix for an ephemeral agent home.""" - return "u-codex-" if agent == "codex" and parent is not None else f"unsloth-{agent}-" + if agent in _CODEX_SHORT_HOME_AGENTS and parent is not None: + return "u-codex-" + return f"unsloth-{agent}-" @contextlib.contextmanager @@ -2967,11 +2973,12 @@ def _session_config( # Windows codex keeps #7519's short, locked home (MAX_PATH + stale reclaim); # every other agent uses the Studio-private root. parent = _ephemeral_session_parent(agent) + prefix = _ephemeral_session_prefix(agent, parent) if parent is not None: - with _short_ephemeral_session(parent) as path: + with _short_ephemeral_session(parent, prefix) as path: yield path else: - with _temporary_agent_config(f"unsloth-{agent}-") as path: + with _temporary_agent_config(prefix) as path: yield path else: # Never wipe this dir: a previously printed recipe may still be running diff --git a/unsloth_cli/tests/test_start.py b/unsloth_cli/tests/test_start.py index 4e33b15064..1674838482 100644 --- a/unsloth_cli/tests/test_start.py +++ b/unsloth_cli/tests/test_start.py @@ -5199,6 +5199,26 @@ def test_session_config_reclaims_old_short_homes_but_keeps_recent_and_live(monke assert not first.exists() +@pytest.mark.parametrize("agent", ["codex", "codex-subagent"]) +def test_windows_codex_homes_use_the_short_parent(monkeypatch, tmp_path, agent): + # codex-subagent nests CODEX_HOME under /parent, so the long Studio auth + # path would eat even more of the legacy MAX_PATH budget than a plain launch. + monkeypatch.setattr(start.os, "name", "nt") + monkeypatch.setattr(start.Path, "home", staticmethod(lambda: tmp_path)) + + assert start._ephemeral_session_parent(agent) == tmp_path / ".unsloth" / ".tmp" + parent = start._ephemeral_session_parent(agent) + assert start._ephemeral_session_prefix(agent, parent) == "u-codex-" + + +def test_non_codex_agents_keep_the_studio_private_root(monkeypatch, tmp_path): + monkeypatch.setattr(start.os, "name", "nt") + monkeypatch.setattr(start.Path, "home", staticmethod(lambda: tmp_path)) + + assert start._ephemeral_session_parent("claude") is None + assert start._ephemeral_session_prefix("claude", None) == "unsloth-claude-" + + def test_session_config_reclaims_abandoned_homes_for_non_codex_agents(monkeypatch, tmp_path): # These homes sit under Studio's auth tree, which nothing else prunes, so a wrapper # killed before its finally runs must be reclaimed by the next launch.