import {
  extractLlmCallEvent
} from "./chunk-4J6RBI3K.js";
import {
  AnalystError
} from "./chunk-YEJR7IXO.js";

// src/analyst-loop/iterations-to-trace-store.ts
import {
  DEFAULT_TRACE_ANALYST_BUDGETS,
  TRACE_ANALYST_TRUNCATION_MARKER_PREFIX
} from "@tangle-network/agent-eval";
var bytesOf = (v) => Buffer.byteLength(JSON.stringify(v) ?? "", "utf8");
var iso = (ms) => new Date(ms).toISOString();
function normalizeSignature(message) {
  return message.replace(/0x[0-9a-fA-F]+/g, "HEX").replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, "UUID").replace(/(\/[\w.-]+){2,}/g, "PATH").replace(/\b\d+(\.\d+)?(ms|s|m|h)\b/g, "DUR").replace(/\b\d+\b/g, "#").replace(/\s+/g, " ").trim().slice(0, 200);
}
function spanKindFor(event, agentRunName) {
  const llm = extractLlmCallEvent(event, agentRunName);
  if (llm) return { kind: "LLM", model: llm.model ?? null, tool: null };
  const type = String(event?.type ?? "");
  if (/tool/i.test(type)) {
    const d = event?.data;
    const tool = typeof d?.name === "string" ? d.name : typeof d?.tool === "string" ? d.tool : type;
    return { kind: "TOOL", model: null, tool };
  }
  return { kind: "SPAN", model: null, tool: null };
}
function errorMessageOf(event) {
  const type = String(event?.type ?? "");
  const d = event?.data;
  if (/error|fail/i.test(type) || d?.error) {
    const m = d?.error ?? d?.message;
    return typeof m === "string" ? m : `${type} error`;
  }
  return void 0;
}
function projectIteration(iter) {
  const traceId = iter.events.find((e) => e?.data?.sandboxId)?.data?.sandboxId ?? `iter-${iter.index}`;
  const start = iso(iter.startedAt);
  const end = iso(iter.endedAt || iter.startedAt);
  const durationMs = Math.max(0, (iter.endedAt || iter.startedAt) - iter.startedAt);
  const rootId = `${traceId}:root`;
  const iterErrored = Boolean(iter.error) || iter.verdict?.valid === false;
  const spans = [
    {
      trace_id: traceId,
      span_id: rootId,
      parent_span_id: null,
      name: iter.agentRunName,
      kind: "AGENT",
      start_time: start,
      end_time: end,
      duration_ms: durationMs,
      status: iter.error ? "ERROR" : "OK",
      status_message: iter.error?.message,
      service_name: "agent-runtime",
      agent_name: iter.agentRunName,
      model_name: null,
      tool_name: null,
      attributes: {
        "iteration.index": iter.index,
        "verdict.valid": iter.verdict?.valid,
        "verdict.score": iter.verdict?.score,
        "output.preview": iter.output === void 0 ? void 0 : String(iter.output).slice(0, 2e3)
      }
    }
  ];
  const models = /* @__PURE__ */ new Set();
  const tools = /* @__PURE__ */ new Set();
  iter.events.forEach((event, i) => {
    const { kind, model, tool } = spanKindFor(event, iter.agentRunName);
    if (model) models.add(model);
    if (tool) tools.add(tool);
    const errMsg = errorMessageOf(event);
    spans.push({
      trace_id: traceId,
      span_id: `${traceId}:e${i}`,
      parent_span_id: rootId,
      name: String(event?.type ?? "event"),
      kind,
      start_time: start,
      end_time: end,
      duration_ms: 0,
      status: errMsg ? "ERROR" : "OK",
      status_message: errMsg,
      service_name: "agent-runtime",
      agent_name: iter.agentRunName,
      model_name: model,
      tool_name: tool,
      attributes: event?.data ?? {}
    });
  });
  const hasErrors = spans.some((s) => s.status === "ERROR") || iterErrored;
  const summary = {
    trace_id: traceId,
    service_name: "agent-runtime",
    agent_name: iter.agentRunName,
    span_count: spans.length,
    has_errors: hasErrors,
    start_time: start,
    end_time: end,
    duration_ms: durationMs,
    raw_jsonl_bytes: bytesOf(spans),
    models: [...models],
    tools: [...tools]
  };
  return { summary, spans, rawBytes: summary.raw_jsonl_bytes };
}
function matchesFilters(t, f) {
  if (!f) return true;
  if (f.has_errors !== void 0 && t.summary.has_errors !== f.has_errors) return false;
  if (f.service_names?.length && !f.service_names.includes(t.summary.service_name ?? ""))
    return false;
  if (f.agent_names?.length && !f.agent_names.includes(t.summary.agent_name ?? "")) return false;
  if (f.model_names?.length && !f.model_names.some((m) => t.summary.models.includes(m)))
    return false;
  if (f.tool_names?.length && !f.tool_names.some((tn) => t.summary.tools.includes(tn))) return false;
  if (f.start_time_after && t.summary.start_time < f.start_time_after) return false;
  if (f.start_time_before && t.summary.start_time > f.start_time_before) return false;
  if (f.regex_pattern && !new RegExp(f.regex_pattern).test(JSON.stringify(t.spans))) return false;
  return true;
}
function capAttributes(attributes, perAttrCap) {
  let truncated = 0;
  const capped = {};
  for (const [k, v] of Object.entries(attributes)) {
    const s = typeof v === "string" ? v : JSON.stringify(v);
    if (typeof s === "string" && s.length > perAttrCap) {
      truncated += 1;
      capped[k] = `${TRACE_ANALYST_TRUNCATION_MARKER_PREFIX} ${s.length}b]${s.slice(0, perAttrCap)}`;
    } else {
      capped[k] = v;
    }
  }
  return { capped, truncated };
}
function iterationsToTraceStore(iterations, budgets = DEFAULT_TRACE_ANALYST_BUDGETS) {
  if (iterations.length === 0) {
    throw new AnalystError("iterationsToTraceStore: no iterations to analyze (empty round)");
  }
  const traces = iterations.map((it) => projectIteration(it));
  const byId = new Map(traces.map((t) => [t.summary.trace_id, t]));
  const buildClusters = (set) => {
    const map = /* @__PURE__ */ new Map();
    for (const t of set) {
      for (const s of t.spans) {
        if (s.status !== "ERROR" || !s.status_message) continue;
        const sig = normalizeSignature(s.status_message);
        const c = map.get(sig) ?? {
          signature: sig,
          status_message_sample: s.status_message,
          span_name: s.name,
          tool_name: s.tool_name,
          trace_count: 0,
          span_count: 0,
          prevalence: 0,
          exemplar_trace_ids: [],
          exemplar_span_ids: []
        };
        c.span_count += 1;
        if (!c.exemplar_trace_ids.includes(t.summary.trace_id) && c.exemplar_trace_ids.length < 10) {
          c.exemplar_trace_ids.push(t.summary.trace_id);
          c.trace_count += 1;
        }
        if (c.exemplar_span_ids.length < 10) c.exemplar_span_ids.push(s.span_id);
        map.set(sig, c);
      }
    }
    const errorTraces = set.filter((t) => t.summary.has_errors).length || 1;
    const clusters = [...map.values()].map((c) => ({
      ...c,
      prevalence: c.trace_count / errorTraces
    }));
    return clusters.sort((a, b) => b.trace_count - a.trace_count);
  };
  return {
    async getOverview(filters) {
      const set = traces.filter((t) => matchesFilters(t, filters));
      const services = /* @__PURE__ */ new Set();
      const agents = /* @__PURE__ */ new Set();
      const models = /* @__PURE__ */ new Set();
      const tools = /* @__PURE__ */ new Set();
      let errorSpans = 0;
      for (const t of set) {
        if (t.summary.service_name) services.add(t.summary.service_name);
        if (t.summary.agent_name) agents.add(t.summary.agent_name);
        for (const m of t.summary.models) models.add(m);
        for (const tn of t.summary.tools) tools.add(tn);
        errorSpans += t.spans.filter((s) => s.status === "ERROR").length;
      }
      const times = set.map((t) => t.summary.start_time).sort();
      return {
        total_traces: set.length,
        raw_jsonl_bytes: set.reduce((n, t) => n + t.rawBytes, 0),
        services: [...services],
        agents: [...agents],
        models: [...models],
        tool_names: [...tools],
        sample_trace_ids: set.slice(0, 20).map((t) => t.summary.trace_id),
        errors: {
          trace_count: set.filter((t) => t.summary.has_errors).length,
          span_count: errorSpans
        },
        error_clusters: buildClusters(set),
        time_range: times.length ? { earliest: times[0], latest: times[times.length - 1] } : null
      };
    },
    async queryTraces(opts) {
      const set = traces.filter((t) => matchesFilters(t, opts.filters));
      const offset = opts.offset ?? 0;
      const page = set.slice(offset, offset + opts.limit);
      return {
        traces: page.map((t) => t.summary),
        total: set.length,
        has_more: offset + opts.limit < set.length
      };
    },
    async countTraces(filters) {
      return traces.filter((t) => matchesFilters(t, filters)).length;
    },
    async viewTrace(opts) {
      const t = byId.get(opts.trace_id);
      if (!t) return { trace_id: opts.trace_id, spans: [] };
      const cap = opts.per_attribute_byte_cap ?? budgets.perAttributeViewBudget;
      const projected = t.spans.map((s) => ({
        ...s,
        attributes: capAttributes(s.attributes, cap).capped
      }));
      if (bytesOf(projected) > budgets.perCallByteCeiling) {
        const names = /* @__PURE__ */ new Map();
        for (const s of t.spans) names.set(s.name, (names.get(s.name) ?? 0) + 1);
        return {
          trace_id: opts.trace_id,
          oversized: {
            span_count: t.spans.length,
            top_span_names: [...names.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20),
            span_response_bytes_max: Math.max(...t.spans.map((s) => bytesOf(s))),
            error_span_count: t.spans.filter((s) => s.status === "ERROR").length
          }
        };
      }
      return { trace_id: opts.trace_id, spans: projected };
    },
    async viewSpans(opts) {
      const t = byId.get(opts.trace_id);
      const cap = opts.per_attribute_byte_cap ?? budgets.perAttributeSpanBudget;
      const want = new Set(opts.span_ids);
      const found = (t?.spans ?? []).filter((s) => want.has(s.span_id));
      let truncated = 0;
      const spans = found.map((s) => {
        const { capped, truncated: n } = capAttributes(s.attributes, cap);
        truncated += n;
        return { ...s, attributes: capped };
      });
      const foundIds = new Set(found.map((s) => s.span_id));
      return {
        trace_id: opts.trace_id,
        spans,
        missing_span_ids: opts.span_ids.filter((id) => !foundIds.has(id)),
        truncated_attribute_count: truncated
      };
    },
    async searchTrace(opts) {
      const t = byId.get(opts.trace_id);
      const max = opts.max_matches ?? 50;
      const hits = [];
      for (const s of t?.spans ?? []) {
        for (const hit of searchSpanAttrs(s, opts.regex_pattern, budgets.perMatchTextBudget)) {
          if (hits.length >= max) break;
          hits.push(hit);
        }
      }
      return {
        trace_id: opts.trace_id,
        hits,
        total_matches: hits.length,
        has_more: hits.length >= max
      };
    },
    async searchSpan(opts) {
      const t = byId.get(opts.trace_id);
      const max = opts.max_matches ?? 50;
      const span = (t?.spans ?? []).find((s) => s.span_id === opts.span_id);
      const hits = span ? searchSpanAttrs(span, opts.regex_pattern, budgets.perMatchTextBudget).slice(0, max) : [];
      return {
        trace_id: opts.trace_id,
        span_id: opts.span_id,
        hits,
        total_matches: hits.length,
        has_more: false
      };
    }
  };
}
function searchSpanAttrs(span, pattern, textCap) {
  const re = new RegExp(pattern, "g");
  const hits = [];
  for (const [k, v] of Object.entries(span.attributes)) {
    const text = typeof v === "string" ? v : JSON.stringify(v);
    if (typeof text !== "string") continue;
    re.lastIndex = 0;
    const m = re.exec(text);
    if (!m) continue;
    const at = m.index;
    hits.push({
      trace_id: span.trace_id,
      span_id: span.span_id,
      span_name: span.name,
      span_kind: span.kind,
      attribute_path: `attributes.${k}`,
      matched_text: m[0].slice(0, textCap),
      context_before: text.slice(Math.max(0, at - textCap / 2), at),
      context_after: text.slice(at + m[0].length, at + m[0].length + textCap / 2),
      match_offset: at
    });
  }
  return hits;
}

// src/analyst-loop/run-analyst-loop.ts
import { diffFindings } from "@tangle-network/agent-eval";
async function runAnalystLoop(opts) {
  const log = opts.log ?? defaultLog;
  const strategy = opts.priorFindingsStrategy ?? "per-kind";
  const emit = makeEmitter(opts.onEvent);
  const startedAt = Date.now();
  const baselineRunId = resolveBaselineRunId(opts);
  const priorAll = baselineRunId ? opts.findingsStore?.loadRun(baselineRunId) ?? [] : [];
  log("baseline resolved", { baselineRunId, prior_findings: priorAll.length });
  await emit({
    type: "baseline-resolved",
    runId: opts.runId,
    baselineRunId,
    priorFindingCount: priorAll.length
  });
  const priorFindings = buildPriorFindingsInput(priorAll, strategy, opts.registry.list());
  const analystResult = await runRegistry(opts, priorFindings, emit);
  log("analyst run complete", {
    findings: analystResult.findings.length,
    cost_usd: analystResult.total_cost_usd,
    per_analyst: analystResult.per_analyst.map((s) => ({
      id: s.analyst_id,
      status: s.status,
      n: s.findings_count
    }))
  });
  if (opts.findingsStore && analystResult.findings.length > 0) {
    await opts.findingsStore.append(opts.runId, analystResult.findings);
    await emit({
      type: "findings-persisted",
      runId: opts.runId,
      count: analystResult.findings.length
    });
  }
  let diff = null;
  if (baselineRunId && analystResult.findings.length > 0) {
    diff = diffFindings(
      priorAll.map((f) => ({ ...f })),
      analystResult.findings.map((f) => ({ ...f, run_id: opts.runId }))
    );
    log("diff vs baseline", {
      appeared: diff.appeared.length,
      disappeared: diff.disappeared.length,
      persisted: diff.persisted.length,
      changed: diff.changed.length
    });
    await emit({
      type: "diff-computed",
      runId: opts.runId,
      baselineRunId,
      appeared: diff.appeared.length,
      disappeared: diff.disappeared.length,
      persisted: diff.persisted.length,
      changed: diff.changed.length
    });
  }
  let knowledge = null;
  if (opts.knowledgeAdapter) {
    knowledge = await runKnowledgeAdapter(opts, analystResult.findings, log, emit);
  }
  let improvement = null;
  if (opts.improvementAdapter) {
    improvement = await runImprovementAdapter(opts, analystResult.findings, log, emit);
  }
  await emit({
    type: "loop-completed",
    runId: opts.runId,
    durationMs: Date.now() - startedAt
  });
  return {
    runId: opts.runId,
    baselineRunId,
    analystResult,
    diff,
    knowledge,
    improvement
  };
}
function makeEmitter(onEvent) {
  if (!onEvent) return async () => {
  };
  return async (event) => {
    await onEvent(event);
  };
}
async function runRegistry(opts, priorFindings, emit) {
  const reg = opts.registry;
  if (typeof reg.runStream === "function" && opts.onEvent) {
    let final = null;
    for await (const ev of reg.runStream(opts.runId, opts.inputs, { priorFindings })) {
      await emit({ type: "analyst", runId: opts.runId, event: ev });
      if (ev.type === "run-completed") final = ev.result;
    }
    if (!final) {
      throw new Error("runAnalystLoop: registry.runStream ended without run-completed event");
    }
    return final;
  }
  return opts.registry.run(opts.runId, opts.inputs, { priorFindings });
}
function resolveBaselineRunId(opts) {
  if (opts.baselineRunId === null) return null;
  if (typeof opts.baselineRunId === "string") return opts.baselineRunId;
  if (!opts.findingsStore) return null;
  const all = opts.findingsStore.loadAll();
  let last = null;
  for (const row of all) {
    if (row.run_id === opts.runId) continue;
    last = row.run_id;
  }
  return last;
}
function buildPriorFindingsInput(prior, strategy, registry) {
  if (strategy === "none" || prior.length === 0) return void 0;
  const stripped = prior.map(({ run_id: _run_id, ...rest }) => rest);
  if (strategy === "wildcard") {
    return { "*": stripped };
  }
  void registry;
  return stripped;
}
async function runKnowledgeAdapter(opts, findings, log, emit) {
  const adapter = opts.knowledgeAdapter;
  const batch = await adapter.proposeFromFindings(findings);
  log("knowledge.proposeFromFindings", {
    proposals: batch.proposals.length,
    skipped: batch.skipped,
    errors: batch.errors.length
  });
  await emit({
    type: "knowledge-proposed",
    runId: opts.runId,
    proposalCount: batch.proposals.length,
    skipped: batch.skipped,
    errors: batch.errors.length
  });
  const auto = opts.autoApply?.knowledge ?? false;
  const threshold = opts.autoApply?.knowledgeConfidenceThreshold ?? 0.85;
  if (!auto || !adapter.apply) {
    await emit({
      type: "knowledge-applied",
      runId: opts.runId,
      writtenCount: 0,
      withheldForReview: batch.proposals.length
    });
    return {
      proposals: batch.proposals,
      applied: [],
      skipped: batch.skipped,
      errors: batch.errors,
      withheld_for_review: batch.proposals.length
    };
  }
  const findingsById = new Map(findings.map((f) => [f.finding_id, f]));
  const safe = [];
  let withheld = 0;
  for (const p of batch.proposals) {
    const src = p.sourceFindingId ? findingsById.get(p.sourceFindingId) : void 0;
    if (!src) {
      withheld += 1;
      continue;
    }
    if (src.confidence < threshold) {
      withheld += 1;
      continue;
    }
    safe.push(p);
  }
  const result = await adapter.apply(safe);
  log("knowledge.apply", {
    applied: result.written.length,
    withheld_for_review: withheld,
    warnings: result.warnings.length
  });
  await emit({
    type: "knowledge-applied",
    runId: opts.runId,
    writtenCount: result.written.length,
    withheldForReview: withheld
  });
  return {
    proposals: batch.proposals,
    applied: result.written,
    skipped: batch.skipped,
    errors: batch.errors,
    withheld_for_review: withheld
  };
}
async function runImprovementAdapter(opts, findings, log, emit) {
  const adapter = opts.improvementAdapter;
  const batch = await adapter.proposeFromFindings(findings);
  log("improvement.proposeFromFindings", {
    edits: batch.edits.length,
    skipped: batch.skipped,
    errors: batch.errors.length
  });
  await emit({
    type: "improvement-proposed",
    runId: opts.runId,
    editCount: batch.edits.length,
    skipped: batch.skipped,
    errors: batch.errors.length
  });
  const auto = opts.autoApply?.improvement ?? false;
  const threshold = opts.autoApply?.improvementConfidenceThreshold ?? 0.9;
  if (!auto || !adapter.apply) {
    await emit({
      type: "improvement-applied",
      runId: opts.runId,
      appliedCount: 0,
      withheldForReview: batch.edits.length
    });
    return {
      edits: batch.edits,
      applied: [],
      skipped: batch.skipped,
      errors: batch.errors,
      withheld_for_review: batch.edits.length
    };
  }
  const findingsById = new Map(findings.map((f) => [f.finding_id, f]));
  const safe = [];
  let withheld = 0;
  for (const e of batch.edits) {
    const src = e.sourceFindingId ? findingsById.get(e.sourceFindingId) : void 0;
    if (!src || src.confidence < threshold) {
      withheld += 1;
      continue;
    }
    safe.push(e);
  }
  const result = await adapter.apply(safe);
  log("improvement.apply", {
    applied: result.applied.length,
    withheld_for_review: withheld,
    warnings: result.warnings.length
  });
  await emit({
    type: "improvement-applied",
    runId: opts.runId,
    appliedCount: result.applied.length,
    withheldForReview: withheld
  });
  return {
    edits: batch.edits,
    applied: result.applied,
    skipped: batch.skipped,
    errors: batch.errors,
    withheld_for_review: withheld
  };
}
function defaultLog(msg, fields) {
  if (fields) console.log(`[analyst-loop] ${msg}`, fields);
  else console.log(`[analyst-loop] ${msg}`);
}

export {
  iterationsToTraceStore,
  runAnalystLoop
};
//# sourceMappingURL=chunk-YPA5MLVE.js.map