From abecd22813ed529033ea0dd389b1d15fee0eb61d Mon Sep 17 00:00:00 2001 From: celanwang Date: Fri, 30 Oct 2026 15:57:51 +0800 Subject: [PATCH] fix(codex): retain snapshot lifecycle compatibility --- reme/components/agent_wrapper/codex_agent_wrapper.py | 12 +++++++++--- tests/unit/test_codex_agent_wrapper.py | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/reme/components/agent_wrapper/codex_agent_wrapper.py b/reme/components/agent_wrapper/codex_agent_wrapper.py index 90b691dd..35aabc18 100644 --- a/reme/components/agent_wrapper/codex_agent_wrapper.py +++ b/reme/components/agent_wrapper/codex_agent_wrapper.py @@ -178,7 +178,8 @@ class CodexAgentWrapper(BaseAgentWrapper): snapshot_dir.mkdir(parents=True, exist_ok=True) fd, raw_path = tempfile.mkstemp(prefix="config-", suffix=".json", dir=snapshot_dir) try: - os.fchmod(fd, 0o600) + if hasattr(os, "fchmod"): + os.fchmod(fd, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as stream: json.dump(self.app_context.app_config.model_dump(mode="json"), stream) except BaseException: @@ -541,8 +542,13 @@ class CodexAgentWrapper(BaseAgentWrapper): for workspace in (_context_workspaces(self.app_context) or {}).values(): if state := workspace.agent_states.get(self.name): state.thread_tool_contexts.clear() - if codex is not None: - await codex.close() + try: + if codex is not None: + await codex.close() + finally: + if self._mcp_snapshot_path is not None: + self._mcp_snapshot_path.unlink(missing_ok=True) + self._mcp_snapshot_path = None async def _prune_thread_tool_contexts(self, codex: AsyncCodex, alias: str = "") -> None: """Forget tool constraints only after the server confirms a thread is unloaded.""" diff --git a/tests/unit/test_codex_agent_wrapper.py b/tests/unit/test_codex_agent_wrapper.py index c498fc41..dbd6f294 100644 --- a/tests/unit/test_codex_agent_wrapper.py +++ b/tests/unit/test_codex_agent_wrapper.py @@ -7,6 +7,7 @@ from dataclasses import dataclass import json import os from pathlib import Path +import stat from types import SimpleNamespace import pytest @@ -18,7 +19,7 @@ from reme.components.job import BackgroundJob from reme.components.outbound_proxy import FixedHttpOutboundProxy from reme.config import resolve_app_config from reme.enumeration import ChunkEnum, ComponentEnum -from reme.schema import Response +from reme.schema import ApplicationConfig, Response # A fresh bridge imports ReMe before replying to initialize; keep this separate # from the 10-second budget for requests to an already initialized server.