export interface RawFlow { _key: string; display_name?: string; name?: string; description?: string; source_context?: string; status?: string; kind?: string; } const DEV_ARTEFACT_PATTERNS = [ /^rv[_\s]/i, /^orphan/i, /^atlas[_\s]?f1/i, /\batlas-f1-fresh\b/i, /mcp[_\s]?sdx[_\s]?smoke/i, /\bcodex[_\s]?test\b/i, /\bsidekick\b/i, /^process_\d{10,}$/i, /\bbackfill\b/i, /\bprobe\b/i, /\bfixture\b/i, ]; const NON_BUSINESS_SOURCE_CONTEXTS = new Set([ "EA2_CHAT_THREAD", "EA2_CHAT_MSG", "CANVAS_CHAT_THREAD", "CANVAS_CHAT_MSG", "CANVAS_CHAT_INBOX", "CANVAS_ATTENDANCE_ROOT", "CANVAS_ATTENDANCE_SITE", "CANVAS_AGENT_MEMORY", "CANVAS_AGENT_VAULT_ROOT", "CANVAS_SEED_PROCESS", "CANVAS_SEED_STEP", "EA2_WIZARD_STEP", ]); export const STARTABLE_FLOW_KEYS = new Set([ "pr_to_po_def", ]); const STARTABLE_SOURCE_CONTEXTS = new Set([ "EA2_DRAFT_PROCESS:process_creation", "fm06-t10-demo-reset-v2", ]); const HEX32 = /[a-f0-9]{32}/gi; const HEX_SUFFIX = /[_-][a-f0-9]{8,}$/i; export function isDevArtefact(f: RawFlow): boolean { if (f.source_context && NON_BUSINESS_SOURCE_CONTEXTS.has(f.source_context)) return true; const haystack = `${f.display_name || ""} ${f.name || ""} ${f.description || ""}`; return DEV_ARTEFACT_PATTERNS.some((p) => p.test(haystack)); } export function curatedPublishedFlows(items: T[]): T[] { return items.filter( (it) => it.status === "published" && it.kind === "definition" && !!it.display_name && !isDevArtefact(it) && (STARTABLE_FLOW_KEYS.has(it._key) || (it.source_context && STARTABLE_SOURCE_CONTEXTS.has(it.source_context))) ); } export async function fetchStartableFlows(baseUrl: string, token: string): Promise { const results = await Promise.all( Array.from(STARTABLE_FLOW_KEYS).map(async (key) => { try { const r = await fetch(`${baseUrl}/api/ea2/flow/${key}`, { headers: { Authorization: `Bearer ${token}` } }); if (!r.ok) return null; const doc = await r.json(); if (doc.kind !== "definition" || doc.status !== "published" || !doc.display_name) return null; return doc as RawFlow; } catch { return null; } }) ); return results.filter((d): d is RawFlow => !!d); } export function friendlyShortId(id: string | undefined | null): string { if (!id) return ""; const trimmed = id.replace(HEX_SUFFIX, ""); if (trimmed.length < id.length && trimmed.length > 0) return trimmed; if (HEX32.test(id)) return `ref-${id.slice(0, 4)}`; return id; } export function scrubIdsFromText(text: string): string { return text.replace(HEX32, ""); }