export const GOAL_RUN_INTENT_PATTERNS = [ /(?:长期目标|当前目标|目标是|计划要|正在建设|准备实现|希望长期)/i, /(?:分阶段|多步骤|长期任务|持续跟进|帮我规划并执行)/i, ]; export function isGoalRunIntent(text) { const normalized = String(text ?? '').trim(); if (!normalized) return false; return GOAL_RUN_INTENT_PATTERNS.some((pattern) => pattern.test(normalized)); } function envFlag(value, fallback = false) { const raw = String(value ?? '').trim().toLowerCase(); if (!raw) return fallback; return ['1', 'true', 'yes', 'on'].includes(raw); } function parseUserIdSet(raw) { return new Set( String(raw ?? '') .split(/[,;\s]+/) .map((item) => item.trim()) .filter(Boolean), ); } export function isGoalRunEnabledForUser(userId, env = process.env) { if (!envFlag(env.GOAL_RUN_ENABLED, false)) return false; const canaryUserIds = parseUserIdSet(env.GOAL_RUN_CANARY_USER_IDS); if (!canaryUserIds.size) return true; return canaryUserIds.has(String(userId ?? '').trim()); } export function extractUserMessageDisplayText(userMessage) { if (!userMessage) return ''; if (typeof userMessage === 'string') return userMessage.trim(); const metadataText = String(userMessage?.metadata?.displayText ?? '').trim(); if (metadataText) return metadataText; const content = userMessage?.content; if (typeof content === 'string') return content.trim(); if (Array.isArray(content)) { for (const item of content) { if (typeof item === 'string' && item.trim()) return item.trim(); if (item?.type === 'text' && String(item.text ?? '').trim()) { return String(item.text).trim(); } } } return ''; } export function deriveGoalTitleFromText(text, maxLength = 80) { const normalized = String(text ?? '').replace(/\s+/g, ' ').trim(); if (!normalized) return '长期任务'; return normalized.length <= maxLength ? normalized : `${normalized.slice(0, Math.max(0, maxLength - 1))}…`; } export function buildGoalContinueUserMessage(feedback = null, { id = null } = {}) { const displayText = String(feedback ?? '').trim() ? `继续下一阶段。补充说明:${String(feedback).trim()}` : '继续执行下一阶段'; const messageId = id ?? (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `goal-continue-${Date.now()}`); return { id: messageId, role: 'user', content: [{ type: 'text', text: displayText }], metadata: { userVisible: true, displayText, }, }; }