export type AgentRunCreateOptions = { toolMode?: 'chat' | 'code'; taskType?: string | null; validation?: AgentRunValidation | null; validationInstruction?: string | null; }; export type AgentRunValidationFile = { path: string; contains?: string; }; export type AgentRunValidation = { expectedFile?: AgentRunValidationFile | string; expectedFiles?: Array; }; function envFlag(value: unknown): boolean { const normalized = String(value ?? '').trim().toLowerCase(); return ['1', 'true', 'yes', 'on'].includes(normalized); } export const agentCodeRunsEnabled = envFlag(import.meta.env.VITE_AGENT_CODE_RUNS_ENABLED); export const agentCodeRunsAutodetectEnabled = envFlag( import.meta.env.VITE_AGENT_CODE_RUNS_AUTODETECT, ); function parseUserIdSet(value: unknown): Set { return new Set( String(value ?? '') .split(',') .map((item) => item.trim()) .filter(Boolean), ); } const agentCodeRunUserIds = parseUserIdSet(import.meta.env.VITE_AGENT_CODE_RUNS_USER_IDS); export function agentCodeRunsEnabledForUser(userId?: string | null): boolean { if (!agentCodeRunsEnabled) return false; if (agentCodeRunUserIds.size === 0) return true; return Boolean(userId && agentCodeRunUserIds.has(userId)); } const CODE_TASK_PATTERNS = [ /\b(repo|repository|branch|commit|pull request|pr|diff|patch)\b/i, /\b(aider|openhands|codex|codebase|workspace)\b/i, /\b(refactor|debug|fix bug|implement|add test|unit test)\b/i, /(修改|修复|重构|调试|实现|新增|编写|更新).{0,12}(代码|仓库|分支|文件|测试|组件|接口)/, /(代码|仓库|项目|文件|组件|接口).{0,12}(修改|修复|重构|调试|实现|新增|编写|更新)/, ]; function sanitizeRequestIdForPath(requestId: string): string { const normalized = String(requestId ?? '').trim().replace(/[^a-zA-Z0-9._-]/g, '-'); return normalized || 'unknown-request'; } export function buildAgentRunValidationReceipt(requestId: string): { validation: AgentRunValidation; instruction: string; } { const normalizedRequestId = String(requestId ?? '').trim(); const safeRequestId = sanitizeRequestIdForPath(normalizedRequestId); const receiptPath = `.memind/agent-runs/${safeRequestId}.json`; const marker = normalizedRequestId || safeRequestId; return { validation: { expectedFile: { path: receiptPath, contains: marker, }, }, instruction: [ '', '[Memind code-run validation]', `Before finishing, create or update ${receiptPath}.`, `The file must be valid JSON and include this requestId value: ${marker}.`, 'Do not skip this receipt even if the user task itself is complete.', ].join('\n'), }; } export function resolveAgentRunOptions( text: string, { taskType = 'code_task', forceCode = false, allowAutodetect = agentCodeRunsAutodetectEnabled, userId = null, requestId = null, }: { taskType?: string; forceCode?: boolean; allowAutodetect?: boolean; userId?: string | null; requestId?: string | null; } = {}, ): AgentRunCreateOptions { if (!agentCodeRunsEnabledForUser(userId)) return {}; const normalizedText = String(text ?? '').trim(); const shouldUseCode = forceCode || (allowAutodetect && CODE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText))); if (!shouldUseCode) return {}; const receipt = buildAgentRunValidationReceipt(requestId ?? crypto.randomUUID()); return { toolMode: 'code', taskType, validation: receipt.validation, validationInstruction: receipt.instruction, }; }