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
+53
View File
@@ -0,0 +1,53 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
resolveCandidateAutoReviewAction,
resolveCandidateAutoReviewScopes,
} from './memory-v2-candidate-auto-review.mjs';
test('resolveCandidateAutoReviewAction accepts durable explicit and preference signals', () => {
assert.deepEqual(
resolveCandidateAutoReviewAction({
status: 'candidate',
content: '请记住我喜欢喝美式咖啡',
policyReason: 'explicit_memory_request',
}),
{ action: 'accept', reason: 'auto_review_policy' },
);
assert.deepEqual(
resolveCandidateAutoReviewAction({
status: 'candidate',
content: '我偏好用 TypeScript 写前端代码',
policyReason: 'preference_signal',
}),
{ action: 'accept', reason: 'auto_review_policy' },
);
});
test('resolveCandidateAutoReviewAction rejects decision signals and questions', () => {
assert.deepEqual(
resolveCandidateAutoReviewAction({
status: 'candidate',
content: '我们已经决定采用 React 作为前端主框架',
policyReason: 'decision_signal',
}),
{ action: 'reject', reason: 'decision_signal_not_durable' },
);
const question = resolveCandidateAutoReviewAction({
status: 'candidate',
content: '什么是 Memory V2',
policyReason: 'stable_fact_signal',
});
assert.equal(question.action, 'reject');
assert.ok(['what_is_question', 'non_durable_content'].includes(question.reason));
});
test('resolveCandidateAutoReviewScopes prefers lifecycle canary users', () => {
const scopes = resolveCandidateAutoReviewScopes({
MEMORY_CANDIDATE_ENABLED: '1',
MEMORY_CANDIDATE_MODE: 'canary',
MEMORY_LIFECYCLE_ROLLOUT_MODE: 'canary',
MEMORY_LIFECYCLE_ROLLOUT_USER_IDS: 'user-a,user-b',
});
assert.deepEqual(scopes, ['user-a', 'user-b']);
});