import { createLogger } from "../logger.js";
import { planExecution } from "./planner.js";
import { executeAgentPlan } from "./executor.js";
import { extractWithSchema } from "../extraction/schema.js";
import {
  requestSampling,
  checkSamplingSupport
} from "../search/sampling.js";
import { isLlmConfigured, runLlmText } from "../integrations/cloud/llm/run.js";
const log = createLogger("agent");
const DEFAULT_MAX_PAGES = 3;
const DEFAULT_MAX_TIME_MS = 6e4;
function getAgentDefaultMaxPages() {
  return DEFAULT_MAX_PAGES;
}
async function runAgentPipeline(input, engines, router, server) {
  const start = Date.now();
  const maxPages = input.max_pages ?? DEFAULT_MAX_PAGES;
  const maxTimeMs = input.max_time_ms ?? DEFAULT_MAX_TIME_MS;
  const deadlineMs = start + maxTimeMs;
  const steps = [];
  try {
    const planStart = Date.now();
    log.info("agent pipeline started", { prompt: input.prompt.slice(0, 100), maxPages, maxTimeMs });
    const plan = await planExecution(input.prompt, input.urls, server);
    steps.push({
      action: "plan",
      detail: `Generated ${plan.searches.length} searches, ${plan.urls.length} URLs${plan.samplingUsed ? " (via sampling)" : " (keyword extraction)"}`,
      time_ms: Date.now() - planStart
    });
    log.info("plan generated", {
      searches: plan.searches.length,
      urls: plan.urls.length,
      samplingUsed: plan.samplingUsed
    });
    const execResult = await executeAgentPlan(plan, engines, router, {
      maxPages,
      deadlineMs
    }, input.prompt);
    steps.push(...execResult.steps);
    const sources = execResult.sources;
    const pagesFetched = sources.filter((s) => s.fetched).length;
    let schemaWarning;
    if (input.schema) {
      const fetchedCount = sources.filter((s) => s.fetched).length;
      if (fetchedCount === 0) {
        schemaWarning = `schema requested but no sources could be fetched \u2014 returning free-text result instead of structured data`;
      } else {
        const extractStart = Date.now();
        const schemaResult = applySchemaExtraction(sources, input.schema);
        steps.push({
          action: "extract",
          detail: `Applied schema extraction to ${fetchedCount} sources`,
          time_ms: Date.now() - extractStart
        });
        if (schemaResult) {
          return {
            result: schemaResult,
            sources,
            pages_fetched: pagesFetched,
            steps,
            total_time_ms: Date.now() - start,
            sampling_supported: !!server && checkSamplingSupport(server)
          };
        }
        schemaWarning = `schema extraction returned no matching fields from ${fetchedCount} fetched sources \u2014 falling back to free-text synthesis`;
      }
    }
    const synthStart = Date.now();
    const { result, samplingUsed, llmUsed } = await synthesizeResult(
      input.prompt,
      sources,
      server
    );
    const resultLen = typeof result === "string" ? result.length : JSON.stringify(result).length;
    const synthPath = samplingUsed ? " (via sampling)" : llmUsed ? " (via configured LLM)" : " (evidence fallback)";
    steps.push({
      action: "synthesize",
      detail: `Produced ${resultLen} char result${synthPath}`,
      time_ms: Date.now() - synthStart
    });
    const partialFailWarning = sources.length > 0 && pagesFetched === 0 ? `fetch failed for all ${sources.length} candidate page(s); no content available for synthesis` : void 0;
    return {
      result,
      sources,
      pages_fetched: pagesFetched,
      steps,
      total_time_ms: Date.now() - start,
      sampling_supported: !!server && checkSamplingSupport(server),
      ...schemaWarning ? { warning: schemaWarning } : partialFailWarning ? { warning: partialFailWarning } : {}
    };
  } catch (err) {
    log.error("agent pipeline failed", {
      prompt: input.prompt.slice(0, 100),
      error: err instanceof Error ? err.message : String(err)
    });
    return {
      result: "",
      sources: [],
      pages_fetched: 0,
      steps,
      total_time_ms: Date.now() - start,
      sampling_supported: !!server && checkSamplingSupport(server),
      error: err instanceof Error ? err.message : String(err)
    };
  }
}
function applySchemaExtraction(sources, schema) {
  try {
    const fetchedSources = sources.filter((s) => s.fetched && s.markdown_content.length > 0);
    if (fetchedSources.length === 0) return null;
    const mergedData = {};
    for (const source of fetchedSources) {
      try {
        const html = `<html><body>${source.markdown_content}</body></html>`;
        const extracted = extractWithSchema(html, schema);
        for (const [key, value] of Object.entries(extracted)) {
          if (value !== void 0 && value !== null && value !== "") {
            if (!(key in mergedData)) {
              mergedData[key] = value;
            }
          }
        }
      } catch (err) {
        log.debug("schema extraction failed for source", {
          url: source.url,
          error: err instanceof Error ? err.message : String(err)
        });
      }
    }
    return Object.keys(mergedData).length > 0 ? mergedData : null;
  } catch (err) {
    log.warn("schema extraction phase failed", {
      error: err instanceof Error ? err.message : String(err)
    });
    return null;
  }
}
async function synthesizeResult(prompt, sources, server) {
  const fetchedSources = sources.filter((s) => s.fetched && s.markdown_content.length > 0);
  if (fetchedSources.length === 0) {
    if (sources.length > 0) {
      const failed = sources.filter((s) => !s.fetched).length;
      const reasonSnippets = sources.map((s) => s.fetch_error).filter((e) => typeof e === "string" && e.length > 0).slice(0, 3);
      const reasonClause = reasonSnippets.length > 0 ? ` (e.g. ${reasonSnippets.join("; ")})` : "";
      return {
        result: `Attempted ${sources.length} page(s) but ${failed} fetch(es) failed${reasonClause}. 0 of ${sources.length} pages yielded content \u2014 try again with a broader query, different URLs, or check upstream availability.`,
        samplingUsed: false
      };
    }
    return { result: "No data could be gathered for this request.", samplingUsed: false };
  }
  if (isLlmConfigured()) {
    try {
      const result = await synthesizeViaLlmRunner(prompt, fetchedSources);
      if (result) return { result, samplingUsed: false, llmUsed: true };
    } catch (err) {
      log.warn("llm runner synthesis failed, falling through to sampling", {
        error: err instanceof Error ? err.message : String(err)
      });
    }
  }
  if (server) {
    try {
      const result = await synthesizeWithSampling(prompt, fetchedSources, server);
      if (result) return { result, samplingUsed: true };
    } catch (err) {
      log.warn("sampling synthesis failed, using evidence fallback", {
        error: err instanceof Error ? err.message : String(err)
      });
    }
  }
  return { result: buildFallbackSynthesis(prompt, fetchedSources), samplingUsed: false };
}
async function synthesizeViaLlmRunner(prompt, sources) {
  const maxCharsPerSource = 3e3;
  const sourceBlocks = sources.map((s, i) => {
    const content = s.markdown_content.slice(0, maxCharsPerSource);
    return `[${i + 1}] ${s.title} (${s.url})
${content}`;
  });
  const truncated = sourceBlocks.join("\n\n").slice(0, 4e4);
  const fullPrompt = `You are a data gathering assistant. Based on the user request and the gathered sources, synthesize a clear, well-organized response. Cite sources as [1], [2], etc.

User request: ${prompt}

Sources:
${truncated}`;
  const r = await runLlmText({ prompt: fullPrompt, maxTokens: 2e3 });
  return r.text && r.text.trim().length > 0 ? r.text.trim() : null;
}
async function synthesizeWithSampling(prompt, sources, server) {
  try {
    const maxCharsPerSource = 3e3;
    const sourceBlocks = sources.map((s, i) => {
      const content = s.markdown_content.slice(0, maxCharsPerSource);
      return `[${i + 1}] ${s.title} (${s.url})
${content}`;
    });
    const totalSourceText = sourceBlocks.join("\n\n");
    const truncatedSourceText = totalSourceText.slice(0, 4e4);
    const samplingPrompt = `You are a data gathering assistant. Based on the user's request and the gathered sources, synthesize a comprehensive result.

User's request: ${prompt}

Gathered sources:
${truncatedSourceText}

Provide a clear, well-organized response that addresses the user's request based on the gathered data. Include source references [1], [2], etc.`;
    if (!checkSamplingSupport(server)) {
      log.debug("client does not support sampling for synthesis");
      return null;
    }
    const response = await requestSampling(
      server,
      [{ role: "user", content: { type: "text", text: samplingPrompt } }],
      2e3
    );
    if (response?.content?.text && response.content.text.trim().length > 0) {
      return response.content.text.trim();
    }
    return null;
  } catch (err) {
    log.debug("sampling synthesis failed", {
      error: err instanceof Error ? err.message : String(err)
    });
    return null;
  }
}
function buildFallbackSynthesis(prompt, sources) {
  const header = `## Results: ${prompt}

Gathered from ${sources.length} source(s):

`;
  let result = header;
  const maxTotal = 6e3;
  let remaining = maxTotal - header.length;
  for (let i = 0; i < sources.length && remaining > 0; i++) {
    const source = sources[i];
    const sourceHeader = `### [${i + 1}] ${source.title}
**URL:** ${source.url}

`;
    if (remaining < sourceHeader.length + 20) break;
    result += sourceHeader;
    remaining -= sourceHeader.length;
    const contentBudget = Math.min(remaining - 10, source.markdown_content.length, 1500);
    if (contentBudget > 0) {
      let content = source.markdown_content.slice(0, contentBudget);
      if (content.length < source.markdown_content.length) {
        content = content.slice(0, Math.max(contentBudget - 3, 0)) + "...";
      }
      result += content + "\n\n";
      remaining -= content.length + 2;
    }
  }
  return result.trimEnd();
}
export {
  getAgentDefaultMaxPages,
  runAgentPipeline
};
//# sourceMappingURL=pipeline.js.map