diff --git a/integrations/codex/README.md b/integrations/codex/README.md
index 955e741f..f6e7dbda 100644
--- a/integrations/codex/README.md
+++ b/integrations/codex/README.md
@@ -173,10 +173,11 @@ already sent to ReMe; a timeout also does not prove that server-side processing
Click **ReMe status** to open a dedicated panel from MCP settings. Connection checks and memory
status share this one entry. Four tabs organize the panel: **Overview**, **Auto Memory**,
-**Memory Consolidation**, and **Components**. They show service health and the memory flow;
+**Consolidation**, and **Components**. They show service health and the pending queue;
capture settings, queued turns/sessions, recall and recent activity; Dream scheduling and results;
and component memory estimates plus process RSS. Click **Refresh** to check again;
-service details are collapsed in Components until needed. The initial view uses the opening call's result without
+connection details, recent activity, usage notes, and raw service details expand when needed. Narrow cards use a
+single content column and two rows of tabs. The initial view uses the opening call's result without
a second request. The panel follows the host theme and the saved `language` setting.
You can also ask Codex to call `reme_status` in a conversation. Hosts without an App view receive
diff --git a/integrations/codex/README_ZH.md b/integrations/codex/README_ZH.md
index dbaca032..49712240 100644
--- a/integrations/codex/README_ZH.md
+++ b/integrations/codex/README_ZH.md
@@ -164,9 +164,10 @@ ReMe-Example-7319 的评审时间和校验词是什么?
## 6. 查看状态和调整配置
点击 **ReMe status**,从 MCP 设置打开独立状态面板。连接检查和记忆状态共用这一个入口,
-面板分为 **总览、自动记忆、记忆整理、组件** 四个页签,分别展示连接健康与记忆流程,
+面板分为 **总览、自动记忆、记忆整理、组件** 四个页签,分别展示连接健康与待提交队列,
记录开关、批次和队列、召回及最近活动,Dream 计划和结果,以及组件内存估算与进程 RSS。
-点击 **刷新** 可重新检查,组件页的服务详情默认折叠。首次展示直接使用打开时返回的结果,不重复请求;面板适配宿主主题及保存的 `language`。
+点击 **刷新** 可重新检查;连接详情、最近活动、使用说明和原始服务详情按需展开。
+窄卡片使用单列内容和两行页签。首次展示直接使用打开时返回的结果,不重复请求;面板适配宿主主题及保存的 `language`。
也可以在对话中让 Codex 调用 `reme_status`;不展示 App 的宿主仍会收到文字结果。
打开或刷新面板不会记录或整理记忆。服务不可达时仍可查看本地队列与活动。
diff --git a/integrations/codex/plugins/reme/.codex-plugin/plugin.json b/integrations/codex/plugins/reme/.codex-plugin/plugin.json
index 04f45d7e..422952d0 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.1",
+ "version": "0.2.2",
"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/ui/status.html b/integrations/codex/plugins/reme/ui/status.html
index 7b89acd3..eeef06a7 100644
--- a/integrations/codex/plugins/reme/ui/status.html
+++ b/integrations/codex/plugins/reme/ui/status.html
@@ -11,31 +11,27 @@
:root[data-theme="dark"] { color-scheme: dark; --bg: #0b0d12; --surface: #12151d; --panel: #181c26; --ink: #f3f5f7; --muted: #979dab; --line: #292e3b; --blue: #79a8ff; --green: #63d4a3; --green-bg: #25382f; --amber: #f2be5c; --amber-bg: #3c3526; --red: #ff7b86; --red-bg: #40262c; }
:root[data-theme="light"] { color-scheme: light; }
* { box-sizing: border-box; }
- body { margin: 0; padding: 28px; background: var(--color-background-primary, var(--bg)); color: var(--color-text-primary, var(--ink)); font: 14px/1.5 system-ui, -apple-system, sans-serif; }
- main { max-width: 1100px; margin: auto; }
- header { display: flex; align-items: start; justify-content: space-between; gap: 20px; margin-bottom: 22px; }
- .brand { display: flex; gap: 12px; align-items: center; }
- .logo { display: grid; place-items: center; width: 44px; height: 44px; flex: 0 0 44px; border-radius: 13px; background: linear-gradient(145deg, #678ff7, #725de3); color: #fff; font-size: 26px; }
- .eyebrow { color: var(--blue); font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
- h1 { font-size: 25px; letter-spacing: -.5px; margin: 0; font-weight: 650; }
- h2 { font-size: 14px; margin: 0 0 12px; font-weight: 600; }
+ body { margin: 0; padding: 24px; background: var(--color-background-primary, var(--bg)); color: var(--color-text-primary, var(--ink)); font: 14px/1.6 system-ui, -apple-system, sans-serif; }
+ main { max-width: 920px; margin: auto; overflow-wrap: anywhere; }
+ header { display: flex; align-items: start; justify-content: space-between; gap: 20px; margin-bottom: 24px; }
+ header > div { min-width: 0; }
+ h1 { font-size: 22px; letter-spacing: -.4px; margin: 0; font-weight: 650; }
+ h2 { font-size: 15px; margin: 0 0 20px; font-weight: 600; }
p { margin: 6px 0; }
.muted, small { color: var(--muted); }
- .label { font-size: 12px; color: var(--muted); }
+ .label { font-size: 13px; color: var(--muted); }
.endpoint { overflow-wrap: anywhere; font-size: 12px; font-family: ui-monospace, monospace; }
- .actions { display: flex; gap: 10px; align-items: center; }
button { background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: 8px; padding: 8px 14px; font: inherit; cursor: pointer; white-space: nowrap; }
button:hover:not(:disabled) { border-color: var(--blue); }
button:focus-visible, summary:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
button:disabled { opacity: .55; cursor: wait; }
- .tabs { display: flex; gap: 4px; overflow-x: auto; border-bottom: 1px solid var(--line); margin-bottom: 20px; }
- .tabs button { color: var(--muted); background: transparent; border: 0; border-radius: 0; border-bottom: 2px solid transparent; padding: 11px 14px; font-weight: 600; }
- .tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--blue); }
- .grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
- .card { background: linear-gradient(150deg, var(--panel), var(--surface)); border: 1px solid var(--line); border-radius: 12px; padding: 18px; min-width: 0; }
- .wide { grid-column: span 2; }
+ .tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; padding: 5px; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; margin-bottom: 24px; }
+ .tabs button { min-width: 0; color: var(--muted); background: transparent; border-color: transparent; padding: 10px 8px; font-weight: 600; white-space: normal; line-height: 1.4; }
+ .tabs button[aria-selected="true"] { color: var(--ink); background: var(--surface); border-color: var(--line); box-shadow: 0 1px 3px #0000000a; }
+ .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
+ .card { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 24px; min-width: 0; }
.full { grid-column: 1 / -1; }
- .value { font-size: 25px; line-height: 1.3; font-weight: 700; margin-top: 8px; }
+ .value { font-size: 26px; line-height: 1.35; font-weight: 650; margin-top: 8px; }
.compact { font-size: 16px; overflow-wrap: anywhere; }
.pill { display: inline-block; color: var(--green); background: var(--green-bg); border-radius: 99px; padding: 4px 10px; font-size: 12px; font-weight: 600; }
[data-tone="good"] { color: var(--green); }
@@ -43,32 +39,42 @@
.pill[data-tone="neutral"] { color: var(--muted); background: var(--bg); }
.pill[data-tone="warn"] { color: var(--amber); background: var(--amber-bg); }
.pill[data-tone="bad"] { color: var(--red); background: var(--red-bg); }
- .flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
- .step { background: var(--surface); border: 1px solid var(--line); padding: 10px; border-radius: 8px; flex: 1 1 100px; }
- .arrow { color: var(--muted); }
+ .section-heading { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 20px; }
+ .section-heading h2 { margin: 0; }
+ .metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
+ .properties { margin: 0; }
+ .properties > div { display: flex; align-items: baseline; justify-content: space-between; gap: 24px; padding: 14px 0; border-bottom: 1px solid var(--line); }
+ .properties > div:first-child { padding-top: 0; }
+ .properties > div:last-child { padding-bottom: 0; border: 0; }
+ .properties dt { color: var(--muted); }
+ .properties dd { margin: 0; text-align: right; font-weight: 600; min-width: 0; }
.rows { padding: 0; margin: 0; list-style: none; }
- .rows li { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
+ .rows li { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; padding: 16px 0; border-bottom: 1px solid var(--line); }
.rows li:last-child { border: 0; padding-bottom: 0; }
.rows code { display: block; color: var(--muted); font-size: 11px; }
- time { color: var(--muted); font-size: 12px; white-space: nowrap; }
- .component-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
- .component { border: 1px solid var(--line); background: var(--surface); padding: 14px; border-radius: 9px; min-width: 0; }
+ time { color: var(--muted); font-size: 12px; flex-shrink: 0; max-width: 45%; }
+ .component-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
+ .component { border: 1px solid var(--line); background: var(--panel); padding: 18px; border-radius: 9px; min-width: 0; }
.component strong { display: block; overflow-wrap: anywhere; font-size: 13px; }
- details { margin-top: 18px; }
+ details { margin-top: 20px; }
+ details.card { margin-top: 0; }
+ details[open] > summary { margin-bottom: 16px; }
+ .card > summary { color: var(--ink); font-weight: 600; font-size: 15px; }
summary { cursor: pointer; color: var(--muted); }
pre { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface); padding: 14px; border: 1px solid var(--line); border-radius: 8px; font-size: 12px; max-height: 280px; overflow: auto; }
- .note { margin: 18px 0 0; font-size: 12px; color: var(--muted); }
+ .note { margin: 20px 0 0; font-size: 13px; color: var(--muted); }
#error { border: 1px solid var(--red); color: var(--red); padding: 12px; border-radius: 8px; margin-bottom: 16px; }
[hidden] { display: none !important; }
- @media (max-width: 850px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
- @media (max-width: 540px) { body { padding: 18px; } .grid, .component-grid { grid-template-columns: 1fr; } .wide { grid-column: 1 / -1; } .card { padding: 16px; } header { flex-wrap: wrap; gap: 12px; } h1 { font-size: 23px; } .rows li { flex-direction: column; gap: 2px; } }
+ @media (max-width: 700px) { .tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
+ @media (max-width: 640px) { body { padding: 16px; } .grid, .component-grid { grid-template-columns: 1fr; } .card { padding: 20px; } header { gap: 12px; } .rows li { flex-direction: column; gap: 6px; } time { max-width: 100%; } }
+ @media (max-width: 380px) { .metrics { grid-template-columns: 1fr; gap: 16px; } .metrics > div { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; } .metrics .value { margin: 0; } }
- ◈
Codex memory
ReMe status
Waiting for status…
-
+ ReMe status
Waiting for status…
+
Opening the connection and memory overview…
@@ -76,49 +82,62 @@
-
ReMe service
-
Runtime phase
-
Queued turns
-
Queued sessions
-
Memory automation
1 · Prompt
Automatic recall →→3 · Workspace
Memory files
-
Connection
+
+ ReMe service
+
+ Connection details
+
+
-
Automatic memory
-
Batch interval
-
Queued turns
-
Queued sessions
-
Automatic recall
-
Recent activity
+
+ Automatic memory
+
+ - Batch interval
+ - Queued turns
+ - Queued sessions
+
+
+
Automatic recall
+
Recent activity
-
Auto Dream
-
Schedule
-
Timezone
-
Last result
-
Next run
The daily schedule runs while the ReMe MCP connection is alive.
-
Last consolidation
+
+ Auto Dream
+
+ - Schedule
+ - Timezone
+ - Last result
+
+
+
Next run
The daily schedule runs while the ReMe MCP connection is alive.
+
Last consolidation
-
Process memory (RSS)
-
Estimated component memory
+
Process memory (RSS)
+
Estimated component memory
Components
Memory estimates from ReMe status. Component-level health is not included in the MCP response.
- Service details
+ Service details
- Connection health does not establish Hook trust. Review ReMe in Codex Hooks settings.
- This view uses saved settings. Save changes in MCP settings, then refresh here.
+ About this view
+ Connection health does not establish Hook trust. Review ReMe in Codex Hooks settings.
+ This view uses saved settings. Save changes in MCP settings, then refresh here.
+
diff --git a/integrations/codex/status-app/src/status.js b/integrations/codex/status-app/src/status.js
index 5f73d214..64bfe408 100644
--- a/integrations/codex/status-app/src/status.js
+++ b/integrations/codex/status-app/src/status.js
@@ -10,10 +10,10 @@ let hasSnapshot = false;
let refreshing = false;
const words = {
en: {
- overview: "Overview", autoMemory: "Auto Memory", consolidation: "Memory Consolidation", components: "Components",
- service: "ReMe service", runtime: "Runtime phase", queuedTurns: "Queued turns", queuedSessions: "Queued sessions",
- automation: "Memory automation", prompt: "1 · Prompt", response: "2 · Response", workspace: "3 · Workspace",
- capture: "Turn capture", files: "Memory files", interval: "Batch interval", timezone: "Timezone", lastResult: "Last result",
+ overview: "Overview", autoMemory: "Auto Memory", consolidation: "Consolidation", components: "Components",
+ service: "ReMe service", queuedTurns: "Queued turns", queuedSessions: "Queued sessions",
+ connectionDetails: "Connection details", viewHelp: "About this view",
+ interval: "Batch interval", timezone: "Timezone", lastResult: "Last result",
healthy: "Healthy", unhealthy: "Unhealthy", unknown: "Unknown", processMemory: "Process memory (RSS)",
componentMemory: "Estimated component memory", noComponents: "No component memory details returned.",
componentHelp: "Memory estimates from ReMe status. Component-level health is not included in the MCP response.",
@@ -41,9 +41,9 @@ const words = {
},
zh: {
overview: "总览", autoMemory: "自动记忆", consolidation: "记忆整理", components: "组件",
- service: "ReMe 服务", runtime: "运行状态", queuedTurns: "待提交轮数", queuedSessions: "待提交会话",
- automation: "记忆流程", prompt: "1 · 提问", response: "2 · 回复", workspace: "3 · 工作区",
- capture: "记录完成轮次", files: "记忆文件", interval: "批次轮数", timezone: "时区", lastResult: "最近结果",
+ service: "ReMe 服务", queuedTurns: "待提交轮数", queuedSessions: "待提交会话",
+ connectionDetails: "连接详情", viewHelp: "使用说明",
+ interval: "批次轮数", timezone: "时区", lastResult: "最近结果",
healthy: "健康", unhealthy: "异常", unknown: "未知", processMemory: "进程内存(RSS)",
componentMemory: "组件内存估算", noComponents: "暂无组件内存信息。",
componentHelp: "内存为 ReMe status 返回的估算值;MCP 响应不包含组件级健康信息。",
@@ -107,7 +107,6 @@ function render(result) {
const summary = typeof health.answer === "string" ? health.answer.trim().match(/(?:^|-\s*)(unhealthy|healthy)$/) : null;
text("service-health", t(!health.reachable ? "offline" : summary?.[1] ?? "unknown"));
$("service-health").dataset.tone = !health.reachable || summary?.[1] === "unhealthy" ? "bad" : summary ? "good" : "neutral";
- text("runtime", t(data.auto_dream.phase));
text("overview-queue", data.auto_memory.queued_turns);
text("overview-sessions", data.auto_memory.queued_sessions);
text("health", health.reachable ? (typeof health.answer === "string" ? health.answer : JSON.stringify(health.answer)) : health.error);
@@ -212,7 +211,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.1" },
+ protocolVersion: "2026-01-26", appInfo: { name: "reme-status", version: "0.2.2" },
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 ff027894..a172f0df 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.1",
+ "plugin_version": "0.2.2",
"mcpUrl": "http://localhost:2333/mcp",
"service": {
"health_check": {"reachable": True, "answer": "ReMe v0.4.1.13 - healthy"},
@@ -105,6 +105,9 @@ def test_initial_result_refresh_offline_failure_theme_and_untrusted_text(languag
assert "healthy" in frame.locator("#health").inner_text()
assert "23:00" in frame.locator("#next").inner_text()
assert frame.locator("#activity li").count() == 2
+ assert not frame.locator("#connection-details").get_attribute("open")
+ assert not frame.locator("#activity-details").get_attribute("open")
+ assert not frame.locator("#view-help").get_attribute("open")
assert not page.evaluate(
"window.calls.some(m => m.method === 'tools/call')",
) # Opening reuses the supplied result.
@@ -119,9 +122,18 @@ def test_initial_result_refresh_offline_failure_theme_and_untrusted_text(languag
assert frame.locator("#component-total").inner_text() == "1.25 MiB"
frame.locator("#tab-components").press("Home")
assert frame.locator("#panel-overview").is_visible()
- page.set_viewport_size({"width": 420, "height": 1250})
- page.evaluate("document.querySelector('iframe').style.width = '100%'")
- assert frame.locator("body").evaluate("node => node.scrollWidth <= window.innerWidth")
+ for width in (320, 480, 640, 940):
+ page.set_viewport_size({"width": width + 16, "height": 1250})
+ page.evaluate("width => document.querySelector('iframe').style.width = `${width}px`", width)
+ for name in ("overview", "memory", "dream", "components"):
+ frame.locator(f"#tab-{name}").click()
+ assert frame.locator("body").evaluate("node => node.scrollWidth <= window.innerWidth")
+ assert frame.locator(f"#tab-{name}").evaluate(
+ "node => node.scrollWidth <= node.clientWidth && node.scrollHeight <= node.clientHeight",
+ )
+ frame.locator("#tab-overview").click()
+ if width == 480:
+ page.screenshot(path=str(tmp_path / f"status-test-host-{theme}-narrow.png"), full_page=True)
offline = snapshot(language)
offline["service"]["health_check"] = {"reachable": False, "error": "ConnectionError"}
@@ -131,6 +143,7 @@ def test_initial_result_refresh_offline_failure_theme_and_untrusted_text(languag
page.evaluate("value => window.result = {structuredContent:value}", offline)
frame.locator("#refresh").click()
playwright.expect(frame.locator("#connection-state")).to_have_attribute("data-tone", "bad")
+ frame.locator("#connection-details summary").click()
assert frame.locator("#endpoint").inner_text() == offline["mcpUrl"]
assert frame.locator("#endpoint img").count() == 0
assert "3" in frame.locator("#queue").inner_text()
@@ -139,17 +152,20 @@ def test_initial_result_refresh_offline_failure_theme_and_untrusted_text(languag
assert frame.locator("#error").is_hidden() # Offline is a useful status result, not a broken panel.
frame.locator("#tab-memory").click()
+ frame.locator("#activity-details summary").click()
+ assert frame.locator("#activity li").first.is_visible()
page.evaluate("window.failRefresh = true")
frame.locator("#refresh").click()
frame.locator("#error").wait_for(state="visible")
assert "3" in frame.locator("#queue").inner_text() # Failed refresh keeps the last snapshot.
assert frame.locator("#refresh").is_enabled()
assert frame.locator("#panel-memory").is_visible() # Refresh preserves navigation.
+ assert frame.locator("#activity li").first.is_visible() # Refresh preserves expanded details.
frame.locator("#tab-components").click()
assert frame.locator("#rss").inner_text() == "—"
assert frame.locator("#components .component").count() == 0
- assert not frame.locator("details").get_attribute("open")
- frame.locator("summary").click()
+ assert not frame.locator("#service-details").get_attribute("open")
+ frame.locator("#service-details summary").click()
assert "ConnectionError" in frame.locator("#details").inner_text()
unhealthy = snapshot(language)
unhealthy["service"]["health_check"]["answer"] = "ReMe v0.4.1.13 - unhealthy"