export const SEARCH_ERROR_CODES = Object.freeze({ CAPABILITY_DISABLED: 'CAPABILITY_DISABLED', INVALID_REQUEST: 'INVALID_REQUEST', PROVIDER_UNAVAILABLE: 'PROVIDER_UNAVAILABLE', UNSAFE_URL: 'UNSAFE_URL' }); export function validateSearchRequest(input = {}) { const query = String(input.query ?? '').trim(); if (!query || query.length > 500) return { ok: false, code: SEARCH_ERROR_CODES.INVALID_REQUEST, message: 'query must be 1-500 characters' }; const limit = Math.max(1, Math.min(20, Number(input.limit ?? 10) || 10)); return { ok: true, value: { query, type: String(input.type ?? 'web'), limit } }; } export function normalizeSearchResult(result = {}, index = 0) { return { title: String(result.title ?? '').trim(), url: String(result.url ?? '').trim(), snippet: String(result.snippet ?? result.content ?? '').trim().slice(0, 2000), source: String(result.source ?? 'unknown'), rank: index + 1 }; } export function buildCitations(results = []) { return results.filter((item) => item.url).map((item, index) => ({ id: `[${index + 1}]`, title: item.title, url: item.url, source: item.source })); } export function isSafeHttpUrl(value) { try { const url = new URL(value); return ['http:', 'https:'].includes(url.protocol) && !['localhost', '127.0.0.1', '::1'].includes(url.hostname); } catch { return false; } }