export function normalizeCommandText(text: unknown) { return String(text || '') .trim() .toLowerCase() .replace(/[!?.]+$/g, ''); } export function isAbortCommand(text: unknown) { const normalized = normalizeCommandText(text); if (!normalized) { return false; } const commands = new Set(['abort', 'cancel', 'stop', '/abort', '/cancel', '/stop']); if (commands.has(normalized)) { return true; } const compact = normalized.replace(/\s+/g, ' '); return compact === 'please abort' || compact === 'please cancel' || compact === 'please stop'; } export function isAbortAllCommand(text: unknown) { const normalized = normalizeCommandText(text); if (!normalized) { return false; } const commands = new Set([ 'abort all', 'cancel all', 'stop all', '/abortall', '/cancelall', '/stopall', 'abort all jobs', 'abort async', '/abort async', ]); if (commands.has(normalized)) { return true; } const compact = normalized.replace(/\s+/g, ' '); return compact.includes('abort all') || compact.includes('cancel all') || compact.includes('stop all'); } export function isShutdownCommand(text: unknown) { const normalized = normalizeCommandText(text); if (!normalized) { return false; } const commands = new Set([ 'shutdown', '/shutdown', 'shutdown agent', 'kill agent', 'please shutdown', 'shutdown the agent', 'shutdown all', 'kill all', 'shutdown everything', 'nuke', '/nuke', 'nuke all', 'nuke everything', '/killall', ]); return commands.has(normalized); } export function isResetCommand(text: unknown) { const normalized = normalizeCommandText(text); if (!normalized) { return false; } const commands = new Set(['reset', '/reset', 'restart agent', 'reset agent', 'restart cli']); return commands.has(normalized); } export function normalizeOutgoingText(text: unknown) { const rawText = String(text || '').trim(); return stripThinkingProcess(rawText); } /** * Strips internal AI thinking process markers from the output text. * This is useful for cleaning up responses from models that include * reasoning steps or internal progress logs in their output. */ export function stripThinkingProcess(text: string): string { if (!text) return ''; let result = text; // Remove ... blocks (common in many reasoning models) result = result.replace(/[\s\S]*?<\/thought>/gi, ''); // Remove Thinking... markers often found at the beginning of output (flexible dot count) result = result.replace(/^Thinking\.+\s*$/gm, ''); // Remove (Thinking: ...) and [Thinking: ...] blocks result = result.replace(/\(Thinking: [\s\S]*?\)/gi, ''); result = result.replace(/\[Thinking: [\s\S]*?\]/gi, ''); return result.trim(); } /** * Generates a random short identifier using base36. * Provides 8 characters of entropy, suitable for job references and correlation IDs. * * @returns A random 8-character string (e.g., "5g7h2k9z") */ export function generateShortId() { return Math.random().toString(36).substring(2, 10); }