fix(agent): restore active-task routing and active-mode validation observation
Memind CI / Test, build, and release guards (push) Successful in 5m23s
Memind CI / Test, build, and release guards (push) Successful in 5m23s
Keep correction follow-ups on Agent using session transcript and active task context, and let Page Data validation reach Orchestrator when mode is active instead of requiring shadowEngine. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+118
-3
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { isRunStreamReplayEnabled } from './agent-run-stream.mjs';
|
||||
import { isDirectChatSessionId } from './direct-chat-service.mjs';
|
||||
import { CHAT_INTENT_ROUTE, resolveGatewayAgentSessionId, resolveLegacyRouteFromClassification, logRouterDecisionShadow } from './chat-intent-router.mjs';
|
||||
import { CHAT_INTENT_ROUTE, formatRouterTranscript, resolveGatewayAgentSessionId, resolveLegacyRouteFromClassification, logRouterDecisionShadow } from './chat-intent-router.mjs';
|
||||
import { resolveSessionAccess } from './session-broker.mjs';
|
||||
import {
|
||||
loadSnapshotMessages,
|
||||
@@ -617,6 +617,103 @@ function resolveEffectiveToolMode(runOptions) {
|
||||
return runOptions?.pageDataAiderWorkflow ? 'chat' : (runOptions?.toolMode ?? 'chat');
|
||||
}
|
||||
|
||||
const ACTIVE_TASK_CONTEXT_MAX_AGE_MS = 4 * 60 * 60 * 1000;
|
||||
|
||||
function parseIntentRoutedEvent(dataJson) {
|
||||
if (!dataJson) return null;
|
||||
const payload = typeof dataJson === 'string'
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(dataJson);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})()
|
||||
: dataJson;
|
||||
if (!payload || typeof payload !== 'object') return null;
|
||||
return {
|
||||
route: payload.route ?? payload.decision?.route ?? null,
|
||||
suggestedSkill: payload.suggestedSkill ?? payload.suggested_skill ?? null,
|
||||
reason: payload.reason ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveActiveTaskContext(pool, {
|
||||
userId,
|
||||
sessionId,
|
||||
excludeRunId = null,
|
||||
nowMs: nowMsFn = Date.now,
|
||||
} = {}) {
|
||||
if (!pool || !userId || !sessionId || isDirectChatSessionId(sessionId)) {
|
||||
return null;
|
||||
}
|
||||
const params = [userId, sessionId];
|
||||
let excludeClause = '';
|
||||
if (excludeRunId) {
|
||||
excludeClause = ' AND r.id <> ?';
|
||||
params.push(excludeRunId);
|
||||
}
|
||||
const [rows] = await pool.query(
|
||||
`SELECT r.id, r.status, r.error_message, r.created_at, r.updated_at,
|
||||
(
|
||||
SELECT e.data_json
|
||||
FROM h5_agent_run_events e
|
||||
WHERE e.run_id = r.id AND e.event_type = 'intent_routed'
|
||||
ORDER BY e.created_at ASC
|
||||
LIMIT 1
|
||||
) AS intent_json
|
||||
FROM h5_agent_runs r
|
||||
WHERE r.user_id = ? AND r.agent_session_id = ?${excludeClause}
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 1`,
|
||||
params,
|
||||
);
|
||||
const row = rows?.[0];
|
||||
if (!row) return null;
|
||||
|
||||
const intent = parseIntentRoutedEvent(row.intent_json);
|
||||
const legacyRoute = String(intent?.route ?? '').trim();
|
||||
const agentRoute = legacyRoute === 'agent'
|
||||
|| legacyRoute === CHAT_INTENT_ROUTE.AGENT
|
||||
|| legacyRoute === 'agent_orchestration';
|
||||
if (!agentRoute) return null;
|
||||
|
||||
const referenceMs = Math.max(Number(row.updated_at) || 0, Number(row.created_at) || 0);
|
||||
if (referenceMs > 0 && nowMsFn() - referenceMs > ACTIVE_TASK_CONTEXT_MAX_AGE_MS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
hasRecentAgentTask: true,
|
||||
lastRunId: row.id,
|
||||
lastRunStatus: String(row.status ?? '').trim() || null,
|
||||
lastRunFailed: String(row.status ?? '').trim() === 'failed',
|
||||
lastSuggestedSkill: intent?.suggestedSkill ?? null,
|
||||
lastRoute: legacyRoute,
|
||||
lastIntentReason: intent?.reason ?? null,
|
||||
lastErrorMessage: row.error_message
|
||||
? String(row.error_message).slice(0, 500)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveRouterTranscript(sessionSnapshotService, {
|
||||
sessionId,
|
||||
userId = null,
|
||||
excludeLatestUserText = null,
|
||||
} = {}) {
|
||||
if (!sessionSnapshotService?.get || !sessionId || isDirectChatSessionId(sessionId)) {
|
||||
return '';
|
||||
}
|
||||
const snapshot = await sessionSnapshotService.get(sessionId).catch(() => null);
|
||||
if (!snapshot) return '';
|
||||
if (userId && snapshot?.session?.user_id && snapshot.session.user_id !== userId) {
|
||||
return '';
|
||||
}
|
||||
const messages = Array.isArray(snapshot?.messages) ? snapshot.messages : [];
|
||||
return formatRouterTranscript(messages, { excludeLatestUserText });
|
||||
}
|
||||
|
||||
function restoreCursorFallbackUserMessage(userMessage) {
|
||||
const message = (userMessage && typeof userMessage === 'object' && !Array.isArray(userMessage))
|
||||
? { ...userMessage }
|
||||
@@ -1547,13 +1644,29 @@ export function createAgentRunGateway({
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveRunRouting(row, userMessage, runOptions) {
|
||||
async function resolveRunRouting(row, userMessage, runOptions, { runId = null } = {}) {
|
||||
if (!chatIntentRouter?.classify) return null;
|
||||
const enabled = chatIntentRouter.isEnabled
|
||||
? await Promise.resolve(chatIntentRouter.isEnabled()).catch(() => false)
|
||||
: true;
|
||||
if (!enabled) return null;
|
||||
const grantedSkills = await resolveGrantedSkills(row.user_id);
|
||||
const activeTaskContext = await resolveActiveTaskContext(pool, {
|
||||
userId: row.user_id,
|
||||
sessionId: row.agent_session_id ?? null,
|
||||
excludeRunId: runId,
|
||||
}).catch(() => null);
|
||||
const displayText = userMessage?.metadata?.displayText
|
||||
?? deriveUserFacingText(
|
||||
typeof userMessage?.content?.find === 'function'
|
||||
? (userMessage.content.find((item) => item?.type === 'text')?.text ?? '')
|
||||
: '',
|
||||
);
|
||||
const recentTranscript = await resolveRouterTranscript(sessionSnapshotService, {
|
||||
sessionId: row.agent_session_id ?? null,
|
||||
userId: row.user_id,
|
||||
excludeLatestUserText: displayText,
|
||||
}).catch(() => '');
|
||||
return chatIntentRouter.classify({
|
||||
userId: row.user_id,
|
||||
userMessage,
|
||||
@@ -1562,6 +1675,8 @@ export function createAgentRunGateway({
|
||||
toolMode: runOptions.toolMode,
|
||||
forceDeepReasoning: runOptions.forceDeepReasoning,
|
||||
grantedSkills,
|
||||
activeTaskContext,
|
||||
recentTranscript,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1624,7 +1739,7 @@ export function createAgentRunGateway({
|
||||
policyBlocked: true,
|
||||
};
|
||||
}
|
||||
const routing = await resolveRunRouting(row, userMessage, runOptions);
|
||||
const routing = await resolveRunRouting(row, userMessage, runOptions, { runId });
|
||||
const routingDecision = resolveLegacyRouteFromClassification(routing) ?? routing?.route ?? null;
|
||||
const cursorFirstApplied = applyCursorFirstAgentExecution(userMessage, runOptions, {
|
||||
routingDecision,
|
||||
|
||||
Reference in New Issue
Block a user