import type { AiAgentMessage } from './aiAgentConversation' interface BrowserNodePreview { nodeId: string text: string } export interface BrowserObservation { documentId?: string url: string totalCharacters?: number totalNodes?: number nodes: BrowserNodePreview[] status: 'pending' | 'done' | 'error' } function parseObject(value?: string): Record | null { if (!value) return null try { const parsed: unknown = JSON.parse(value) return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? parsed as Record : null } catch { return null } } function stringField(object: Record | null, ...keys: string[]): string | undefined { for (const key of keys) { const value = object?.[key] if (typeof value === 'string' && value.trim()) return value.trim() } return undefined } function numberField(object: Record | null, ...keys: string[]): number | undefined { for (const key of keys) { const value = object?.[key] if (typeof value === 'number' && Number.isFinite(value)) return value } return undefined } function browserAction(action: AiAgentMessage['actions'][number]): boolean { if (action.tool === 'read_web_page' || action.tool === 'read_web_page_nodes') return true const input = parseObject(action.input) if (action.tool !== 'magic_brush') return false const steps = input?.steps return Array.isArray(steps) && steps.some((step) => ( typeof step === 'object' && step !== null && ['read_web_page', 'read_web_page_nodes'].includes(String((step as Record).tool)) )) } function previewNodes(output: Record | null): BrowserNodePreview[] { const raw = output?.node_previews ?? output?.nodes if (!Array.isArray(raw)) return [] return raw.flatMap((item) => { if (typeof item !== 'object' || item === null) return [] const node = item as Record const nodeId = stringField(node, 'node_id', 'nodeId') const text = stringField(node, 'text') return nodeId && text ? [{ nodeId, text }] : [] }) } export function latestBrowserObservation(messages: AiAgentMessage[]): BrowserObservation | null { const actions = messages.flatMap((message) => message.actions).filter(browserAction) const action = actions.at(-1) if (!action) return null const input = parseObject(action.input) const output = parseObject(action.output) const url = stringField(output, 'url') ?? stringField(input, 'url') if (!url) return null return { documentId: stringField(output, 'document_id', 'documentId'), url, totalCharacters: numberField(output, 'total_characters', 'totalCharacters'), totalNodes: numberField(output, 'total_nodes', 'totalNodes'), nodes: previewNodes(output), status: action.status, } }