fix(codex): purge removes tui-thread-reference-capabilities/<uuid> (#78)
This commit is contained in:
1 parent
c6eb4564d2
commit
51dfd38f0b
2 files changed
+96
-2
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import os, json, glob, sqlite3, subprocess
|
||||
import os, json, glob, shutil, sqlite3, subprocess
|
||||
from typing import Optional, Any, List
|
||||
from lib_py.agents.base import BaseAgentAdapter, DiscoveryContext, _ro_connect, scan_names, db_rows_naming
|
||||
from lib_py.agents.base import BaseAgentAdapter, DiscoveryContext, _plausible_id, _ro_connect, scan_names, db_rows_naming
|
||||
from lib_py.verify_session import workspace_key
|
||||
|
||||
class CodexAgentAdapter(BaseAgentAdapter):
|
||||
@@ -131,6 +131,22 @@ class CodexAgentAdapter(BaseAgentAdapter):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _purge_tui_thread_capability(self, uuid: str, ctx: DiscoveryContext, purged: List[str]) -> None:
|
||||
if not _plausible_id(uuid):
|
||||
return
|
||||
cap = os.path.join(self._codex_home(ctx), "tui-thread-reference-capabilities", uuid)
|
||||
if not os.path.lexists(cap):
|
||||
return
|
||||
try:
|
||||
if os.path.isdir(cap) and not os.path.islink(cap):
|
||||
shutil.rmtree(cap)
|
||||
else:
|
||||
os.unlink(cap)
|
||||
if not os.path.lexists(cap):
|
||||
purged.append(cap)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def purge_artifacts(self, uuid: str, ctx: DiscoveryContext) -> List[str]:
|
||||
purged = []
|
||||
path = self._find_rollout(uuid, ctx)
|
||||
@@ -140,12 +156,16 @@ class CodexAgentAdapter(BaseAgentAdapter):
|
||||
if res.returncode == 0:
|
||||
if path:
|
||||
purged.append(path)
|
||||
self._purge_tui_thread_capability(uuid, ctx, purged)
|
||||
return purged
|
||||
except Exception:
|
||||
pass
|
||||
if path and os.path.exists(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
purged.append(path)
|
||||
except Exception:
|
||||
pass
|
||||
db = self._state_db(ctx)
|
||||
if db and os.path.exists(db):
|
||||
try:
|
||||
@@ -156,6 +176,7 @@ class CodexAgentAdapter(BaseAgentAdapter):
|
||||
purged.append(f"state db row for thread: {uuid}")
|
||||
except Exception:
|
||||
pass
|
||||
self._purge_tui_thread_capability(uuid, ctx, purged)
|
||||
return purged
|
||||
|
||||
def residual_artifacts(self, uuid: str, ctx: DiscoveryContext) -> List[str]:
|
||||
|
||||
@@ -249,3 +249,76 @@ def test_stop_purge_refusal_prints_holders(mam_sandbox, mock_herdr, monkeypatch)
|
||||
finally:
|
||||
holder.kill()
|
||||
holder.wait()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("delete_ok", [True, False], ids=["delete_ok", "delete_fails"])
|
||||
def test_codex_purge_removes_tui_thread_capability(tmp_path, monkeypatch, delete_ok):
|
||||
home = tmp_path / "home"
|
||||
codex_home = tmp_path / "codex-home"
|
||||
ws = tmp_path / "ws"
|
||||
bindir = tmp_path / "bin"
|
||||
for d in (home, codex_home, ws, bindir):
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
conv = str(uuidlib.uuid4())
|
||||
other = str(uuidlib.uuid4())
|
||||
|
||||
rollout = codex_home / "sessions" / "2026" / "10" / "28" / f"rollout-2026-10-28T00-00-00-{conv}.jsonl"
|
||||
rollout.parent.mkdir(parents=True, exist_ok=True)
|
||||
rollout.write_text(json.dumps({"payload": {"id": conv, "cwd": str(ws)}}) + "\n", encoding="utf-8")
|
||||
|
||||
cap_dir = codex_home / "tui-thread-reference-capabilities"
|
||||
cap_dir.mkdir(parents=True, exist_ok=True)
|
||||
cap_mine = cap_dir / conv
|
||||
cap_other = cap_dir / other
|
||||
cap_mine.write_text("cap-mine", encoding="utf-8")
|
||||
cap_other.write_text("cap-other", encoding="utf-8")
|
||||
|
||||
state_db = codex_home / "state_5.sqlite"
|
||||
conn = sqlite3.connect(str(state_db))
|
||||
conn.execute("CREATE TABLE threads (id TEXT PRIMARY KEY, rollout_path TEXT, cwd TEXT)")
|
||||
conn.execute("INSERT INTO threads VALUES (?, ?, ?)", (conv, str(rollout), str(ws)))
|
||||
conn.execute("INSERT INTO threads VALUES (?, ?, ?)", (other, "", str(ws)))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
fake_codex = bindir / "codex"
|
||||
if delete_ok:
|
||||
# Simulate `codex delete <uuid> --force` removing rollout + DB row, leaving tui-thread-reference-capabilities.
|
||||
fake_codex.write_text(
|
||||
f"#!{sys.executable}\n"
|
||||
"import os, sqlite3, sys\n"
|
||||
"if len(sys.argv) >= 3 and sys.argv[1] == 'delete':\n"
|
||||
f" p = {str(rollout)!r}\n"
|
||||
" if os.path.exists(p):\n"
|
||||
" os.remove(p)\n"
|
||||
f" conn = sqlite3.connect({str(state_db)!r})\n"
|
||||
" conn.execute('DELETE FROM threads WHERE id=?', (sys.argv[2],))\n"
|
||||
" conn.commit()\n"
|
||||
" conn.close()\n"
|
||||
" sys.exit(0)\n"
|
||||
"sys.exit(1)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
else:
|
||||
fake_codex.write_text("#!/bin/sh\nexit 1\n", encoding="utf-8")
|
||||
fake_codex.chmod(0o755)
|
||||
|
||||
monkeypatch.setenv("HOME", str(home))
|
||||
monkeypatch.setenv("HOME_DIR", str(home))
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
monkeypatch.setenv("PATH", f"{bindir}:{os.environ.get('PATH', '')}")
|
||||
|
||||
adapter = get_adapter("codex")
|
||||
ctx = DiscoveryContext(workspace=str(ws), agent_name="codex", home_dir=str(home))
|
||||
assert str(cap_mine) in adapter.residual_artifacts(conv, ctx)
|
||||
|
||||
purged = adapter.purge_artifacts(conv, ctx)
|
||||
|
||||
assert str(cap_mine) in purged
|
||||
assert not cap_mine.exists()
|
||||
assert cap_other.exists()
|
||||
assert not rollout.exists()
|
||||
assert adapter.residual_artifacts(conv, ctx) == []
|
||||
assert str(cap_other) in adapter.residual_artifacts(other, ctx)
|
||||
|
||||
Reference in new issue
Block a user