From 593c0ce6babc69a1d14290748d7be27fe3214041 Mon Sep 17 00:00:00 2001 From: huangsen Date: Thu, 8 Oct 2026 22:07:51 +0800 Subject: [PATCH] fix(codex): retain status resources for active MCP connections --- integrations/codex/README.md | 1 + integrations/codex/README_ZH.md | 1 + .../plugins/reme/.codex-plugin/plugin.json | 2 +- integrations/codex/plugins/reme/reme_tools.py | 10 ++++--- .../codex/plugins/reme/ui/status.html | 2 +- integrations/codex/status-app/src/status.js | 2 +- .../test_codex_plugin_status_ui.py | 2 +- tests/unit/test_codex_plugin_mcp.py | 27 +++++++++++++++++++ 8 files changed, 39 insertions(+), 8 deletions(-) diff --git a/integrations/codex/README.md b/integrations/codex/README.md index 4e2db6b6..3aba9ca3 100644 --- a/integrations/codex/README.md +++ b/integrations/codex/README.md @@ -223,6 +223,7 @@ with the previous address; switch back to retry them. Changes made during consol | --- | --- | | No MCP settings entry | Confirm your desktop version meets the requirement above, then open the installed ReMe detail and its `reme` MCP card; confirm it is enabled and restart. | | Hooks settings keep loading or do not show ReMe | Open a local project folder and select the local host before reopening Hooks settings; see below. | +| Status cannot open after an update, with `No such file or directory` pointing to an old plugin version | Fully quit and reopen Codex, then reopen ReMe status from MCP settings. Closing the card or starting a new conversation does not reload an existing MCP connection. | | Blank status card followed by a plugin feature loading error | Check network access to the Codex sandbox page; see below. | | Connection healthy, no recording or recall | Review every ReMe Hook; check the auto switches and recent activity. Health does not prove Hook execution. | | Turns stay queued | Keep Codex and ReMe running. Check the ReMe model configuration and service logs, and confirm the startup command includes `service.tool_error_on_failure=true`. | diff --git a/integrations/codex/README_ZH.md b/integrations/codex/README_ZH.md index 5f85241f..e1852918 100644 --- a/integrations/codex/README_ZH.md +++ b/integrations/codex/README_ZH.md @@ -217,6 +217,7 @@ ReMe-Example-7319 的评审时间和项目关键词是什么? | --- | --- | | 找不到 MCP 设置 | 确认桌面端符合上述版本要求,再打开已安装的 ReMe 详情及 `reme` MCP 卡片;确认启用后重启。 | | Hooks 设置一直加载或没有 ReMe | 先打开本地项目文件夹并选择本机,再重新进入 Hooks 设置;见下方说明。 | +| 升级后状态页打不开,`No such file or directory` 指向旧插件版本 | 完全退出并重新打开 Codex,再从 MCP 设置打开 ReMe status。关闭卡片或新建会话不会重新加载已有的 MCP 连接。 | | 状态卡片空白,随后提示“插件功能未成功加载” | 检查 Codex 沙箱页面的网络访问;见下方说明。 | | 连接健康,但没有记录或召回 | 检查全部 ReMe Hook 的信任状态、自动开关及最近活动;连接健康不代表 Hook 已执行。 | | 记录一直在队列中 | 保持 Codex 和 ReMe 运行,检查 ReMe 模型配置和服务日志,并确认启动命令包含 `service.tool_error_on_failure=true`。 | diff --git a/integrations/codex/plugins/reme/.codex-plugin/plugin.json b/integrations/codex/plugins/reme/.codex-plugin/plugin.json index 5924f53f..4dfd74ad 100644 --- a/integrations/codex/plugins/reme/.codex-plugin/plugin.json +++ b/integrations/codex/plugins/reme/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "reme", - "version": "0.2.5", + "version": "0.2.6", "description": "Automatic memory, focused recall, runtime status, and consolidation through ReMe MCP.", "author": { "name": "EconML team of Alibaba Tongyi Lab", diff --git a/integrations/codex/plugins/reme/reme_tools.py b/integrations/codex/plugins/reme/reme_tools.py index 9e6760da..6bce9f6c 100644 --- a/integrations/codex/plugins/reme/reme_tools.py +++ b/integrations/codex/plugins/reme/reme_tools.py @@ -77,6 +77,10 @@ def status_text(snapshot: dict, language: str) -> str: def register_tools(server: FastMCP) -> None: """Keep local tools discoverable even while the upstream service is unavailable.""" + # Codex may remove this installation cache before closing the existing MCP connection. + root = Path(__file__).parent + status_html = (root / "ui/status.html").read_text(encoding="utf-8") + plugin_version = json.loads((root / ".codex-plugin/plugin.json").read_text(encoding="utf-8"))["version"] @server.tool(name=SEARCH, title="Search ReMe memory", annotations={"readOnlyHint": True}) async def search( @@ -110,8 +114,7 @@ def register_tools(server: FastMCP) -> None: @server.resource(STATUS_URI, title="ReMe status", mime_type="text/html;profile=mcp-app", meta=STATUS_META) def status_view() -> ResourceResult: """Serve a self-contained read-only view without exposing a local HTTP server.""" - html = (Path(__file__).parent / "ui/status.html").read_text(encoding="utf-8") - return ResourceResult([ResourceContent(html, mime_type="text/html;profile=mcp-app", meta=STATUS_META)]) + return ResourceResult([ResourceContent(status_html, mime_type="text/html;profile=mcp-app", meta=STATUS_META)]) @server.tool( name=STATUS, @@ -126,11 +129,10 @@ def register_tools(server: FastMCP) -> None: service_snapshot(config, "health_check"), service_snapshot(config, "status"), ) - manifest = Path(__file__).parent / ".codex-plugin/plugin.json" snapshot = { "checked_at": time.time(), "language": config["language"], - "plugin_version": json.loads(manifest.read_text(encoding="utf-8"))["version"], + "plugin_version": plugin_version, "mcpUrl": config["mcpUrl"], "service": {"health_check": health, "status": components}, **local_status(config), diff --git a/integrations/codex/plugins/reme/ui/status.html b/integrations/codex/plugins/reme/ui/status.html index 5aae92d7..1d2aa786 100644 --- a/integrations/codex/plugins/reme/ui/status.html +++ b/integrations/codex/plugins/reme/ui/status.html @@ -658,7 +658,7 @@ } const host = await transport.request("ui/initialize", { protocolVersion: "2026-01-26", - appInfo: { name: "reme-status", version: "0.2.5" }, + appInfo: { name: "reme-status", version: "0.2.6" }, appCapabilities: { availableDisplayModes: ["fullscreen"] } }); if (host.protocolVersion !== "2026-01-26") throw new Error("Unsupported UI protocol"); diff --git a/integrations/codex/status-app/src/status.js b/integrations/codex/status-app/src/status.js index 33612e9d..241eab95 100644 --- a/integrations/codex/status-app/src/status.js +++ b/integrations/codex/status-app/src/status.js @@ -235,7 +235,7 @@ $("refresh").addEventListener("click", async () => { async function connect() { if (window.parent === window) { text("loading", t("noHost")); return; } const host = await transport.request("ui/initialize", { - protocolVersion: "2026-01-26", appInfo: { name: "reme-status", version: "0.2.5" }, + protocolVersion: "2026-01-26", appInfo: { name: "reme-status", version: "0.2.6" }, appCapabilities: { availableDisplayModes: ["fullscreen"] }, }); if (host.protocolVersion !== "2026-01-26") throw new Error("Unsupported UI protocol"); diff --git a/tests/integration/test_codex_plugin_status_ui.py b/tests/integration/test_codex_plugin_status_ui.py index c34e4b95..0413ea3a 100644 --- a/tests/integration/test_codex_plugin_status_ui.py +++ b/tests/integration/test_codex_plugin_status_ui.py @@ -22,7 +22,7 @@ def snapshot(language): return { "checked_at": 1790757300, "language": language, - "plugin_version": "0.2.5", + "plugin_version": "0.2.6", "mcpUrl": "http://localhost:2333/mcp", "service": { "health_check": {"reachable": True, "answer": "ReMe v0.4.1.13 - healthy"}, diff --git a/tests/unit/test_codex_plugin_mcp.py b/tests/unit/test_codex_plugin_mcp.py index aede23a6..31cd33e7 100644 --- a/tests/unit/test_codex_plugin_mcp.py +++ b/tests/unit/test_codex_plugin_mcp.py @@ -5,6 +5,7 @@ import asyncio import importlib import json +import shutil from datetime import datetime, timezone from pathlib import Path from unittest.mock import AsyncMock @@ -220,6 +221,32 @@ async def test_status_opens_a_self_contained_fullscreen_app_resource(plugin): assert "