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:
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { attachPortalUserMemoryRoutes } from './portal-user-memory-routes.mjs';
|
||||
import { attachPortalUserMemoryRoutes, resolveLatestMemoryRecallHint } from './portal-user-memory-routes.mjs';
|
||||
|
||||
function createRouterRecorder() {
|
||||
const routes = new Map();
|
||||
@@ -69,6 +69,7 @@ test('User Memory module preserves route inventory and order', () => {
|
||||
|
||||
assert.deepEqual([...api.routes.keys()], [
|
||||
'POST /user-memory/v1/remember-recent',
|
||||
'GET /user-memory/v1/recall-hint',
|
||||
'POST /user-memory/v1/sync',
|
||||
'GET /user-memory/v1/items',
|
||||
'DELETE /user-memory/v1/items/:memoryId',
|
||||
@@ -212,6 +213,11 @@ test('remember-recent preserves write, sync, resolve order and response projecti
|
||||
memories: 2,
|
||||
totalMemories: 3,
|
||||
syncedToSession: true,
|
||||
personalMemory: {
|
||||
savedPreviews: [],
|
||||
autoReviewed: 0,
|
||||
pendingReview: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -419,3 +425,79 @@ test('delete preserves forwarding, unavailable fallback, success, and errors', a
|
||||
assert.equal(failureRes.statusCode, 500);
|
||||
assert.deepEqual(failureRes.body, { message: 'forget failed' });
|
||||
});
|
||||
|
||||
test('resolveLatestMemoryRecallHint merges recall event and saved candidate preview', async () => {
|
||||
const pool = {
|
||||
async query(sql, params) {
|
||||
if (String(sql).includes('agent_memory_resolved')) {
|
||||
return [[{
|
||||
data_json: JSON.stringify({
|
||||
memoryCount: 3,
|
||||
injectionEnabled: true,
|
||||
mode: 'canary',
|
||||
memoryPreviews: ['我喜欢喝美式咖啡', '每周三做代码评审'],
|
||||
}),
|
||||
created_at: 1000,
|
||||
run_id: 'run-1',
|
||||
}]];
|
||||
}
|
||||
if (String(sql).includes('h5_memory_v2_candidates')) {
|
||||
assert.deepEqual(params, ['user-1', 'session-1']);
|
||||
return [[{ content: '我喜欢喝美式咖啡', updated_at: 900 }]];
|
||||
}
|
||||
return [[]];
|
||||
},
|
||||
};
|
||||
const hint = await resolveLatestMemoryRecallHint(pool, {
|
||||
userId: 'user-1',
|
||||
sessionId: 'session-1',
|
||||
});
|
||||
assert.deepEqual(hint, {
|
||||
memoryCount: 3,
|
||||
injectionEnabled: true,
|
||||
mode: 'canary',
|
||||
createdAt: 1000,
|
||||
runId: 'run-1',
|
||||
savedPreview: '我喜欢喝美式咖啡',
|
||||
savedAt: 900,
|
||||
memoryPreviews: ['我喜欢喝美式咖啡', '每周三做代码评审'],
|
||||
});
|
||||
});
|
||||
|
||||
test('recall-hint preserves capability gate and returns merged hint payload', async () => {
|
||||
const api = createRouterRecorder();
|
||||
attachPortalUserMemoryRoutes(
|
||||
api,
|
||||
createDependencies({
|
||||
getPool: () => ({
|
||||
async query(sql) {
|
||||
if (String(sql).includes('agent_memory_resolved')) {
|
||||
return [[{
|
||||
data_json: JSON.stringify({ memoryCount: 2, injectionEnabled: true, mode: 'canary' }),
|
||||
created_at: 2000,
|
||||
run_id: 'run-9',
|
||||
}]];
|
||||
}
|
||||
return [[]];
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const res = createResponseRecorder();
|
||||
await api.routes.get('GET /user-memory/v1/recall-hint')(
|
||||
createRequest({ query: { sessionId: 'session-9' } }),
|
||||
res,
|
||||
);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.deepEqual(res.body, {
|
||||
ok: true,
|
||||
memoryCount: 2,
|
||||
injectionEnabled: true,
|
||||
mode: 'canary',
|
||||
createdAt: 2000,
|
||||
runId: 'run-9',
|
||||
savedPreview: null,
|
||||
savedAt: null,
|
||||
memoryPreviews: [],
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user