import {
  AnalystRegistry,
  DEFAULT_TRACE_ANALYST_KINDS,
  createTraceAnalystKind
} from "./chunk-S4SYLDFX.js";
import {
  makeFinding
} from "./chunk-45EEMHTC.js";
import {
  LLM_INPUT_TOKEN_ATTR_KEYS,
  LLM_OUTPUT_TOKEN_ATTR_KEYS,
  TOOL_NAME_ATTR_KEYS
} from "./chunk-YBIGNSCZ.js";

// src/trace-analyst/behavioral-metrics.ts
var INPUT_GROWTH_FACTOR = 3;
var MIN_TOOL_CALLS = 3;
var VERIFY_RE = /verif|eval|inspect|check|assert|validat|review|confirm/i;
function num(v) {
  return typeof v === "number" && Number.isFinite(v) ? v : null;
}
function numAttr(attrs, keys) {
  for (const key of keys) {
    const value = num(attrs[key]);
    if (value !== null) return value;
  }
  return null;
}
function inputTokensOf(s) {
  return numAttr(s.attributes, LLM_INPUT_TOKEN_ATTR_KEYS) ?? num(s.attributes["llm.usage.input_tokens"]);
}
function outputTokensOf(s) {
  return numAttr(s.attributes, LLM_OUTPUT_TOKEN_ATTR_KEYS) ?? num(s.attributes["llm.usage.output_tokens"]);
}
function stepOf(s) {
  return num(s.attributes.step);
}
function toolNameOf(s) {
  if (s.tool_name) return s.tool_name;
  for (const key of TOOL_NAME_ATTR_KEYS) {
    const t = s.attributes[key];
    if (typeof t === "string" && t.length > 0) return t;
  }
  return null;
}
function computeTraceMetrics(spans) {
  const ordered = [...spans].sort((a, b) => {
    const sa = stepOf(a);
    const sb = stepOf(b);
    if (sa !== null && sb !== null && sa !== sb) return sa - sb;
    return a.start_time.localeCompare(b.start_time);
  });
  const inputTokenTrajectory = [];
  const outputTokenTrajectory = [];
  const toolHistogram = {};
  let hasSelfVerification = false;
  for (const s of ordered) {
    const inT = inputTokensOf(s);
    if (inT !== null) inputTokenTrajectory.push(inT);
    const outT = outputTokensOf(s);
    if (outT !== null) outputTokenTrajectory.push(outT);
    const tool = toolNameOf(s);
    if (tool) {
      toolHistogram[tool] = (toolHistogram[tool] ?? 0) + 1;
      if (VERIFY_RE.test(tool)) hasSelfVerification = true;
    }
  }
  const totalToolCalls = Object.values(toolHistogram).reduce((a, b) => a + b, 0);
  const distinctTools = Object.keys(toolHistogram).length;
  const toolDiversityRatio = totalToolCalls === 0 ? 1 : distinctTools / totalToolCalls;
  const signals = [];
  if (inputTokenTrajectory.length >= 3) {
    const first = inputTokenTrajectory[0];
    const last = inputTokenTrajectory[inputTokenTrajectory.length - 1];
    const growthFromZero = first === 0 && last > 0;
    const growth = growthFromZero ? Infinity : first > 0 ? last / first : 0;
    if (last > first && growth >= INPUT_GROWTH_FACTOR) {
      const growthLabel = growthFromZero ? "0\u2192nonzero (unbounded)" : `${growth.toFixed(1)}x`;
      signals.push({
        code: "monotonic-input-growth",
        severity: "high",
        detail: `LLM input tokens grew ${growthLabel} (${first}\u2192${last}) across ${inputTokenTrajectory.length} calls \u2014 full history re-sent each step with no compression.`,
        evidence: {
          first,
          last,
          growth_x: growthFromZero ? "unbounded" : Number(growth.toFixed(2)),
          calls: inputTokenTrajectory.length
        }
      });
    }
  }
  if (outputTokenTrajectory.length >= 3) {
    const first = outputTokenTrajectory[0];
    const last = outputTokenTrajectory[outputTokenTrajectory.length - 1];
    if (last < first) {
      signals.push({
        code: "output-length-decay",
        severity: "medium",
        detail: `LLM output tokens shrank ${first}\u2192${last} over ${outputTokenTrajectory.length} calls \u2014 less planning/reasoning per step as context grows.`,
        evidence: { first, last, calls: outputTokenTrajectory.length }
      });
    }
  }
  if (totalToolCalls >= MIN_TOOL_CALLS && distinctTools === 1) {
    const only = Object.keys(toolHistogram)[0];
    signals.push({
      code: "single-tool-dependency",
      severity: "medium",
      detail: `All ${totalToolCalls} tool calls are \`${only}\` \u2014 no tool diversity and no fallback path.`,
      evidence: { tool: only, calls: totalToolCalls, distinct_tools: 1 }
    });
  }
  if (totalToolCalls >= MIN_TOOL_CALLS && !hasSelfVerification) {
    signals.push({
      code: "no-self-verification",
      severity: "medium",
      detail: `${totalToolCalls} tool calls and none verify/inspect/check state \u2014 the agent never validates its own actions.`,
      evidence: { tool_calls: totalToolCalls, verification_calls: 0 }
    });
  }
  return {
    llmCallCount: inputTokenTrajectory.length,
    inputTokenTrajectory,
    outputTokenTrajectory,
    toolHistogram,
    totalToolCalls,
    distinctTools,
    toolDiversityRatio,
    hasSelfVerification,
    signals
  };
}

// src/analyst/behavioral-analyst.ts
var RECOMMENDED_ACTION = {
  "monotonic-input-growth": "Add a context-budget instruction: once prior context exceeds a threshold, summarize earlier steps into a short status line instead of re-sending full history.",
  "output-length-decay": "Require a minimum planning/reasoning budget per step so late steps do not degrade into terse, error-prone commands.",
  "single-tool-dependency": "Direct the agent to use the full toolset (verify / inspect / alternate actions), not a single execute call, and to plan a fallback when a call returns an unexpected result.",
  "no-self-verification": "After every state-mutating action, verify the result (eval / inspect / assert) before proceeding."
};
var ANALYST_ID = "efficiency-behavioral";
function deriveEfficiencyFindings(metrics, opts = {}) {
  const analystId = opts.analystId ?? ANALYST_ID;
  return metrics.signals.map(
    (sig) => makeFinding({
      analyst_id: analystId,
      area: "efficiency",
      subject: sig.code,
      // kebab — passes the cluster grammar; stable key for diffFindings
      claim: sig.detail,
      severity: sig.severity,
      // Deterministic arithmetic over spans, not a model judgment → certain.
      confidence: 1,
      evidence_refs: [
        {
          kind: "metric",
          uri: `metric://efficiency/${sig.code}`,
          excerpt: JSON.stringify(sig.evidence)
        }
      ],
      recommended_action: RECOMMENDED_ACTION[sig.code],
      metadata: { deterministic: true, evidence: sig.evidence },
      ...opts.producedAt ? { produced_at: opts.producedAt } : {}
    })
  );
}
function behavioralAnalyst() {
  return {
    id: ANALYST_ID,
    description: "Deterministic behavioral/efficiency findings over OTLP spans \u2014 token-growth, output-decay, tool-monoculture, missing self-verification. Zero LLM; model-agnostic by construction.",
    inputKind: "trace-store",
    cost: { kind: "deterministic" },
    version: "1.0.0",
    async analyze(store) {
      const overview = await store.getOverview();
      const spans = [];
      for (const traceId of overview.sample_trace_ids) {
        const viewed = await store.viewTrace({ trace_id: traceId });
        if (viewed.spans) spans.push(...viewed.spans);
      }
      return deriveEfficiencyFindings(computeTraceMetrics(spans));
    }
  };
}

// src/analyst/default-registry.ts
function buildDefaultAnalystRegistry(opts = {}) {
  const registry = new AnalystRegistry(opts.registry);
  if (opts.includeBehavioral !== false) {
    registry.register(behavioralAnalyst());
  }
  if (opts.ai) {
    const kinds = opts.kinds ?? DEFAULT_TRACE_ANALYST_KINDS;
    for (const spec of kinds) {
      registry.register(createTraceAnalystKind(spec, { ai: opts.ai, model: opts.model }));
    }
  }
  return registry;
}

export {
  computeTraceMetrics,
  deriveEfficiencyFindings,
  behavioralAnalyst,
  buildDefaultAnalystRegistry
};
//# sourceMappingURL=chunk-AQ5WQAIV.js.map