feat(memory): add gated agent recall and lifecycle controls

This commit is contained in:
john
2026-07-16 21:05:27 +08:00
parent c1012da120
commit 6e460336f7
13 changed files with 655 additions and 3 deletions
+77
View File
@@ -1078,6 +1078,83 @@ test('createChatIntentRouter timeout fallback prefers agent even when policy req
assert.equal(result.source, 'fallback');
});
test('agent memory shadow resolve is independently gated and never marked for injection', async () => {
const calls = [];
const router = createChatIntentRouter({
env: {
MEMORY_AGENT_RESOLVE_ENABLED: '1',
MEMORY_AGENT_INJECTION_MODE: 'shadow',
MEMORY_AGENT_RESOLVE_LIMIT: '3',
MEMORY_AGENT_RESOLVE_TIMEOUT_MS: '100',
},
memoryV2: {
async resolve(input) {
calls.push(input);
return { source: 'legacy-conversation-memory', memories: [{ label: 'preference', text: '喜欢完整方案' }] };
},
},
});
const result = await router.resolveAgentMemoryContext({
userId: 'u1',
sessionId: 's1',
text: '帮我设计一个商城',
});
assert.equal(result.mode, 'shadow');
assert.equal(result.injectionEnabled, false);
assert.equal(result.skipped, false);
assert.equal(result.memories.length, 1);
assert.deepEqual(calls[0], { userId: 'u1', sessionId: 's1', query: '帮我设计一个商城', limit: 3 });
});
test('agent memory canary injects only for configured user ids', async () => {
const router = createChatIntentRouter({
env: {
MEMORY_AGENT_RESOLVE_ENABLED: '1',
MEMORY_AGENT_INJECTION_MODE: 'canary',
MEMORY_AGENT_CANARY_USER_IDS: 'user-canary, user-other',
},
memoryV2: {
async resolve() {
return { memories: [{ label: 'goal', text: '当前项目是 TKMind' }] };
},
},
});
const canary = await router.resolveAgentMemoryContext({ userId: 'user-canary', text: '继续项目' });
const control = await router.resolveAgentMemoryContext({ userId: 'user-control', text: '继续项目' });
assert.equal(canary.injectionEnabled, true);
assert.equal(control.injectionEnabled, false);
assert.equal(canary.memories.length, 1);
assert.equal(control.memories.length, 1);
});
test('active agent memory context is hidden from displayText but available to orchestration envelope', () => {
const enriched = applyAgentOrchestrationToUserMessage(
{
role: 'user',
content: [{ type: 'text', text: '帮我设计一个商城' }],
},
{
route: CHAT_INTENT_ROUTE.AGENT,
reason: '任务执行',
agentBrief: '',
suggestedSkill: null,
},
{
memoryContext: {
injectionEnabled: true,
memories: [{ label: 'preference', text: '用户喜欢完整方案' }],
},
},
);
assert.equal(enriched.metadata.displayText, '帮我设计一个商城');
assert.match(enriched.content[0].text, /Memory Context/);
assert.match(enriched.content[0].text, /用户喜欢完整方案/);
});
test('logRouterDecisionShadow emits payload only in shadow mode', () => {
const previous = process.env.MEMIND_ROUTER_NORMALIZED_DECISION;
const lines = [];