Route Codex subagent homes through the short Windows parent

`_ephemeral_session_parent` matched the agent name exactly, so only `codex`
reached the short `~/.unsloth/.tmp/u-codex-*` root. The subagent path is created
as `codex-subagent`, and it also nests CODEX_HOME one level deeper under
`<home>/parent`, so it needed the short root more than a plain launch, not less.

On Windows with an 8 character mkdtemp suffix the resulting CODEX_HOME was 86
characters against 248 for the git limit, where current main was 69 and a plain
codex launch is 45. Codex checks out its curated plugins below CODEX_HOME, which
is what exceeded the limit in the first place. Both names now take the short
root and the same `u-codex-` prefix, so one scavenger pass covers both, and the
subagent home lands at 52.

`_session_config` now derives its prefix from `_ephemeral_session_prefix` for
both branches rather than rebuilding it inline.

unsloth_cli/tests/test_start.py: 385 passed.
This commit is contained in:
danielhanchen 2026-07-29 08:36:24 +00:00
commit c3d09c0fa1
2 changed files with 31 additions and 4 deletions

View file

@ -2808,9 +2808,13 @@ def _temporary_agent_config(prefix: str):
yield path
# codex-subagent nests CODEX_HOME under <home>/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

View file

@ -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 <home>/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.