Skip to content

feat(memory): show agent memory in chat - #1868

Merged
zerob13 merged 1 commit into
devfrom
feat/agent-memory-chat-visibility
Jul 2, 2026
Merged

feat(memory): show agent memory in chat#1868
zerob13 merged 1 commit into
devfrom
feat/agent-memory-chat-visibility

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator
  • Add memory update chip for newly extracted memories
  • Add per-turn memory details dialog in assistant messages
  • Support remember, archive, amend, and targeted delete flows
  • Extend memory contracts with ids-only event and lookup payloads

Summary by CodeRabbit

  • New Features

    • Added in-chat memory controls for assistant messages, including a memory toolbar action, a memory update chip, and a memory details dialog.
    • Users can now save selected text as memory, view memory usage for a turn, and archive or update memories from the UI.
    • Memory support is now available in multiple languages.
  • Bug Fixes

    • Improved memory update handling so selected items, archived status, and turn details stay in sync across the chat experience.
    • Added support for viewing multiple memories at once and preserving the order of requested items.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds agent-memory archive and batch-get operations, extends the memory.updated event with contextual metadata (memoryId, sessionId, createdIds), and introduces renderer-side memory activity features: a Pinia store, a per-turn memory review dialog, a memory update chip UI, toolbar integration, and full i18n translations across all locales.

Changes

Memory context, archive/getByIds, and chip/turn UI

Layer / File(s) Summary
Memory event/route contracts
src/shared/contracts/events/memory.events.ts, src/shared/contracts/routes.ts, src/shared/contracts/routes/memory.routes.ts, test/main/routes/memoryDto.test.ts
Adds reindex reason, optional memoryId/sessionId/createdIds (max 50) to memoryUpdatedEvent, new memory.getByIds and memory.archive route contracts, and selectedIds on the view manifest schema.
MemoryPresenter core: change context, archive, getByIds
src/main/presenter/index.ts, src/main/presenter/memoryPresenter/{index,types}.ts, src/main/presenter/sqlitePresenter/tables/agentMemory.ts, test/main/presenter/*
Forwards context through onMemoryChanged, batches created-ids for extract events, implements archiveUserMemory (with audit logging) and getByIds, and adds listByIds to the repository/table.
Route dispatcher wiring
src/main/routes/index.ts, test/main/routes/dispatcher.test.ts
Adds Deepchat-guarded memoryGetByIdsRoute/memoryArchiveRoute handlers, passes sessionId into memory.add, and derives selectedIds for manifest DTOs.
Renderer MemoryClient API
src/renderer/api/MemoryClient.ts, test/renderer/api/clients.test.ts
Adds getByIds and archive client methods and forwards sessionId when adding memory.
memoryActivity Pinia store
src/renderer/src/stores/ui/memoryActivity.ts, test/renderer/stores/memoryActivityStore.test.ts
New store managing chip items, per-turn manifest/details caching with staleness/versioning, memory update event handling, and mutation actions (undo/forget/amend/rememberSelection).
MemoryTurnDialog component
src/renderer/src/components/chat/MemoryTurnDialog.vue, test/renderer/components/chat/MemoryTurnDialog.test.ts
New dialog rendering per-turn memory state with a guarded forget action per item.
MemoryUpdateChip component
src/renderer/src/components/chat/MemoryUpdateChip.vue, test/renderer/components/chat/MemoryUpdateChip.test.ts
New popover chip listing created/edited memories with undo/forget/edit/amend actions.
Toolbar/message/ChatPage integration
src/renderer/src/components/message/{MessageToolbar,MessageItemAssistant}.vue, src/renderer/src/pages/ChatPage.vue, related tests
Adds a memory toolbar button/event, wires memory open/remember-selection actions, and renders the new components in ChatPage.
Memory UI translations
src/renderer/src/i18n/*/chat.json
Adds a memory localization section across all supported locales.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MessageItemAssistant
  participant MemoryActivityStore
  participant MemoryClient
  participant MemoryTurnDialog

  User->>MessageItemAssistant: click "memory" toolbar action
  MessageItemAssistant->>MemoryActivityStore: openTurnMemories(assistantMessageId)
  MemoryActivityStore->>MemoryActivityStore: locate preceding user message
  MemoryActivityStore->>MemoryClient: listViewManifests / getByIds
  MemoryClient-->>MemoryActivityStore: manifest + memory details
  MemoryActivityStore-->>MemoryTurnDialog: selectedTurn (ready/stale/error)
  User->>MemoryTurnDialog: click "forget"
  MemoryTurnDialog->>MemoryActivityStore: forget(memoryId)
  MemoryActivityStore->>MemoryClient: archive(agentId, memoryId)
  MemoryClient-->>MemoryActivityStore: ok
  MemoryActivityStore-->>MemoryTurnDialog: updated status
Loading

Possibly related PRs

Suggested reviewers: deepinfect

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding agent memory visibility and controls in the chat UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-memory-chat-visibility

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 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: 2

🧹 Nitpick comments (2)
src/renderer/src/components/chat/MemoryTurnDialog.vue (1)

70-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Raw memory kind/category values rendered without i18n.

detail.memory.kind and detail.memory.category are interpolated directly into the badges, unlike detail.memory.status === 'archived' which correctly goes through t('chat.memory.status.archived') (Line 83). For the bounded kind enum (semantic/episodic), this leaves raw English/technical values visible to non-English users.

🌐 Proposed fix to translate `kind`
-                    <Badge variant="outline" class="text-[10px]">{{ detail.memory.kind }}</Badge>
+                    <Badge variant="outline" class="text-[10px]">{{ t(`chat.memory.kind.${detail.memory.kind}`) }}</Badge>

As per coding guidelines, "All user-facing strings must use vue-i18n keys in src/renderer/src/i18n".

🤖 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 `@src/renderer/src/components/chat/MemoryTurnDialog.vue` around lines 70 - 86,
The memory badges in MemoryTurnDialog currently render raw kind/category values
instead of localized text. Update the detail.memory.kind and
detail.memory.category badge content to use vue-i18n translations, following the
same pattern as the archived status badge via t(...). Add or reuse the
appropriate i18n keys in src/renderer/src/i18n for the bounded memory kind enum
and any category labels so all user-facing text stays localized.

Source: Coding guidelines

src/renderer/src/components/chat/MemoryUpdateChip.vue (1)

44-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

kind badge is not localized.

The status: archived badge is translated (t('chat.memory.status.archived')), but the kind badge (line 46) renders the raw enum value (semantic/episodic) directly. This is inconsistent and will show untranslated English-derived tokens in the other 19 non-English locales this PR adds.

♻️ Proposed fix
-                  <Badge variant="outline" class="text-[10px]">{{ item.memory.kind }}</Badge>
+                  <Badge variant="outline" class="text-[10px]">
+                    {{ t(`chat.memory.kind.${item.memory.kind}`) }}
+                  </Badge>

As per coding guidelines, "All user-facing strings must use vue-i18n keys in src/renderer/src/i18n."

🤖 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 `@src/renderer/src/components/chat/MemoryUpdateChip.vue` around lines 44 - 58,
The `kind` badge in `MemoryUpdateChip.vue` is rendering the raw
`item.memory.kind` enum instead of a localized label, unlike the archived status
badge. Update the badge to use a vue-i18n key from `src/renderer/src/i18n` (for
example by mapping the memory kind values in this component or a helper) and
keep the `Badge` rendering logic in `MemoryUpdateChip.vue` using translated text
for all user-facing strings.

Source: Coding guidelines

🤖 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 `@src/main/routes/index.ts`:
- Around line 648-651: The selected count in the route mapping can drift from
the resolved IDs because it is computed from raw record.selected while
deriveSelectedMemoryIds() dedupes and filters invalid entries. Update the
mapping in index.ts so selectedCount is derived from the same normalized
selectedIds value used for selectedIds, keeping the turn dialog count consistent
with the actual resolved memories.

In `@src/renderer/src/stores/ui/memoryActivity.ts`:
- Around line 440-446: The selected count shown by the turn dialog can diverge
from the actual rendered details because `getMemoryTurns` in `memoryActivity.ts`
deduplicates and truncates `manifest.selectedIds` before building `details`,
while `MemoryTurnDialog.vue` still displays `turn.manifest.selectedCount`.
Update the displayed stat to use the effective deduped/truncated selection count
derived from `selectedIds` (or store that count alongside `details`), and handle
cases where `uniqueIds(...)` or `MEMORY_DETAILS_BATCH_LIMIT` changes the visible
list.

---

Nitpick comments:
In `@src/renderer/src/components/chat/MemoryTurnDialog.vue`:
- Around line 70-86: The memory badges in MemoryTurnDialog currently render raw
kind/category values instead of localized text. Update the detail.memory.kind
and detail.memory.category badge content to use vue-i18n translations, following
the same pattern as the archived status badge via t(...). Add or reuse the
appropriate i18n keys in src/renderer/src/i18n for the bounded memory kind enum
and any category labels so all user-facing text stays localized.

In `@src/renderer/src/components/chat/MemoryUpdateChip.vue`:
- Around line 44-58: The `kind` badge in `MemoryUpdateChip.vue` is rendering the
raw `item.memory.kind` enum instead of a localized label, unlike the archived
status badge. Update the badge to use a vue-i18n key from
`src/renderer/src/i18n` (for example by mapping the memory kind values in this
component or a helper) and keep the `Badge` rendering logic in
`MemoryUpdateChip.vue` using translated text for all user-facing strings.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd6c1ca4-6406-45db-a960-3487567d1cd8

📥 Commits

Reviewing files that changed from the base of the PR and between f12b17c and c410e11.

📒 Files selected for processing (47)
  • src/main/presenter/index.ts
  • src/main/presenter/memoryPresenter/index.ts
  • src/main/presenter/memoryPresenter/types.ts
  • src/main/presenter/sqlitePresenter/tables/agentMemory.ts
  • src/main/routes/index.ts
  • src/renderer/api/MemoryClient.ts
  • src/renderer/src/components/chat/MemoryTurnDialog.vue
  • src/renderer/src/components/chat/MemoryUpdateChip.vue
  • src/renderer/src/components/message/MessageItemAssistant.vue
  • src/renderer/src/components/message/MessageToolbar.vue
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/renderer/src/pages/ChatPage.vue
  • src/renderer/src/stores/ui/memoryActivity.ts
  • src/shared/contracts/events/memory.events.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/memory.routes.ts
  • test/main/presenter/agentMemoryTable.test.ts
  • test/main/presenter/fakes/memoryFakes.ts
  • test/main/presenter/memoryPresenter.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/routes/memoryDto.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/chat/MemoryTurnDialog.test.ts
  • test/renderer/components/chat/MemoryUpdateChip.test.ts
  • test/renderer/components/message/MessageItemAssistant.test.ts
  • test/renderer/components/message/MessageToolbar.trace.test.ts
  • test/renderer/stores/memoryActivityStore.test.ts

Comment thread src/main/routes/index.ts
Comment on lines 648 to +651
tokenBudget: readNumber(record.tokenBudget),
estimatedTokens: readNumber(record.estimatedTokens),
selectedCount: Array.isArray(record.selected) ? record.selected.length : 0,
selectedIds: deriveSelectedMemoryIds(record.selected),

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

selectedCount can diverge from selectedIds.length.

selectedCount counts raw record.selected entries, while selectedIds dedupes and drops invalid/empty entries. If the underlying "selected" payload ever has duplicate or malformed entries, the turn dialog could show a selection count that doesn't match the number of memories actually resolved via selectedIds.

💡 Proposed fix: derive selectedCount from selectedIds
+    const selectedIds = deriveSelectedMemoryIds(record.selected)
     return {
       sessionId: row.session_id,
       messageId,
       entryId: row.entry_id,
       policyVersion:
         typeof record.policyVersion === 'number' && Number.isFinite(record.policyVersion)
           ? record.policyVersion
           : null,
       tokenBudget: readNumber(record.tokenBudget),
       estimatedTokens: readNumber(record.estimatedTokens),
-      selectedCount: Array.isArray(record.selected) ? record.selected.length : 0,
-      selectedIds: deriveSelectedMemoryIds(record.selected),
+      selectedCount: selectedIds ? selectedIds.length : Array.isArray(record.selected) ? record.selected.length : 0,
+      selectedIds,
📝 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
tokenBudget: readNumber(record.tokenBudget),
estimatedTokens: readNumber(record.estimatedTokens),
selectedCount: Array.isArray(record.selected) ? record.selected.length : 0,
selectedIds: deriveSelectedMemoryIds(record.selected),
const selectedIds = deriveSelectedMemoryIds(record.selected)
selectedCount: selectedIds ? selectedIds.length : Array.isArray(record.selected) ? record.selected.length : 0,
selectedIds,
🤖 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 `@src/main/routes/index.ts` around lines 648 - 651, The selected count in the
route mapping can drift from the resolved IDs because it is computed from raw
record.selected while deriveSelectedMemoryIds() dedupes and filters invalid
entries. Update the mapping in index.ts so selectedCount is derived from the
same normalized selectedIds value used for selectedIds, keeping the turn dialog
count consistent with the actual resolved memories.

Comment on lines +440 to +446
let details: MemoryTurnDetail[] = []
if (manifest?.selectedIds?.length) {
const selectedIds = uniqueIds(manifest.selectedIds).slice(0, MEMORY_DETAILS_BATCH_LIMIT)
const memories = await memoryClient.getByIds(agentId, selectedIds)
const memoryById = new Map(memories.map((memory) => [memory.id, memory]))
details = selectedIds.map((id) => ({ id, memory: memoryById.get(id) ?? null }))
}

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

Displayed detail count can silently diverge from the manifest's selectedCount stat.

manifest.selectedIds is deduplicated via uniqueIds(...) and truncated to MEMORY_DETAILS_BATCH_LIMIT (50) before building details. The turn dialog stat card renders turn.manifest.selectedCount directly (raw backend count, which may include duplicates or exceed 50), while turn.details reflects the deduped/truncated list. When duplicates or >50 selections occur (a scenario the test suite explicitly exercises with selectedIds: ['m2', 'missing', 'm2']), the "Selected" stat shown in MemoryTurnDialog.vue (Line 41) will not match the number of detail cards rendered, which can confuse users.

Consider deriving the displayed stat from the deduped/effective list, or surfacing a note when truncation/deduplication occurred.

🤖 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 `@src/renderer/src/stores/ui/memoryActivity.ts` around lines 440 - 446, The
selected count shown by the turn dialog can diverge from the actual rendered
details because `getMemoryTurns` in `memoryActivity.ts` deduplicates and
truncates `manifest.selectedIds` before building `details`, while
`MemoryTurnDialog.vue` still displays `turn.manifest.selectedCount`. Update the
displayed stat to use the effective deduped/truncated selection count derived
from `selectedIds` (or store that count alongside `details`), and handle cases
where `uniqueIds(...)` or `MEMORY_DETAILS_BATCH_LIMIT` changes the visible list.

@zerob13
zerob13 merged commit 673a79f into dev Jul 2, 2026
3 checks passed
@zhangmo8
zhangmo8 deleted the feat/agent-memory-chat-visibility branch July 7, 2026 05:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants