import { isGenericPageGenerationRequest, isPageGenerationIntent, } from './chat-skills.mjs'; import { cursorExecutorEnabled, resolvePreferredCodeExecutor, } from './cursor-agent-launch.mjs'; import { enforcePageGenerationCursorRuntime } from './cursor-page-routing.mjs'; function envFlag(value, fallback = false) { const raw = String(value ?? '').trim().toLowerCase(); if (!raw) return fallback; return ['1', 'true', 'yes', 'on'].includes(raw); } export function cursorPageTimeoutTakeoverEnabled(env = process.env) { return envFlag(env.MEMIND_CURSOR_PAGE_TIMEOUT_TAKEOVER, false); } function extractRunDisplayText(row) { const message = row?.user_message_json; const parsed = typeof message === 'string' ? (() => { try { return JSON.parse(message); } catch { return {}; } })() : (message ?? {}); const displayText = String(parsed?.metadata?.displayText ?? '').trim(); if (displayText) return displayText; const content = parsed?.content; if (typeof content === 'string') return content.trim(); if (Array.isArray(content)) { return content .filter((item) => item?.type === 'text') .map((item) => String(item.text ?? '').trim()) .filter(Boolean) .join('\n') .trim(); } return ''; } function getRunOptionsFromMessage(userMessage) { const metadata = userMessage?.metadata; const runMetadata = metadata?.memindRun ?? metadata?.agentRun ?? {}; return { toolMode: String(runMetadata?.toolMode ?? metadata?.toolMode ?? 'chat').trim().toLowerCase(), taskType: runMetadata?.taskType ?? metadata?.taskType ?? null, forceDeepReasoning: runMetadata?.forceDeepReasoning === true || metadata?.forceDeepReasoning === true, }; } function cursorExecutorAvailable(toolGatewayStatus, env = process.env) { if (!cursorExecutorEnabled(env)) return false; if (resolvePreferredCodeExecutor(env) !== 'cursor') return false; if (!toolGatewayStatus?.enabled) return false; const executors = Array.isArray(toolGatewayStatus.executors) ? toolGatewayStatus.executors.map((item) => String(item).trim().toLowerCase()) : []; return executors.includes('cursor'); } export function shouldRetryPageGenerationWithCursor({ row, error = null, toolGatewayStatus = null, env = process.env, } = {}) { if (!cursorPageTimeoutTakeoverEnabled(env)) return false; if (String(error?.code ?? '') !== 'AGENT_RUN_TIMEOUT') return false; if (!cursorExecutorAvailable(toolGatewayStatus, env)) return false; const userMessage = typeof row?.user_message_json === 'string' ? (() => { try { return JSON.parse(row.user_message_json); } catch { return {}; } })() : (row?.user_message_json ?? {}); const runMetadata = userMessage?.metadata?.memindRun ?? userMessage?.metadata?.agentRun ?? {}; if (runMetadata.pageCursorDefault === true || runMetadata.cursorTimeoutTakeover === true) { return false; } if (String(runMetadata.requiredExecutor ?? runMetadata.executor ?? '').trim().toLowerCase() === 'cursor') { return false; } const taskText = extractRunDisplayText(row); if (!isPageGenerationIntent(taskText) || isGenericPageGenerationRequest(taskText)) { return false; } return true; } export function buildCursorPageTimeoutTakeover(row, { env = process.env } = {}) { const userMessage = typeof row?.user_message_json === 'string' ? (() => { try { return JSON.parse(row.user_message_json); } catch { return {}; } })() : (row?.user_message_json ?? {}); const runOptions = getRunOptionsFromMessage(userMessage); const enforced = enforcePageGenerationCursorRuntime(userMessage, { rawToolMode: runOptions.toolMode, taskType: runOptions.taskType, forceDeepReasoning: runOptions.forceDeepReasoning ?? true, env, channelEligible: true, policy: null, }); if (!enforced.requiredExecutor) return null; const metadata = enforced.userMessage?.metadata ?? {}; const memindRun = metadata.memindRun ?? {}; const updatedMessage = { ...enforced.userMessage, metadata: { ...metadata, memindRun: { ...memindRun, cursorTimeoutTakeover: true, }, }, }; return { userMessage: updatedMessage, runOptions: { toolMode: enforced.rawToolMode, taskType: enforced.taskType, forceDeepReasoning: enforced.forceDeepReasoning, requiredExecutor: enforced.requiredExecutor, }, }; } export function resolveCursorPageTimeoutTakeover(input) { if (!shouldRetryPageGenerationWithCursor(input)) return null; return buildCursorPageTimeoutTakeover(input.row, { env: input.env }); }