feat: LLM intent router, direct chat execution, and Memory V2 light intervention

Wire chat intent routing with direct_chat on regular sessions, skill-selected
short-circuit to Agent, memory light/heavy intervention tiers, and fix direct
chat UI stuck streaming after completion.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-04 22:32:57 +08:00
parent bfb6356f7d
commit e45c9300bf
33 changed files with 3412 additions and 105 deletions
+24 -9
View File
@@ -16,6 +16,10 @@ import { buildCurrentTimeAgentPrefix, buildTaskRoutingAgentText } from './user-m
import { reconcileAgentSession } from './session-reconcile.mjs';
import { createImgproxySigner } from './imgproxy-signer.mjs';
import { isDirectChatSessionId } from './direct-chat-service.mjs';
import {
memoryLimitForIntervention,
resolveMemoryInterventionMode,
} from './memory-intervention.mjs';
const insecureDispatcher = new Agent({
connect: { rejectUnauthorized: false },
@@ -1078,19 +1082,31 @@ export function createTkmindProxy({
};
}
async function resolveUserMemories(userId, { sessionId = null, query = null, limit = 40 } = {}) {
async function resolveUserMemories(
userId,
{ sessionId = null, query = null, limit = null, forceDeepReasoning = false, recallQuestion = false } = {},
) {
if (!userId) return [];
const intervention = resolveMemoryInterventionMode({
forceDeepReasoning,
recallQuestion,
context: 'agent',
});
const resolvedLimit = Number.isFinite(Number(limit)) && Number(limit) > 0
? Number(limit)
: memoryLimitForIntervention(intervention, { context: 'agent' });
if (resolvedLimit <= 0) return [];
if (memoryV2?.resolve) {
const resolved = await memoryV2.resolve({
userId,
sessionId,
query,
limit,
limit: resolvedLimit,
}).catch(() => null);
return Array.isArray(resolved?.memories) ? resolved.memories : [];
}
return conversationMemoryService?.listMemories
? conversationMemoryService.listMemories(userId, { limit }).catch(() => [])
? conversationMemoryService.listMemories(userId, { limit: resolvedLimit }).catch(() => [])
: [];
}
@@ -1140,7 +1156,7 @@ export function createTkmindProxy({
}
}
const publishLayout = await userAuth.getUserPublishLayout(userId);
const userMemories = await resolveUserMemories(userId, { sessionId: session.id, limit: 40 });
const userMemories = await resolveUserMemories(userId, { sessionId: session.id });
await reconcileAgentSession(
(pathname, init) => apiFetch(startTarget, apiSecret, pathname, init),
session.id,
@@ -1244,7 +1260,7 @@ export function createTkmindProxy({
async function reconcileSessionPolicyForUser(
userId,
sessionId,
{ toolMode = 'chat', query = null } = {},
{ toolMode = 'chat', query = null, forceDeepReasoning = false } = {},
) {
if (!userId || !sessionId) return;
const target = await resolveTarget(sessionId);
@@ -1254,7 +1270,7 @@ export function createTkmindProxy({
const userMemories = await resolveUserMemories(userId, {
sessionId,
query,
limit: 40,
forceDeepReasoning,
});
await reconcileAgentSession(
(pathname, init) => apiFetch(target, apiSecret, pathname, init),
@@ -1282,7 +1298,7 @@ export function createTkmindProxy({
sessionId,
requestId,
userMessage,
{ toolMode = 'chat' } = {},
{ toolMode = 'chat', forceDeepReasoning = false } = {},
) {
if (!userId || !sessionId) throw new Error('缺少会话信息');
const owns = await userAuth.ownsSession(userId, sessionId);
@@ -1298,6 +1314,7 @@ export function createTkmindProxy({
await reconcileSessionPolicyForUser(userId, sessionId, {
toolMode,
query: firstUserText(userMessage),
forceDeepReasoning,
});
await applySessionLlmProvider(sessionId);
@@ -1432,7 +1449,6 @@ export function createTkmindProxy({
const publishLayout = await userAuth.getUserPublishLayout(req.currentUser.id);
const userMemories = await resolveUserMemories(req.currentUser.id, {
sessionId: session.id,
limit: 40,
});
const api = (pathname, init) => apiFetch(startTarget, apiSecret, pathname, init);
try {
@@ -1503,7 +1519,6 @@ export function createTkmindProxy({
const publishLayout = await userAuth.getUserPublishLayout(req.currentUser.id);
const userMemories = await resolveUserMemories(req.currentUser.id, {
sessionId,
limit: 40,
});
try {
await reconcileAgentSession(