Skip to content

feat(memory): 记忆浏览页面添加事实、反思和事实归档的预览查看 - #2565

Open
for-the-zero wants to merge 9 commits into
Project-N-E-K-O:mainfrom
for-the-zero:feat/browse-more-memory-data
Open

feat(memory): 记忆浏览页面添加事实、反思和事实归档的预览查看#2565
for-the-zero wants to merge 9 commits into
Project-N-E-K-O:mainfrom
for-the-zero:feat/browse-more-memory-data

Conversation

@for-the-zero

@for-the-zero for-the-zero commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

改动概述 / Summary

给记忆浏览页面加了标签页,可以以只读方式查看facts.json facts_archive.json reflections.json中的内容
screenshot_1785335215133

测试 / Testing

  • test_recent_chat_payload_caps.py
  • test_external_import_eta.py
  • test_character_memory_regression.py
    手动验证和比对文件没问题

Summary by CodeRabbit

  • 新功能
    • 记忆浏览器新增“事实 / 事实归档 / 反思”组件标签页,可按类型加载并展示对应内容。
    • 组件视图支持更完善的空态与列表呈现。
  • 界面与本地化
    • 新增组件标签页样式(激活/悬停/焦点、可展开分区、状态徽标)并优化文本换行。
    • 同步更新多语言文案与无障碍提示;调整长期记忆为“可预览”说明。
  • 可靠性与安全
    • 强化组件加载校验与目录穿越防护;解析失败将回退为空态并展示错误信息。
    • 保存与切换流程更稳定,避免并发导致的内容错乱。

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 01cd0910-e270-4204-8a9c-fcc22f1eb04d

📥 Commits

Reviewing files that changed from the base of the PR and between 376667f and ff680d8.

📒 Files selected for processing (3)
  • static/js/memory_browser.js
  • static/locales/es.json
  • static/locales/pt.json
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)
🚧 Files skipped from review as they are similar to previous changes (2)
  • static/locales/pt.json
  • static/locales/es.json

Walkthrough

新增记忆组件读取 API,并在记忆浏览器中加入事实、事实归档和反思标签页,配套实现数据渲染、保存串行化、竞态控制、样式及多语言文案更新,喵。

Changes

记忆组件浏览

Layer / File(s) Summary
组件文件读取接口
main_routers/memory_router.py
新增组件类型映射及读取接口,支持 JSON、NDJSON、路径越界校验、未知类型和缺失文件响应,喵。
标签页状态与组件渲染
static/js/memory_browser.js
新增保存请求串行化、组件加载缓存、事实与反思渲染、请求竞态控制、标签切换及最近记忆回退逻辑,喵。
标签页界面与本地化文案
templates/memory_browser.html, static/css/memory_browser.css, static/locales/*.json
新增记忆组件标签、组件视图样式、可展开分区、状态徽标及多语言标签和提示文案,喵。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Project-N-E-K-O/N.E.K.O#2465:同样扩展记忆浏览器的前端文件,本次组件标签页接入涉及相同的界面范围,喵。

Suggested labels: enhancement

Poem

标签页展开记忆光,喵
事实反思排成行,喵
JSON 轻轻流入窗,喵
深色徽标也闪亮,喵
猫娘浏览新篇章,喵

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning 已包含改动概述和测试,但改动触及 memory/*.py 却缺少必填的回归报告。 补充“回归报告”一节,逐项写明改动内容、必要性、前后差异和潜在回归点/验证方式。
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 标题与改动一致,准确概括了记忆浏览页新增事实、反思和事实归档预览标签页。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 30, 2026
Comment thread static/js/memory_browser.js Outdated
Comment thread static/js/memory_browser.js Outdated
@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

本次改动为记忆浏览器增加长期记忆组件的只读预览,并修复此前反馈的异步切换、并发保存与本地化问题喵。

  • 新增事实、事实归档和反思标签页及对应卡片、空态、状态徽标样式喵。
  • 新增按角色和组件类型读取记忆文件的 API,并限制可访问的文件类型与目录范围喵。
  • 补齐各支持语言的组件标签、元数据和状态文案喵。
  • 使用独立请求序号协调近期记忆与组件加载,并串行执行记忆保存喵。

Confidence Score: 5/5

当前改动看起来可以安全合并喵。

此前报告的跨标签陈旧响应、近期记忆响应覆盖、快速标签选择覆盖、并发保存覆盖及组件本地化缺失均已在当前代码中得到处理,未发现仍然存在的阻塞性故障喵。

Important Files Changed

Filename Overview
static/js/memory_browser.js 新增组件标签页加载与渲染逻辑,并通过跨视图请求失效和保存队列修复此前报告的竞态喵。
main_routers/memory_router.py 新增只读组件文件接口,并对白名单类型、解析错误和目录越界进行处理喵。
templates/memory_browser.html 在现有编辑器头部加入具备 ARIA 状态的记忆组件标签页喵。
static/css/memory_browser.css 为组件标签、列表、折叠区和明暗主题状态徽标增加样式喵。
static/locales/en.json 补充组件视图所需英文文案并更新长期记忆提示喵。
static/locales/es.json 补充组件视图所需西班牙语文案并更新长期记忆提示喵。
static/locales/ja.json 补充组件视图所需日语文案并更新长期记忆提示喵。
static/locales/ko.json 补充组件视图所需韩语文案并更新长期记忆提示喵。
static/locales/pt.json 补充组件视图所需葡萄牙语文案并更新长期记忆提示喵。
static/locales/ru.json 补充组件视图所需俄语文案并更新长期记忆提示喵。
static/locales/zh-CN.json 补充组件视图所需简体中文文案并更新长期记忆提示喵。
static/locales/zh-TW.json 补充组件视图所需繁体中文文案并更新长期记忆提示喵。

Sequence Diagram

sequenceDiagram
  participant U as 用户
  participant B as 记忆浏览器
  participant A as 记忆组件 API
  participant F as 角色记忆文件
  U->>B: 选择事实/归档/反思标签
  B->>B: 使旧请求失效
  B->>A: "GET /api/memory/component/{name}"
  A->>A: 校验组件类型与目录边界
  A->>F: 读取白名单文件
  F-->>A: JSON 数据
  A-->>B: 组件数据
  B->>B: 校验请求序号并渲染卡片
Loading

Reviews (9): Last reviewed commit: "fix(i18n): revert locale clear typo fix ..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ce9d38bd3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return;
}

_loadComponentTab(container, currentCatName, tab);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve unsaved edits when switching tabs

When the recent-chat editor has unsaved textarea edits, clicking Facts/Archive/Reflections reaches this load path without the existing unsaved-change dialog used for role switches. The component load replaces the editor and hides the save controls; returning to Recent then reloads from disk and clears the dirty state, so those edits are silently lost. Gate component tab switches on memoryHasUnsavedChanges or reuse the same save/discard dialog before replacing the editor.

Useful? React with 👍 / 👎.

Comment on lines +4044 to +4047
.then(function (result) {
_componentDataCache[type] = result.data;
if (type === 'facts' || type === 'facts_archive') _renderMemoryFacts(container, result.data);
else if (type === 'reflections') _renderMemoryReflections(container, result.data);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore stale component loads after selection changes

If a component request is in flight and the user quickly selects another role or switches back to Recent, this handler still renders the response for the old name/type into the shared editor. On a slow read this can show one character's facts under another selected role or overwrite the chat view after selectMemoryFile has already rendered it; add a request id or verify _activeMemoryTab and currentCatName before rendering, like the recent-file loader does.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
static/css/memory_browser.css (1)

3819-3824: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

word-break: break-word 已经过时啦,换成 overflow-wrap 喵!

Stylelint 报告这个属性值已废弃,规范推荐用 overflow-wrap: break-word(或 anywhere)替代。

🎀 建议修复
 .memory-component-text {
     font-size: var(--memory-font-body, 14px);
     line-height: 1.55;
     color: var(--memory-text);
-    word-break: break-word;
+    overflow-wrap: break-word;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/css/memory_browser.css` around lines 3819 - 3824, Update the
.memory-component-text rule by replacing the deprecated word-break: break-word
declaration with overflow-wrap: break-word, while preserving the existing
typography and color properties.

Source: Linters/SAST tools

templates/memory_browser.html (1)

284-297: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

tablist的aria-label忘记走i18n了喵!

role="tablist" 上的 aria-label="记忆组件" 是硬编码中文,四个tab按钮本身都乖乖用了 data-i18n,就这个容器漏网了。非中文语言下用屏幕阅读器的用户永远只会听到中文"记忆组件",笨蛋才会漏掉这种细节喵!

🌐 建议修复
-                                <div class="memory-tab-bar" role="tablist" aria-label="记忆组件">
+                                <div class="memory-tab-bar" role="tablist" data-i18n-aria="memory.componentTabsAria" aria-label="记忆组件">

需要在各 locale 文件里补一个类似 memory.componentTabsAria 的key喵。

Based on learnings, the repo's i18n aria-label convention is to use data-i18n-aria="<key>" to drive aria-label, with a hardcoded fallback kept in place.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/memory_browser.html` around lines 284 - 297, Update the memory
tablist in the editor header to use the established data-i18n-aria convention
with a new memory.componentTabsAria key while retaining the existing Chinese
aria-label as fallback, and add the corresponding translation key to every
locale file.

Source: Learnings

static/js/memory_browser.js (1)

3976-4038: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

两个渲染函数长得跟双胞胎一样,合并一下喵~

_renderMemoryFacts_renderMemoryReflections 除了meta字段(source/entity vs status/entity)几乎完全一样,排序、空态、卡片DOM构建全是重复代码,笨蛋才会复制粘贴这么长一段喵!抽一个通用函数出来更省心。

♻️ 建议重构
-    function _renderMemoryFacts(container, data) {
-        container.innerHTML = '';
-        if (!Array.isArray(data) || !data.length) {
-            container.innerHTML = '<div class="memory-component-empty">无数据</div>';
-            return;
-        }
-        var sorted = data.slice().sort(function (a, b) {
-            return (b.created_at || '').localeCompare(a.created_at || '');
-        });
-        var list = document.createElement('div');
-        list.className = 'memory-component-list';
-        sorted.forEach(function (fact) {
-            var card = document.createElement('div');
-            card.className = 'memory-component-item';
-            var text = document.createElement('div');
-            text.className = 'memory-component-text';
-            text.textContent = fact.text || '(空)';
-            card.appendChild(text);
-            var meta = document.createElement('div');
-            meta.className = 'memory-component-meta';
-            var parts = [];
-            if (fact.created_at) parts.push(fact.created_at.replace('T', ' ').slice(0, 19));
-            if (fact.importance != null) parts.push('重要性: ' + fact.importance);
-            if (fact.source) parts.push('来源: ' + fact.source);
-            if (fact.entity) parts.push('实体: ' + fact.entity);
-            meta.textContent = parts.join(' | ');
-            card.appendChild(meta);
-            list.appendChild(card);
-        });
-        container.appendChild(list);
-    }
-
-    function _renderMemoryReflections(container, data) {
-        container.innerHTML = '';
-        if (!Array.isArray(data) || !data.length) {
-            container.innerHTML = '<div class="memory-component-empty">无数据</div>';
-            return;
-        }
-        var sorted = data.slice().sort(function (a, b) {
-            return (b.created_at || '').localeCompare(a.created_at || '');
-        });
-        var list = document.createElement('div');
-        list.className = 'memory-component-list';
-        sorted.forEach(function (ref) {
-            var card = document.createElement('div');
-            card.className = 'memory-component-item';
-            var text = document.createElement('div');
-            text.className = 'memory-component-text';
-            text.textContent = ref.text || '(空)';
-            card.appendChild(text);
-            var meta = document.createElement('div');
-            meta.className = 'memory-component-meta';
-            var parts = [];
-            if (ref.created_at) parts.push(ref.created_at.replace('T', ' ').slice(0, 19));
-            if (ref.importance != null) parts.push('重要性: ' + ref.importance);
-            if (ref.status) parts.push('状态: ' + ref.status);
-            if (ref.entity) parts.push('实体: ' + ref.entity);
-            meta.textContent = parts.join(' | ');
-            card.appendChild(meta);
-            list.appendChild(card);
-        });
-        container.appendChild(list);
-    }
+    function _renderMemoryCards(container, data, metaBuilder) {
+        container.innerHTML = '';
+        if (!Array.isArray(data) || !data.length) {
+            container.innerHTML = '<div class="memory-component-empty">无数据</div>';
+            return;
+        }
+        var sorted = data.slice().sort(function (a, b) {
+            return (b.created_at || '').localeCompare(a.created_at || '');
+        });
+        var list = document.createElement('div');
+        list.className = 'memory-component-list';
+        sorted.forEach(function (item) {
+            var card = document.createElement('div');
+            card.className = 'memory-component-item';
+            var text = document.createElement('div');
+            text.className = 'memory-component-text';
+            text.textContent = item.text || '(空)';
+            card.appendChild(text);
+            var meta = document.createElement('div');
+            meta.className = 'memory-component-meta';
+            meta.textContent = metaBuilder(item).join(' | ');
+            card.appendChild(meta);
+            list.appendChild(card);
+        });
+        container.appendChild(list);
+    }
+
+    function _renderMemoryFacts(container, data) {
+        _renderMemoryCards(container, data, function (fact) {
+            var parts = [];
+            if (fact.created_at) parts.push(fact.created_at.replace('T', ' ').slice(0, 19));
+            if (fact.importance != null) parts.push('重要性: ' + fact.importance);
+            if (fact.source) parts.push('来源: ' + fact.source);
+            if (fact.entity) parts.push('实体: ' + fact.entity);
+            return parts;
+        });
+    }
+
+    function _renderMemoryReflections(container, data) {
+        _renderMemoryCards(container, data, function (ref) {
+            var parts = [];
+            if (ref.created_at) parts.push(ref.created_at.replace('T', ' ').slice(0, 19));
+            if (ref.importance != null) parts.push('重要性: ' + ref.importance);
+            if (ref.status) parts.push('状态: ' + ref.status);
+            if (ref.entity) parts.push('实体: ' + ref.entity);
+            return parts;
+        });
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/memory_browser.js` around lines 3976 - 4038, 合并
`_renderMemoryFacts` 和 `_renderMemoryReflections` 中重复的排序、空态及卡片 DOM
构建逻辑,提取一个通用渲染函数;通过参数或回调分别配置 facts 的 `source` 元数据和 reflections 的 `status`
元数据,同时保留两者现有的 `entity`、文本、排序及空态行为,并让两个入口函数委托该通用实现。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@main_routers/memory_router.py`:
- Around line 1239-1243: 在读取文件的解析逻辑中为 ndjson 逐行解析和普通 JSON 整体解析增加
try/except,覆盖空文件、截断行及其他 JSONDecodeError;捕获后按同文件其他接口的既有风格返回明确的
JSONResponse“加载失败”错误,而不是让异常未处理导致 500。保留成功解析时现有的 {"data": ...} 返回结构。
- Around line 1225-1238: Update get_memory_component to validate name before
constructing the path, rejecting path separators and traversal components, and
perform a resolved-path boundary check against the resolved memory_dir before
reading the fixed component filename. Preserve the existing invalid-type and
missing-file responses while ensuring no requested path can escape memory_dir.

In `@static/js/memory_browser.js`:
- Around line 4040-4052: Update _loadComponentTab to validate the fetch response
before parsing and rendering it: reject or otherwise route any response where
r.ok is false to the existing failure handler, so HTTP errors display “加载失败”
instead of passing undefined result.data to the renderers. Preserve the current
successful response handling and caching behavior.
- Around line 4066-4074: Update the recent-tab branch of switchMemoryTab and/or
_selectMemoryFileInternal so the current memory file’s corresponding list item
is resolved via findMemoryRoleListItem (using current_filename/current_catname)
before calling selectMemoryFile. Preserve the existing selection while switching
back to “recent” so the sidebar li remains highlighted instead of being cleared.

In `@static/locales/es.json`:
- Around line 1827-1834: Update the Spanish locale’s clear translation in the
chat history strings from “Claro” to an action meaning “clear,” such as the
established Spanish equivalent “Borrar” or “Limpiar,” while leaving the
surrounding translations unchanged.

In `@static/locales/pt.json`:
- Around line 1827-1834: Update the Portuguese translation value for the "clear"
key in the locale entries to "Limpar" instead of "Claro", preserving the
existing command-style wording used by "save".

---

Nitpick comments:
In `@static/css/memory_browser.css`:
- Around line 3819-3824: Update the .memory-component-text rule by replacing the
deprecated word-break: break-word declaration with overflow-wrap: break-word,
while preserving the existing typography and color properties.

In `@static/js/memory_browser.js`:
- Around line 3976-4038: 合并 `_renderMemoryFacts` 和 `_renderMemoryReflections`
中重复的排序、空态及卡片 DOM 构建逻辑,提取一个通用渲染函数;通过参数或回调分别配置 facts 的 `source` 元数据和 reflections 的
`status` 元数据,同时保留两者现有的 `entity`、文本、排序及空态行为,并让两个入口函数委托该通用实现。

In `@templates/memory_browser.html`:
- Around line 284-297: Update the memory tablist in the editor header to use the
established data-i18n-aria convention with a new memory.componentTabsAria key
while retaining the existing Chinese aria-label as fallback, and add the
corresponding translation key to every locale file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 444c9c75-e15e-46c2-a9e2-e79ca87cae0a

📥 Commits

Reviewing files that changed from the base of the PR and between 366689a and 3ce9d38.

📒 Files selected for processing (12)
  • main_routers/memory_router.py
  • static/css/memory_browser.css
  • static/js/memory_browser.js
  • static/locales/en.json
  • static/locales/es.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/pt.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
  • templates/memory_browser.html
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)

Comment thread main_routers/memory_router.py
Comment thread main_routers/memory_router.py Outdated
Comment thread static/js/memory_browser.js
Comment thread static/js/memory_browser.js
Comment thread static/locales/es.json
Comment thread static/locales/pt.json
- 后端: 添加路径遍历防护与JSON解析异常处理
- 前端: 添加异步响应过时防护、硬编码字符串本地化、标签页切换前保存未修改内容、API错误日志记录、侧栏高亮恢复
- 区域: ja/ko 反思选项卡翻译修正
Comment thread static/js/memory_browser.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
static/js/memory_browser.js (1)

4063-4098: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

切换标签页前取消旧请求,并等待保存成功喵!

saveCurrentMemory() 是异步函数,但这里既未等待也未检查结果;保存失败后仍会进入只读标签页喵。另一个问题是从组件标签切回 recent 时没有递增 _componentRequestId,未完成的组件请求会在第 4050 行仍被视为最新响应,并覆盖刚恢复的聊天记录视图喵。

建议修复喵
-    function switchMemoryTab(tab) {
+    async function switchMemoryTab(tab) {
         if (tab === _activeMemoryTab) return;
 
+        _componentRequestId++;
+
         // Preserve unsaved edits when switching away from recent tab
         if (_activeMemoryTab === 'recent' && memoryHasUnsavedChanges) {
-            saveCurrentMemory();
+            var saved = await saveCurrentMemory();
+            if (!saved) return;
         }

Based on learnings,已将该 JS 文件中紧邻的标签切换问题合并在此评论中喵。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/memory_browser.js` around lines 4063 - 4098, Update switchMemoryTab
so it invalidates any in-flight component request when leaving a component tab
by incrementing _componentRequestId before restoring the recent view. Await
saveCurrentMemory() and only complete the tab switch after it succeeds; on save
failure, keep the current tab and do not proceed to the requested read-only tab.
Preserve the existing tab rendering and memory restoration behavior.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@static/js/memory_browser.js`:
- Around line 4063-4098: Update switchMemoryTab so it invalidates any in-flight
component request when leaving a component tab by incrementing
_componentRequestId before restoring the recent view. Await saveCurrentMemory()
and only complete the tab switch after it succeeds; on save failure, keep the
current tab and do not proceed to the requested read-only tab. Preserve the
existing tab rendering and memory restoration behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d0c4206f-769e-4000-9bd5-6965e8ed5d6f

📥 Commits

Reviewing files that changed from the base of the PR and between 3ce9d38 and ee3a295.

📒 Files selected for processing (4)
  • main_routers/memory_router.py
  • static/js/memory_browser.js
  • static/locales/ja.json
  • static/locales/ko.json
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)
🚧 Files skipped from review as they are similar to previous changes (2)
  • static/locales/ko.json
  • static/locales/ja.json

- CSS: word-break: break-word → overflow-wrap: break-word (已废弃属性)
- HTML: tablist aria-label 添加 data-i18n-aria 本地化支持
- JS: 合并 _renderMemoryFacts/_renderMemoryReflections 为 _renderMemoryCards
- JS: switchMemoryTab 改为 async、await saveCurrentMemory()、切换标签时递增 _componentRequestId 防止过期响应覆盖
- i18n: es.json clear: Claro→Borrar, pt.json clear: Claro→Limpar
- i18n: 新增 memory.componentTabsAria 覆盖全部8个区域
Comment thread static/js/memory_browser.js
快速连续点击两个组件标签时,较早的 switchMemoryTab 在 await saveCurrentMemory()
恢复后会覆盖较新的 _activeMemoryTab。现在在 await 后校验 _componentRequestId
是否被其它调用递增过,是则放弃本次切换。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/js/memory_browser.js`:
- Around line 4048-4056: Update switchMemoryTab so clicking the currently active
tab while another tab switch is awaiting saveCurrentMemory still cancels or
supersedes the pending switch. Adjust the early-return logic and request
tracking around _componentRequestId and the pending target, preserving normal
no-op behavior when no switch is in progress.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e6e1c55f-3170-4695-a6b0-0d52fb9e74d4

📥 Commits

Reviewing files that changed from the base of the PR and between 1f016a0 and 7687af2.

📒 Files selected for processing (1)
  • static/js/memory_browser.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)

Comment thread static/js/memory_browser.js
Comment thread static/js/memory_browser.js
@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

- 将 ++_componentRequestId 提前到 switchMemoryTab 的 early return 之前,
  防止 await save 期间点击当前标签时不递增序号导致旧请求继续执行
- 在所有 8 个 locale 文件中新增 memory.componentEmpty/empty/importance/
  source/entity/status 共 6 个翻译键
Comment thread static/js/memory_browser.js
pending recent-memory 请求使用独立的 memoryFileRequestId 守卫,
切换到组件标签时只 bump _componentRequestId 不够,旧响应仍会通过
自己的 guard 并覆盖组件视图。现在离开 recent 标签时同步递增
memoryFileRequestId 使其失效喵。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/js/memory_browser.js`:
- Around line 4049-4050: Update the click handling around _componentRequestId
and _activeMemoryTab so repeated clicks on the currently active facts,
facts_archive, or reflections tab return without incrementing
_componentRequestId while _loadComponentTab is still pending. Only advance the
request token when an actual tab switch or save operation is being cancelled,
preserving the in-progress load and its response validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1efac8be-af0b-4cd0-aa02-3bc784bae4ef

📥 Commits

Reviewing files that changed from the base of the PR and between 7687af2 and d25ac98.

📒 Files selected for processing (9)
  • static/js/memory_browser.js
  • static/locales/en.json
  • static/locales/es.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/pt.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)
🚧 Files skipped from review as they are similar to previous changes (8)
  • static/locales/pt.json
  • static/locales/zh-TW.json
  • static/locales/es.json
  • static/locales/ru.json
  • static/locales/ja.json
  • static/locales/zh-CN.json
  • static/locales/ko.json
  • static/locales/en.json

Comment thread static/js/memory_browser.js Outdated
@for-the-zero

Copy link
Copy Markdown
Contributor Author

有完没完 😡

- 将 ++_componentRequestId 移至 same-tab 早退检查之后
- 仅在需要取消挂起保存时(同标签+最近+未保存)才递增序号
- 组件标签加载中重复点击直接返回,不会中断进行中的请求

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
static/js/memory_browser.js (2)

4107-4122: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

先尊重记忆导入冻结,再修改标签状态喵

原始 selectMemoryFile 在 Line 3276 会拦截导入期间的普通文件切换喵,但这个包装器在调用原始函数前已经修改 _activeMemoryTab、按钮状态并递增 _componentRequestId 喵。于是用户在事实或反思页触发一次被禁止的文件选择时,文件切换虽被拦截,界面却会错误回到“聊天记录”并取消当前组件请求喵。

请把相同的导入守卫放在所有 UI 和令牌修改之前,或先委托原始函数确认允许切换后再同步标签状态喵。

🐾 建议修复
 selectMemoryFile = async function (filename, li, catName, options) {
+    var allowDuringImport = !!(options && options.allowDuringImport);
+    if (window._memoryImportInProgress && !allowDuringImport) {
+        return _selectMemoryFileInternal(filename, li, catName, options);
+    }
     if (_activeMemoryTab !== 'recent') {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/memory_browser.js` around lines 4107 - 4122, Update the
selectMemoryFile wrapper to honor the original import-freeze guard before
changing _activeMemoryTab, tab button state, save-row visibility, or
incrementing _componentRequestId. Delegate to _selectMemoryFileOrig first or
replicate its guard so blocked selections leave the current tab and request
state unchanged, while allowed selections retain the existing recent-tab
synchronization.

4058-4061: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

不要把 _componentRequestId 当成保存取消的钥匙喵

saveCurrentMemory() 直接发了整份 chatData/api/memory/recent_file/save,只在返回后才用 _componentRequestId 决定这次标签切换要不要继续;后端也只用 chat 数据无版本/ETag 保护就直接 atomic_write_json_async,所以旧的保存响应晚到仍会覆盖新快照,造成刚编辑的内容丢失喵。

请按文件串行化保存,或使用前端文件请求 ID + 后端版本号/ETag 拒绝过期快照喵。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/memory_browser.js` around lines 4058 - 4061, 修复 saveCurrentMemory 与
/api/memory/recent_file/save 的并发保存竞态,不能仅依赖返回后的 _componentRequestId 判断是否继续。为
recent_file 保存按文件串行化请求,或引入前端请求 ID 配合后端版本号/ETag 校验,在写入前拒绝过期快照,确保旧响应不会覆盖更新后的
chatData。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@static/js/memory_browser.js`:
- Around line 4107-4122: Update the selectMemoryFile wrapper to honor the
original import-freeze guard before changing _activeMemoryTab, tab button state,
save-row visibility, or incrementing _componentRequestId. Delegate to
_selectMemoryFileOrig first or replicate its guard so blocked selections leave
the current tab and request state unchanged, while allowed selections retain the
existing recent-tab synchronization.
- Around line 4058-4061: 修复 saveCurrentMemory 与 /api/memory/recent_file/save
的并发保存竞态,不能仅依赖返回后的 _componentRequestId 判断是否继续。为 recent_file 保存按文件串行化请求,或引入前端请求 ID
配合后端版本号/ETag 校验,在写入前拒绝过期快照,确保旧响应不会覆盖更新后的 chatData。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9002e0bf-64b8-4270-aa90-818bbd4030dd

📥 Commits

Reviewing files that changed from the base of the PR and between 915907a and 19e1d7e.

📒 Files selected for processing (1)
  • static/js/memory_browser.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)

1. selectMemoryFile包装器先检查导入冻结再修改标签状态
2. saveCurrentMemory加入保存请求ID校验防止并发保存竞态
Comment thread static/js/memory_browser.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/js/memory_browser.js`:
- Around line 3370-3378: Update the save request error handler associated with
reqId in the recent-file save flow to first check whether reqId !==
_saveRequestId; stale requests must return false without calling showSaveStatus,
while the current request retains the existing failure-status behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f78de2a-e681-4895-8114-fd2c8e19d245

📥 Commits

Reviewing files that changed from the base of the PR and between 19e1d7e and 376667f.

📒 Files selected for processing (1)
  • static/js/memory_browser.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Project-N-E-K-O/N.E.K.O.-PC (manual)

Comment on lines +3370 to +3378
var reqId = ++_saveRequestId;
try {
const resp = await fetch('/api/memory/recent_file/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: currentMemoryFile, chat: chatData })
});
const data = await resp.json();
if (reqId !== _saveRequestId) return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

过期保存请求仍可能覆盖最新保存状态喵

reqId 只在成功响应路径校验;如果旧请求在新请求发起后进入 catch,它仍会调用 showSaveStatus(...保存失败...),把最新保存的状态短暂覆盖成过期错误喵。请在 catch 中同样先判断 reqId !== _saveRequestId,仅让当前请求更新 UI;旧请求仍可返回 false 终止对应的过期切换喵。

🐾 建议修复
         } catch (e) {
+            if (reqId !== _saveRequestId) return false;
             showSaveStatus(window.t ? window.t('memory.saveFailedGeneral') : '保存失败', false);
             return false;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var reqId = ++_saveRequestId;
try {
const resp = await fetch('/api/memory/recent_file/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: currentMemoryFile, chat: chatData })
});
const data = await resp.json();
if (reqId !== _saveRequestId) return false;
var reqId = ++_saveRequestId;
try {
const resp = await fetch('/api/memory/recent_file/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: currentMemoryFile, chat: chatData })
});
const data = await resp.json();
if (reqId !== _saveRequestId) return false;
} catch (e) {
if (reqId !== _saveRequestId) return false;
showSaveStatus(window.t ? window.t('memory.saveFailedGeneral') : '保存失败', false);
return false;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/js/memory_browser.js` around lines 3370 - 3378, Update the save
request error handler associated with reqId in the recent-file save flow to
first check whether reqId !== _saveRequestId; stale requests must return false
without calling showSaveStatus, while the current request retains the existing
failure-status behavior.

…iles; serialize save to prevent concurrent overwrite (Greptile P1)

The i18n sync CI check requires identical diff hunk ranges across all
locale files. es.json/pt.json had an extra hunk from fixing a translation
typo (Claro→Borrar/Limpar) that the other 6 files don't have. Reverting
these two hunks makes all 8 locale diffs match the reference en.json.

Concurrent-save guard: chain saves via _savePromise so that only one
save is in-flight per file at any time, preventing the backend from
receiving writes out of order (older request overwriting newer data).
@wehos

wehos commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

这项功能在testbench(tests/testbench)里有更完善的版本,不仅包含内容查看,还有embedding聚类以及自动化issue挖掘,目前仅供开发人员参考。

@wehos

wehos commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

我们认为对一般用户暴露更多记忆细节适得其反。未来也许有日记之类的专门给用户看的功能会好一些。另外下版本我们会加入记忆卡片锻造

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants