feat(memory-v2): close Phase A with auto-review, product events, and H5 recall UI.

Add candidate auto-review pipeline, shadow audit tooling, admin metrics page,
and user-visible memory recall hints in chat with phase-a readiness checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-01 17:14:06 +08:00
parent 666db0b939
commit 6f3e53a56a
50 changed files with 3730 additions and 38 deletions
+56
View File
@@ -0,0 +1,56 @@
import type { UserMemoryRecallHintResponse, UserMemorySyncResponse } from '../types';
export type { MemoryRecallBadge } from '../types';
export function formatPersonalMemorySavedNotice(
result: Pick<UserMemorySyncResponse, 'personalMemory'>,
): string | null {
const preview = result.personalMemory?.savedPreviews?.[0]?.preview;
if (!preview) return null;
return `已记住:${preview}${preview.length >= 60 ? '…' : ''}`;
}
export function formatMemoryRecallNotice(count: number): string | null {
if (!Number.isFinite(count) || count <= 0) return null;
return `参考了你的 ${count} 条记忆`;
}
export function formatMemoryHintNotice(hint: UserMemoryRecallHintResponse): string | null {
if (hint.injectionEnabled && hint.memoryCount > 0) {
return formatMemoryRecallNotice(hint.memoryCount);
}
if (hint.savedPreview) {
const preview = hint.savedPreview;
return `已记住:${preview}${preview.length >= 60 ? '…' : ''}`;
}
return null;
}
export function formatUserMemorySyncNotice(result: UserMemorySyncResponse): string {
const savedNotice = formatPersonalMemorySavedNotice(result);
if (savedNotice) return savedNotice;
if (result.memories > 0) {
return `新增 ${result.memories} 条长期记忆,当前累计 ${result.totalMemories}`;
}
if (result.analyzed > 0) {
return `已分析最近对话,暂无新的长期记忆,当前累计 ${result.totalMemories}`;
}
return `没有新的用户对话可提炼,当前累计 ${result.totalMemories}`;
}
export function buildMemoryRecallBadge(
hint: UserMemoryRecallHintResponse,
): { count: number; previews: string[] } | null {
if (!hint.injectionEnabled || hint.memoryCount <= 0) return null;
return {
count: hint.memoryCount,
previews: hint.memoryPreviews ?? [],
};
}
export function resolveMessageRecallKey(
message: { id?: string | null; role: string; created: number },
index: number,
): string {
return message.id ?? `msg-${index}-${message.role}-${message.created}`;
}