/** * PromptQL executor — AgentMessage event-tree text extraction. * * thread_events rows carry a deeply-nested `event_data` blob; the assistant's * final reply lives at a `final_response.message` leaf (or, on older builds, * embedded as `` XML inside `response_text`). */ export function walkStrings( node: unknown, out: Array<{ path: string; text: string }> = [], path = "" ): Array<{ path: string; text: string }> { if (node == null) return out; if (typeof node === "string") { if ( node.length >= 1 && !/^[0-9a-f-]{36}$/i.test(node) && !/^\d{4}-\d{2}-\d{2}T/.test(node) ) { out.push({ path, text: node }); } return out; } if (Array.isArray(node)) { node.forEach((v, i) => walkStrings(v, out, `${path}[${i}]`)); return out; } if (typeof node === "object") { for (const [k, v] of Object.entries(node as Record)) { walkStrings(v, out, path ? `${path}.${k}` : k); } } return out; } export function extractFinalResponseMessage(eventData: unknown): string | null { const hits = walkStrings(eventData).filter((t) => /final_response\.message$/i.test(t.path)); if (hits.length) return hits[hits.length - 1]!.text; // response_text XML fallback const raw = walkStrings(eventData).find((t) => /response_text$/i.test(t.path)); if (raw) { const m = raw.text.match(/\s*([\s\S]*?)\s*<\/final_response>/i); if (m) return m[1]!.trim(); } return null; } export function isFinalAgentEvent(eventData: unknown): boolean { const s = JSON.stringify(eventData || {}); if (s.includes("final_response_sent")) return true; return Boolean(extractFinalResponseMessage(eventData)); } export function eventKind(eventData: unknown): string { if (!eventData || typeof eventData !== "object") return "unknown"; return Object.keys(eventData as object)[0] || "unknown"; }