import {
  OtlpFileTraceStore,
  buildTraceAnalystTools,
  runTraceAnalysisLoop
} from "./chunk-IR3KBHOY.js";

// src/trace-analyst/prompts.ts
var TRACE_ANALYST_ACTOR_DESCRIPTION = `You answer questions about an OTLP-shaped JSONL trace dataset using the trace tools provided in the \`traces\` namespace.

DISCOVERY \u2192 NARROW \u2192 DEEP-READ protocol \u2014 follow exactly:

1. ALWAYS call \`traces.getDatasetOverview({})\` FIRST without a regex_pattern. The result tells you total_traces, raw_jsonl_bytes, services, agents, models, and sample_trace_ids (real ids \u2014 never fabricate one).

2. Use raw_jsonl_bytes to gauge how expensive raw scans will be. \`filters.regex_pattern\` is the one scan-heavy filter on getDatasetOverview / queryTraces / countTraces \u2014 narrow with indexed fields (has_errors, model_names, service_names, agent_names, time bounds) BEFORE adding a regex on a large dataset.

3. To list more traces than the sample, call \`traces.queryTraces({ filters?, limit, offset? })\`. Each summary carries raw_jsonl_bytes \u2014 use it to choose between viewTrace and searchTrace BEFORE calling either.

4. Per-trace inspection:
   - SMALL trace (raw_jsonl_bytes well under 150_000): call \`traces.viewTrace({ trace_id })\`. Returns all spans. Per-attribute payloads are head-capped at ~4KB; large \`input.value\` / \`output.value\` / \`llm.input_messages\` will show a \`[trace-analyst truncated: N bytes]\` marker.
   - LARGE trace (raw_jsonl_bytes near or above 150_000, or you saw an \`oversized\` response): use \`traces.searchTrace({ trace_id, regex_pattern })\` to get bounded SpanMatchRecords (span metadata + matched text + surrounding context). Then call \`traces.viewSpans({ trace_id, span_ids: [...] })\` for surgical reads (~16KB cap, 4\xD7 higher than discovery), or \`traces.searchSpan({ trace_id, span_id, regex_pattern })\` for one large span. Stays bounded regardless of trace size.
   - Useful regex patterns: \`STATUS_CODE_ERROR\` (failures), tool names like \`grep\` or \`view_trace\`, error strings like \`MaxTurnsExceeded\`, model names, attribute keys.

5. ONLY call viewTrace / viewSpans / searchTrace / searchSpan with trace/span ids you have already seen in sample_trace_ids, a queryTraces page, or a previous search result. Never invent ids.

5a. **Result-shape contract** \u2014 searchTrace and searchSpan return \`{ trace_id, hits, total_matches, has_more }\`. Iterate \`result.hits\` (NOT result.matches). Each hit has \`{ span_id, span_name, span_kind, attribute_path, matched_text, context_before, context_after, match_offset }\`. viewTrace returns \`{ trace_id, spans }\` (or \`oversized\`). viewSpans returns \`{ trace_id, spans, missing_span_ids, truncated_attribute_count }\`. Never assume a field name \u2014 log the result shape first if unsure.

6. If viewTrace returns an \`oversized\` summary instead of \`spans\`, DO NOT retry the same call. Read the summary's top_span_names, span_count, span_response_bytes_max, error_span_count to plan a follow-up: switch to searchTrace (or searchSpan for one large span), then viewSpans on a smaller, surgical span_ids set.

7. If searchTrace or searchSpan returns has_more=true, REFINE the regex to be more specific rather than blindly raising max_matches.

8. If a tool errors (invalid regex, range error), STOP and reconsider \u2014 don't retry with a guessed id or argument. Use the discovery tools above to recover.

9. If a ~4KB-truncated payload from viewTrace / searchTrace matters for your answer, first try viewSpans on that span id (~16KB cap). If a 16KB-truncated payload from viewSpans still matters, narrow further with searchSpan against a more specific regex rather than asking for the full payload again.

10. If the question splits into independent reasoning branches, use bounded \`llmQuery(...)\` calls over evidence you already loaded. Subqueries cannot inspect the trace store, so pass the exact trace excerpts they need. Example:

    const reviews = await llmQuery([
      { query: 'Classify the failure mechanism in this excerpt.', context: traceAbcExcerpt },
      { query: 'Classify the failure mechanism in this excerpt.', context: traceDefExcerpt },
    ]);

OBSERVABILITY rules:
- Each discovery turn must emit at least one concise \`console.log(...)\` showing what evidence was learned.
- Finish gathering evidence before submitting the analysis.
- Reuse runtime variables across turns; don't recompute.

OUTPUT contract \u2014 your final answer must include:
- A clear prose conclusion answering the user's question.
- Trace ids and span ids cited as evidence for each claim.
- Failure modes named in the user's domain language, with frequency and concrete examples.
- A concise findings array containing only claims supported by inspected evidence.

Do NOT invent trace ids, span ids, error messages, or model names. Every fact must be traceable to a tool result.`;
var TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION = "trace-analyst-actor-v6-2026-07-14";

// src/trace-analyst/analyst.ts
async function analyzeTraces(input, options) {
  if (!input.question || typeof input.question !== "string") {
    throw new TypeError("analyzeTraces: input.question must be a non-empty string");
  }
  rejectRemovedOptions(options);
  const store = typeof options.source === "string" ? new OtlpFileTraceStore({ path: options.source }) : options.source;
  if (store instanceof OtlpFileTraceStore) {
    await store.ensureIndexed();
  }
  const tools = buildTraceAnalystTools({ store });
  const turns = [];
  let progressFs;
  if (options.progressLogPath) {
    const { createWriteStream } = await import("fs");
    const { mkdir } = await import("fs/promises");
    const { dirname } = await import("path");
    await mkdir(dirname(options.progressLogPath), { recursive: true });
    progressFs = createWriteStream(options.progressLogPath, { flags: "a" });
  }
  const actorTurnCallback = async (turn) => {
    const snap = {
      stage: turn.stage,
      turn: turn.turn,
      isError: turn.isError,
      code: turn.code,
      output: turn.output,
      thought: turn.thought
    };
    turns.push(snap);
    if (progressFs) {
      try {
        progressFs.write(`${JSON.stringify({ ...snap, ts: Date.now() })}
`);
      } catch {
      }
    }
    if (options.onTurn) await options.onTurn(snap);
  };
  const maxSubqueries = options.maxSubqueries ?? 4;
  const maxTurns = options.maxTurns ?? 12;
  const maxParallelSubqueries = options.maxParallelSubqueries ?? 2;
  const maxRuntimeChars = options.maxRuntimeChars ?? 6e3;
  let completed;
  try {
    completed = await runTraceAnalysisLoop({
      id: "TraceAnalyst",
      description: "Analyzes OTLP-shaped JSONL traces using bounded discovery tools to identify systemic failure modes.",
      prompt: `${options.actorDescription ?? TRACE_ANALYST_ACTOR_DESCRIPTION}

The report must answer the user's question, and findings must be an array of concise evidence-backed strings.`,
      question: input.question,
      ai: options.ai,
      ...options.model ? { model: options.model } : {},
      tools,
      findingType: "string",
      maxSubqueries,
      maxParallelSubqueries,
      maxTurns,
      maxRuntimeChars,
      ...options.signal ? { signal: options.signal } : {},
      onTurn: actorTurnCallback
    });
  } finally {
    if (progressFs) {
      await new Promise((resolve) => progressFs.end(() => resolve()));
    }
  }
  return {
    answer: completed.report,
    findings: completed.findings,
    turns,
    turnCount: turns.length,
    usage: { actor: normalizeRecordArray(completed.usage), responder: [] },
    chatLog: { actor: normalizeRecordArray(completed.chatLog), responder: [] },
    actorPromptVersion: TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION
  };
}
function rejectRemovedOptions(options) {
  const supplied = options;
  const migrations = [
    ["maxDepth", "maxSubqueries"],
    ["maxParallelSubagents", "maxParallelSubqueries"],
    ["subagentDescription", "actorDescription"]
  ];
  for (const [removed, replacement] of migrations) {
    if (removed in supplied) {
      throw new TypeError(`analyzeTraces: '${removed}' is unsupported; use '${replacement}'`);
    }
  }
}
function normalizeRecordArray(value) {
  if (!Array.isArray(value)) return [];
  return value.map(
    (item) => item && typeof item === "object" ? { ...item } : { value: item }
  );
}

export {
  TRACE_ANALYST_ACTOR_DESCRIPTION,
  TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION,
  analyzeTraces
};
//# sourceMappingURL=chunk-NUKSVU3W.js.map