Files
memind/memory-v2-user-feedback.mjs
T
john 6f3e53a56a 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>
2026-08-01 17:14:06 +08:00

55 lines
1.9 KiB
JavaScript

export function summarizePersonalMemoryObservation(observation) {
if (!observation || observation.skipped || !observation.enabled) {
return { savedPreviews: [], autoReviewed: 0, pendingReview: 0 };
}
const savedPreviews = (observation.results ?? [])
.filter((item) => item?.accepted && item?.candidate?.status === 'accepted')
.map((item) => ({
preview: String(item.candidate.content ?? '').slice(0, 60),
policyReason: String(item.candidate.policyReason ?? ''),
memoryType: String(item.candidate.memoryType ?? ''),
}))
.slice(0, 3);
return {
savedPreviews,
autoReviewed: Number(observation.autoReviewed ?? 0),
pendingReview: Number(observation.pendingReview ?? 0),
};
}
export function formatPersonalMemorySavedNotice(summary) {
if (!summary?.savedPreviews?.length) return null;
const preview = summary.savedPreviews[0]?.preview;
if (!preview) return null;
return `已记住:${preview}${preview.length >= 60 ? '…' : ''}`;
}
export function formatMemoryRecallNotice(count) {
const safeCount = Number(count ?? 0);
if (!Number.isFinite(safeCount) || safeCount <= 0) return null;
return `参考了你的 ${safeCount} 条记忆`;
}
const INTERNAL_MEMORY_PREVIEW_RES = [
/^\[Memory Context\]/i,
/^\[用户身份\]/,
/^【TKMind 路由提示】/,
/^【Memind 任务编排】/,
/^\[MindSpace 上下文\]/,
];
export function buildMemoryRecallPreviews(memories, { limit = 5, maxLength = 80 } = {}) {
if (!Array.isArray(memories)) return [];
const previews = [];
for (const item of memories) {
const raw = String(item?.text ?? item?.content ?? '').trim();
if (!raw) continue;
if (INTERNAL_MEMORY_PREVIEW_RES.some((pattern) => pattern.test(raw))) continue;
if (/\[Memory Context\]/i.test(raw)) continue;
const clipped = raw.slice(0, maxLength);
previews.push(clipped.length < raw.length ? `${clipped}` : clipped);
if (previews.length >= limit) break;
}
return previews;
}