{"version":3,"sources":["../src/reporter.ts","../src/harness.ts"],"sourcesContent":["import { DefaultReporter, VerboseReporter } from \"vitest/node\";\nimport c from \"tinyrainbow\";\nimport type { ToolCallRecord, UsageSummary } from \"./harness\";\nimport { toolCalls } from \"./harness\";\n\nconst TEST_NAME_SEPARATOR = c.dim(\" > \");\nconst REPORT_LEVEL_ENV = \"VITEST_EVALS_REPORT_LEVEL\";\nconst TOOL_DETAIL_ENV = \"VITEST_EVALS_TOOL_DETAILS\";\nconst TOOL_DETAIL_LEVEL_ENV = \"VITEST_EVALS_TOOL_DETAILS_LEVEL\";\nconst DEFAULT_TOOL_DETAIL_LEVEL = 0;\nconst INFO_TOOL_DETAIL_LEVEL = 3;\n\ntype EvalReportLevel = \"normal\" | \"info\";\n\ntype EvalReporterOptions = {\n  isTTY?: boolean;\n  reportLevel?: EvalReportLevel;\n  silent?: boolean | \"passed-only\";\n  /** @deprecated Use `reportLevel: \"info\"` or `VITEST_EVALS_REPORT_LEVEL=info`. */\n  toolDetails?: boolean | number;\n};\n\n/** Vitest reporter that renders eval scores, usage, and harness traces. */\nexport default class DefaultEvalReporter extends VerboseReporter {\n  private readonly toolDetailLevel: number;\n\n  constructor(options: EvalReporterOptions = {}) {\n    super(options);\n    this.toolDetailLevel = this.resolveToolDetailLevel(options);\n  }\n\n  override onTestCaseResult(test: any): void {\n    const meta = test.meta();\n    if (!meta.eval && !meta.harness) {\n      super.onTestCaseResult(test);\n      return;\n    }\n\n    // Preserve DefaultReporter's bookkeeping without letting VerboseReporter\n    // print the stock per-test line; eval cases need custom score output.\n    DefaultReporter.prototype.onTestCaseResult.call(this, test);\n\n    const testResult = test.result();\n    if (\n      this.ctx.config.hideSkippedTests &&\n      testResult.state === \"skipped\" &&\n      test.options?.mode !== \"todo\"\n    ) {\n      return;\n    }\n\n    if (meta.harness) {\n      this.logHarnessTestCase(test, meta.harness);\n      if (testResult.state !== \"failed\" && meta.eval?.scores?.length) {\n        this.logJudgeScoreDetails(meta.eval.scores);\n      }\n    } else if (meta.eval) {\n      this.logEvalTestCase(test, meta.eval.avgScore);\n    }\n\n    if (testResult.state === \"failed\") {\n      if (meta.harness && meta.eval?.thresholdFailed) {\n        this.logEvalFailureDetails(meta.eval, testResult.errors, {\n          includeFinal: false,\n        });\n      } else if (meta.harness) {\n        this.logFailureDetails(testResult.errors);\n      } else if (meta.eval) {\n        this.logEvalFailureDetails(meta.eval, testResult.errors);\n      } else {\n        this.logFailureDetails(testResult.errors);\n      }\n    }\n\n    if (test.annotations().length) {\n      this.log();\n      this.printAnnotations(test, \"log\", 3);\n      this.log();\n    }\n  }\n\n  override reportSummary(files: any[], errors: any[]): void {\n    if (!this.isEvalOnlyRun(files)) {\n      super.reportSummary(files, errors);\n      return;\n    }\n\n    if (errors.length > 0) {\n      this.ctx.logger.printUnhandledErrors(errors);\n      this.error();\n    }\n\n    const leakCount = (DefaultReporter.prototype as any).printLeaksSummary.call(\n      this,\n    );\n    (DefaultReporter.prototype as any).reportTestSummary.call(\n      this,\n      files,\n      errors,\n      leakCount,\n    );\n  }\n\n  private logEvalTestCase(test: any, avgScore: number): void {\n    const colorFn =\n      avgScore < 0.5 ? c.red : avgScore < 0.75 ? c.yellow : c.green;\n\n    let title = this.getFormattedTestTitle(test);\n    title += ` [${colorFn(avgScore.toFixed(2))}]`;\n    title += this.getTestCaseSuffix(test);\n\n    this.log(title);\n  }\n\n  private logHarnessTestCase(\n    test: any,\n    harnessMeta: {\n      name: string;\n      run: {\n        session: Parameters<typeof toolCalls>[0];\n        output?: unknown;\n        usage?: {\n          totalTokens?: number;\n          inputTokens?: number;\n          outputTokens?: number;\n          reasoningTokens?: number;\n          toolCalls?: number;\n        };\n        errors?: unknown[];\n      };\n    },\n  ): void {\n    let title = this.getFormattedTestTitle(test);\n    const summary = this.formatHarnessSummary(harnessMeta);\n    if (summary) {\n      title += c.dim(` [${summary}]`);\n    }\n    title += this.getTestCaseSuffix(test);\n\n    this.log(title);\n\n    if (this.toolDetailLevel > 0) {\n      this.logHarnessToolDetails(harnessMeta.run);\n    }\n  }\n\n  private formatHarnessSummary(harnessMeta: {\n    name: string;\n    run: {\n      session: Parameters<typeof toolCalls>[0];\n      usage?: {\n        totalTokens?: number;\n        inputTokens?: number;\n        outputTokens?: number;\n        reasoningTokens?: number;\n        toolCalls?: number;\n      };\n      errors?: unknown[];\n    };\n  }) {\n    const parts: string[] = [];\n    const totalTokens =\n      harnessMeta.run.usage?.totalTokens ??\n      (harnessMeta.run.usage?.inputTokens ?? 0) +\n        (harnessMeta.run.usage?.outputTokens ?? 0) +\n        (harnessMeta.run.usage?.reasoningTokens ?? 0);\n    const totalTools =\n      harnessMeta.run.usage?.toolCalls ??\n      toolCalls(harnessMeta.run.session).length;\n\n    if (totalTokens > 0) {\n      parts.push(`${totalTokens} tok`);\n    }\n    if (totalTools > 0) {\n      parts.push(`${totalTools} tool${totalTools === 1 ? \"\" : \"s\"}`);\n    }\n    if ((harnessMeta.run.errors?.length ?? 0) > 0) {\n      parts.push(`${harnessMeta.run.errors?.length} err`);\n    }\n\n    return parts.length > 0 ? parts.join(\" | \") : null;\n  }\n\n  private logHarnessToolDetails(run: {\n    session: Parameters<typeof toolCalls>[0];\n    output?: unknown;\n  }) {\n    const calls = toolCalls(run.session);\n    const hasOutput = this.summarizeValue(run.output) !== null;\n\n    for (const [index, call] of calls.entries()) {\n      const isLastItem = index === calls.length - 1 && !hasOutput;\n      for (const line of this.formatToolCallLines(call, isLastItem)) {\n        this.log(line);\n      }\n\n      if (this.toolDetailLevel >= 4 && call.arguments !== undefined) {\n        this.log(this.formatRawLine(\"raw in\", call.arguments, isLastItem));\n      }\n      if (this.toolDetailLevel >= 4) {\n        if (call.error) {\n          this.log(this.formatRawLine(\"raw err\", call.error, isLastItem));\n        } else if (call.result !== undefined) {\n          this.log(this.formatRawLine(\"raw out\", call.result, isLastItem));\n        }\n      }\n    }\n\n    const outputSummary = this.summarizeValue(run.output);\n    if (outputSummary) {\n      this.log(this.formatOutputLine(outputSummary));\n    }\n  }\n\n  private formatToolCallLines(call: ToolCallRecord, isLastItem: boolean) {\n    const prefix = c.dim(`   ${this.getItemPrefix(isLastItem)} `);\n    const detailPrefix = c.dim(this.getDetailPrefix(isLastItem));\n    const replayStatus = this.getReplayStatus(call);\n    const lines = [\n      `${prefix}${c.dim(this.formatFieldLabel(\"tool\"))} ${c.cyan(call.name)}${\n        replayStatus ? ` ${c.dim(`[${replayStatus}]`)}` : \"\"\n      }`,\n    ];\n\n    const argumentsSummary = this.formatToolCallArguments(call.arguments);\n    if (argumentsSummary) {\n      lines.push(\n        `${detailPrefix}${c.dim(this.formatFieldLabel(\"args\"))} ${argumentsSummary}`,\n      );\n    }\n\n    lines.push(\n      `${detailPrefix}${c.dim(this.formatFieldLabel(call.error ? \"error\" : \"result\"))} ${this.formatToolCallOutcome(call)}`,\n    );\n\n    return lines;\n  }\n\n  private formatToolCallOutcome(call: ToolCallRecord) {\n    const totalTokens = this.getToolCallTokens(call);\n    const summary = call.error\n      ? this.summarizeValue(call.error)\n      : this.summarizeToolResult(call.result, call.arguments);\n    const responseSize = this.getSerializedSize(call.error ?? call.result);\n    const metrics: string[] = [];\n\n    if (this.toolDetailLevel >= 2 && totalTokens && totalTokens > 0) {\n      metrics.push(`${totalTokens} tok`);\n    } else if (this.toolDetailLevel >= 2 && responseSize !== null) {\n      metrics.push(this.formatBytes(responseSize));\n    }\n    if (\n      this.toolDetailLevel >= 2 &&\n      call.durationMs !== undefined &&\n      call.durationMs > 0\n    ) {\n      metrics.push(`${call.durationMs}ms`);\n    }\n\n    const outcome = summary ?? (call.error ? \"tool failed\" : \"ok\");\n    const metricsText =\n      metrics.length > 0 ? ` ${c.dim(`[${metrics.join(\" | \")}]`)}` : \"\";\n    if (call.error) {\n      return `${c.red(outcome)}${metricsText}`;\n    }\n\n    return `${outcome}${metricsText}`;\n  }\n\n  private getItemPrefix(isLastItem: boolean) {\n    return isLastItem ? \"└─\" : \"├─\";\n  }\n\n  private getDetailPrefix(isLastItem: boolean) {\n    return isLastItem ? \"      \" : \"   │  \";\n  }\n\n  private formatRawLine(\n    label: \"raw in\" | \"raw out\" | \"raw err\",\n    value: unknown,\n    isLastItem: boolean,\n  ) {\n    return c.dim(\n      `${this.getDetailPrefix(isLastItem)}${this.formatFieldLabel(label)} ${this.formatInlineJson(\n        value,\n        {\n          maxLength: 160,\n        },\n      )}`,\n    );\n  }\n\n  private formatOutputLine(summary: string) {\n    return `${c.dim(`   ${this.getItemPrefix(true)} `)}${c.dim(this.formatFieldLabel(\"final\"))} ${summary}`;\n  }\n\n  private logJudgeScoreDetails(\n    scores: Array<{\n      name?: string;\n      score?: number | null;\n    }>,\n  ) {\n    for (const score of scores) {\n      this.log(\n        this.formatDetailLine(\n          \"score\",\n          `${score.name || \"Unknown\"} ${this.formatScore(score.score ?? 0)}`,\n        ),\n      );\n    }\n  }\n\n  private formatFieldLabel(\n    label:\n      | \"tool\"\n      | \"args\"\n      | \"result\"\n      | \"error\"\n      | \"final\"\n      | \"reason\"\n      | \"score\"\n      | \"raw in\"\n      | \"raw out\"\n      | \"raw err\",\n  ) {\n    return label.padEnd(7, \" \");\n  }\n\n  private logEvalFailureDetails(\n    evalMeta: {\n      avgScore: number;\n      output?: unknown;\n      scores?: Array<{\n        name?: string;\n        score?: number | null;\n        metadata?: {\n          rationale?: string;\n          output?: unknown;\n        };\n      }>;\n    },\n    errors: Array<{ message?: string }>,\n    options: {\n      includeFinal?: boolean;\n    } = {},\n  ) {\n    const scoredFailures = [...(evalMeta.scores ?? [])]\n      .filter(\n        (score) =>\n          (score.score ?? 0) < 1 ||\n          score.metadata?.rationale ||\n          score.metadata?.output !== undefined,\n      )\n      .sort((left, right) => (left.score ?? 0) - (right.score ?? 0));\n\n    if (scoredFailures.length <= 1) {\n      const primary = scoredFailures[0];\n      if (primary) {\n        this.log(\n          this.formatDetailLine(\n            \"score\",\n            `${primary.name || \"Unknown\"} ${this.formatScore(primary.score ?? 0)}`,\n          ),\n        );\n      }\n      const reason =\n        primary?.metadata?.rationale ?? this.getCompactErrorMessage(errors);\n      if (reason) {\n        this.log(this.formatDetailLine(\"reason\", reason));\n      }\n    } else {\n      for (const score of scoredFailures.slice(0, 3)) {\n        const scoreValue = this.formatScore(score.score ?? 0);\n        const rationale = score.metadata?.rationale\n          ? ` ${c.dim(\"·\")} ${score.metadata.rationale}`\n          : \"\";\n        this.log(\n          this.formatDetailLine(\n            \"score\",\n            `${score.name || \"Unknown\"} ${scoreValue}${rationale}`,\n          ),\n        );\n      }\n    }\n\n    const outputSummary = this.summarizeEvalOutput(evalMeta.output);\n    if (options.includeFinal !== false && outputSummary) {\n      this.log(this.formatDetailLine(\"final\", outputSummary));\n    }\n  }\n\n  private logFailureDetails(errors: Array<{ message?: string }>) {\n    const message = this.getCompactErrorMessage(errors);\n    if (!message) {\n      return;\n    }\n\n    this.log(this.formatDetailLine(\"reason\", c.red(message)));\n  }\n\n  private formatDetailLine(label: \"reason\" | \"score\" | \"final\", value: string) {\n    if (label === \"final\") {\n      return `${c.dim(\"   \")}${c.dim(this.formatFieldLabel(\"final\"))} ${value}`;\n    }\n\n    const renderedValue = label === \"reason\" ? c.red(value) : value;\n    return `${c.dim(\"   \")}${c.dim(this.formatFieldLabel(label))} ${renderedValue}`;\n  }\n\n  private summarizeEvalOutput(value: unknown) {\n    if (typeof value !== \"string\") {\n      return this.summarizeValue(value);\n    }\n\n    const trimmed = value.trim();\n    if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) {\n      return this.summarizeValue(value);\n    }\n\n    try {\n      return this.summarizeValue(JSON.parse(trimmed));\n    } catch {\n      return this.summarizeValue(value);\n    }\n  }\n\n  private getCompactErrorMessage(errors: Array<{ message?: string }>) {\n    for (const error of errors) {\n      const message = error.message?.split(\"\\n\")[0]?.trim();\n      if (message) {\n        return message;\n      }\n    }\n\n    return null;\n  }\n\n  private isEvalOnlyRun(files: Array<{ filepath?: string; name?: string }>) {\n    return (\n      files.length > 0 &&\n      files.every((file) => {\n        const path = String(file.filepath ?? file.name ?? \"\");\n        return path.endsWith(\".eval.ts\");\n      })\n    );\n  }\n\n  private summarizeToolArguments(value: unknown, maxLength: number) {\n    if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n      return this.summarizeValue(value);\n    }\n\n    return this.summarizeRecord(value as Record<string, unknown>, undefined, {\n      separator: \", \",\n      maxLength,\n    });\n  }\n\n  private formatToolCallArguments(value: unknown) {\n    if (\n      value === undefined ||\n      value === null ||\n      (typeof value === \"object\" &&\n        !Array.isArray(value) &&\n        Object.keys(value as Record<string, unknown>).length === 0)\n    ) {\n      return null;\n    }\n\n    const maxLength = this.toolDetailLevel >= 3 ? 160 : 96;\n    return this.summarizeToolArguments(value, maxLength);\n  }\n\n  private formatScore(score: number) {\n    const rendered = score.toFixed(2);\n    if (score < 0.5) {\n      return c.red(rendered);\n    }\n    if (score < 0.75) {\n      return c.yellow(rendered);\n    }\n    return c.green(rendered);\n  }\n\n  private summarizeToolResult(result: unknown, argumentsValue: unknown) {\n    if (\n      !result ||\n      typeof result !== \"object\" ||\n      Array.isArray(result) ||\n      !argumentsValue ||\n      typeof argumentsValue !== \"object\" ||\n      Array.isArray(argumentsValue)\n    ) {\n      return this.summarizeValue(result);\n    }\n\n    const summarized = this.summarizeRecord(\n      result as Record<string, unknown>,\n      argumentsValue as Record<string, unknown>,\n    );\n    return summarized ?? this.summarizeValue(result);\n  }\n\n  private summarizeValue(value: unknown): string | null {\n    if (value === undefined) {\n      return null;\n    }\n\n    if (value === null) {\n      return \"null\";\n    }\n\n    if (typeof value === \"string\") {\n      const formatted = this.formatSummaryPrimitive(value);\n      return formatted === null ? null : this.truncateSummary(formatted);\n    }\n\n    if (typeof value === \"number\" || typeof value === \"boolean\") {\n      return String(value);\n    }\n\n    if (Array.isArray(value)) {\n      if (value.length === 0) {\n        return \"array(0)\";\n      }\n\n      const first: string | null = this.summarizeValue(value[0]);\n      const suffix = value.length > 1 ? \" ...\" : \"\";\n      return this.truncateSummary(\n        `array(${value.length}) ${first ?? \"\"}${suffix}`.trim(),\n      );\n    }\n\n    if (typeof value === \"object\") {\n      return this.summarizeRecord(value as Record<string, unknown>);\n    }\n\n    return this.truncateSummary(String(value));\n  }\n\n  private resolveToolDetailLevel(options: EvalReporterOptions) {\n    if (options.toolDetails !== undefined) {\n      return this.resolveLegacyToolDetailLevel(options.toolDetails);\n    }\n\n    const configuredReportLevel = this.resolveConfiguredReportLevel(\n      options.reportLevel,\n    );\n    if (configuredReportLevel) {\n      return configuredReportLevel === \"info\"\n        ? INFO_TOOL_DETAIL_LEVEL\n        : DEFAULT_TOOL_DETAIL_LEVEL;\n    }\n\n    return this.resolveLegacyToolDetailLevel(undefined);\n  }\n\n  private resolveConfiguredReportLevel(\n    reportLevel: EvalReporterOptions[\"reportLevel\"],\n  ) {\n    if (reportLevel === \"normal\" || reportLevel === \"info\") {\n      return reportLevel;\n    }\n\n    const envReportLevel = process.env[REPORT_LEVEL_ENV];\n    if (envReportLevel === \"normal\" || envReportLevel === \"info\") {\n      return envReportLevel;\n    }\n\n    return null;\n  }\n\n  private resolveLegacyToolDetailLevel(\n    toolDetails: EvalReporterOptions[\"toolDetails\"],\n  ) {\n    if (typeof toolDetails === \"number\") {\n      return Math.max(DEFAULT_TOOL_DETAIL_LEVEL, Math.floor(toolDetails));\n    }\n\n    if (toolDetails === true) {\n      return 2;\n    }\n\n    if (toolDetails === false) {\n      return DEFAULT_TOOL_DETAIL_LEVEL;\n    }\n\n    const levelFromEnv = Number.parseInt(\n      process.env[TOOL_DETAIL_LEVEL_ENV] ?? \"\",\n      10,\n    );\n    if (Number.isFinite(levelFromEnv)) {\n      return Math.max(DEFAULT_TOOL_DETAIL_LEVEL, levelFromEnv);\n    }\n\n    if (process.env[TOOL_DETAIL_ENV] === \"1\") {\n      return 2;\n    }\n\n    return DEFAULT_TOOL_DETAIL_LEVEL;\n  }\n\n  private getToolCallTokens(call: ToolCallRecord) {\n    const usage = call.metadata?.usage;\n    if (this.isUsageSummary(usage)) {\n      return (\n        usage.totalTokens ??\n        (usage.inputTokens ?? 0) +\n          (usage.outputTokens ?? 0) +\n          (usage.reasoningTokens ?? 0)\n      );\n    }\n\n    const metadataTotalTokens = call.metadata?.totalTokens;\n    return typeof metadataTotalTokens === \"number\" ? metadataTotalTokens : null;\n  }\n\n  private getReplayStatus(call: ToolCallRecord) {\n    const replay = call.metadata?.replay;\n    if (\n      replay &&\n      typeof replay === \"object\" &&\n      !Array.isArray(replay) &&\n      \"status\" in replay\n    ) {\n      const status = (replay as { status?: unknown }).status;\n      if (status === \"recorded\") {\n        return \"recorded\";\n      }\n      if (status === \"replayed\") {\n        return \"cached\";\n      }\n    }\n\n    return null;\n  }\n\n  private getSerializedSize(value: unknown) {\n    if (value === undefined) {\n      return null;\n    }\n\n    let formatted: string;\n    try {\n      formatted = JSON.stringify(value);\n    } catch {\n      formatted = String(value);\n    }\n\n    return Buffer.byteLength(formatted, \"utf8\");\n  }\n\n  private formatBytes(bytes: number) {\n    if (bytes < 1024) {\n      return `${bytes}B`;\n    }\n\n    const kib = bytes / 1024;\n    if (kib < 10) {\n      return `${kib.toFixed(1)}KB`;\n    }\n\n    return `${Math.round(kib)}KB`;\n  }\n\n  private isUsageSummary(value: unknown): value is UsageSummary {\n    return Boolean(value && typeof value === \"object\");\n  }\n\n  private formatInlineJson(\n    value: unknown,\n    { maxLength }: { maxLength: number },\n  ) {\n    let formatted: string;\n    try {\n      formatted = JSON.stringify(value);\n    } catch {\n      formatted = String(value);\n    }\n\n    if (formatted.length <= maxLength) {\n      return formatted;\n    }\n\n    return `${formatted.slice(0, maxLength - 3)}...`;\n  }\n\n  private truncateSummary(value: string, maxLength = 96) {\n    if (value.length <= maxLength) {\n      return value;\n    }\n\n    return `${value.slice(0, maxLength - 3)}...`;\n  }\n\n  private formatSummaryPrimitive(value: unknown): string | null {\n    if (value === undefined) {\n      return null;\n    }\n\n    if (value === null) {\n      return \"null\";\n    }\n\n    if (typeof value === \"string\") {\n      const truncated = this.truncateSummary(value, 32);\n      return /^[a-zA-Z0-9_.:-]+$/.test(truncated)\n        ? truncated\n        : JSON.stringify(truncated);\n    }\n\n    if (typeof value === \"number\" || typeof value === \"boolean\") {\n      return String(value);\n    }\n\n    if (Array.isArray(value)) {\n      return `array(${value.length})`;\n    }\n\n    if (typeof value === \"object\") {\n      return `object(${Object.keys(value as Record<string, unknown>).length})`;\n    }\n\n    return String(value);\n  }\n\n  private summarizeRecord(\n    record: Record<string, unknown>,\n    omitMatchingValuesFrom?: Record<string, unknown>,\n    options: {\n      separator?: string;\n      maxLength?: number;\n    } = {},\n  ) {\n    const separator = options.separator ?? \" \";\n    const maxLength = options.maxLength ?? 96;\n    const keys = Object.keys(record);\n    if (keys.length === 0) {\n      return \"object(0)\";\n    }\n\n    const preferredKeys = [\n      \"status\",\n      \"invoiceId\",\n      \"refundId\",\n      \"id\",\n      \"amount\",\n      \"customer\",\n      \"refundable\",\n      \"reason\",\n      \"message\",\n      \"type\",\n      \"name\",\n    ];\n\n    const visibleKeys = keys.filter((key) => {\n      if (!omitMatchingValuesFrom || !(key in omitMatchingValuesFrom)) {\n        return true;\n      }\n\n      return !this.valuesMatch(record[key], omitMatchingValuesFrom[key]);\n    });\n\n    const orderedKeys = [\n      ...preferredKeys.filter((key) => visibleKeys.includes(key)),\n      ...visibleKeys.filter((key) => !preferredKeys.includes(key)),\n    ].slice(0, 4);\n\n    const parts = orderedKeys\n      .map((key) => {\n        const formattedValue = this.formatSummaryPrimitive(record[key]);\n        return formattedValue === null ? null : `${key}=${formattedValue}`;\n      })\n      .filter((part): part is string => part !== null);\n\n    if (parts.length === 0) {\n      return null;\n    }\n\n    const suffix = visibleKeys.length > orderedKeys.length ? \" ...\" : \"\";\n    return this.truncateSummary(`${parts.join(separator)}${suffix}`, maxLength);\n  }\n\n  private valuesMatch(left: unknown, right: unknown): boolean {\n    if (left === right) {\n      return true;\n    }\n\n    if (left === null || right === null) {\n      return left === right;\n    }\n\n    if (Array.isArray(left) && Array.isArray(right)) {\n      return (\n        left.length === right.length &&\n        left.every((item, index) => this.valuesMatch(item, right[index]))\n      );\n    }\n\n    if (\n      typeof left === \"object\" &&\n      typeof right === \"object\" &&\n      !Array.isArray(left) &&\n      !Array.isArray(right)\n    ) {\n      const leftRecord = left as Record<string, unknown>;\n      const rightRecord = right as Record<string, unknown>;\n      const leftKeys = Object.keys(leftRecord);\n      const rightKeys = Object.keys(rightRecord);\n\n      return (\n        leftKeys.length === rightKeys.length &&\n        leftKeys.every(\n          (key) =>\n            key in rightRecord &&\n            this.valuesMatch(leftRecord[key], rightRecord[key]),\n        )\n      );\n    }\n\n    return false;\n  }\n\n  private getFormattedTestTitle(test: any) {\n    let title = ` ${this.getEntityPrefix(test)} `;\n    title += test.module.task.name;\n    if (test.location) {\n      title += c.dim(`:${test.location.line}:${test.location.column}`);\n    }\n    title += TEST_NAME_SEPARATOR;\n    title += test.fullName ?? this.getTestName(test.task, TEST_NAME_SEPARATOR);\n    return title;\n  }\n}\n","import {\n  assistantMessages,\n  failedSpans,\n  latestAssistantMessageContent,\n  messagesByRole,\n  spans,\n  spansByKind,\n  systemMessages,\n  toolCalls,\n  toolMessages,\n  userMessages,\n} from \"@vitest-evals/core\";\nimport type {\n  GenAiOperationName,\n  HarnessRun,\n  HarnessRunError,\n  JsonPrimitive,\n  JsonValue,\n  NormalizedMessage,\n  NormalizedSession,\n  NormalizedSpan,\n  NormalizedSpanAttributes,\n  NormalizedSpanEvent,\n  NormalizedTrace,\n  TimingSummary,\n  ToolCallRecord,\n  UsageSummary,\n} from \"@vitest-evals/core\";\n\nexport {\n  assistantMessages,\n  failedSpans,\n  latestAssistantMessageContent,\n  messagesByRole,\n  spans,\n  spansByKind,\n  systemMessages,\n  toolCalls,\n  toolMessages,\n  userMessages,\n} from \"@vitest-evals/core\";\nexport type {\n  GenAiOperationName,\n  GenAiOutputType,\n  GenAiProviderName,\n  GenAiSemanticAttributeKey,\n  GenAiSemanticAttributes,\n  GenAiTokenType,\n  GenAiToolType,\n  HarnessRun,\n  HarnessRunError,\n  JsonPrimitive,\n  JsonValue,\n  NormalizedMessage,\n  NormalizedSession,\n  NormalizedSpan,\n  NormalizedSpanAttributeKey,\n  NormalizedSpanAttributes,\n  NormalizedSpanEvent,\n  NormalizedTrace,\n  OpenTelemetrySemanticAttributeKey,\n  OpenTelemetrySemanticAttributes,\n  TimingSummary,\n  ToolCallRecord,\n  UsageSummary,\n} from \"@vitest-evals/core\";\n\n/** Options for converting normalized tool calls into trace spans. */\nexport type CreateToolCallSpansOptions = {\n  /** Trace id to attach to each generated tool span. */\n  traceId?: string;\n  /** Parent span id to attach to each generated tool span. */\n  parentId?: string;\n  /** Prefix used to create internal span ids instead of reusing tool-call ids. */\n  spanIdPrefix?: string;\n};\n\n/** Options for attaching a fallback run trace to a harness result. */\nexport type EnsureRunTraceOptions = {\n  /** Human-readable run or harness name. */\n  name: string;\n  /** Wall-clock start time for the harness run. */\n  startedAt: Date;\n  /** Wall-clock finish time for the harness run. */\n  finishedAt: Date;\n  /** Optional trace id. A generated id is used when omitted. */\n  id?: string;\n  /** GenAI operation name to place on the root run span. */\n  operationName?: GenAiOperationName;\n  /** Optional JSON-safe source marker for the trace metadata. */\n  source?: string;\n};\n\ntype OutputField<TOutput extends JsonValue | undefined> =\n  undefined extends TOutput ? { output?: TOutput } : { output: TOutput };\n\n/** Per-run metadata shape accepted by harnesses and eval tests. */\nexport type HarnessMetadata = Record<string, unknown>;\n\n/**\n * Runtime context passed from the eval fixture into a harness run.\n *\n * @example\n * ```ts\n * const harness: Harness<string> = {\n *   name: \"refund-agent\",\n *   async run(input, context) {\n *     context.setArtifact(\"inputLength\", input.length);\n *\n *     return {\n *       output: undefined,\n *       session: { messages: [{ role: \"user\", content: input }] },\n *       usage: {},\n *       errors: [],\n *     };\n *   },\n * };\n * ```\n */\nexport type HarnessContext<\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n> = {\n  /** Per-run metadata passed through `run(input, { metadata })`. */\n  metadata: Readonly<TMetadata>;\n  /** Abort signal from Vitest when available. */\n  signal?: AbortSignal;\n  /** Mutable JSON-safe artifact bag shared with the harness. */\n  artifacts: Record<string, JsonValue>;\n  /** Stores one JSON-safe artifact on the current run. */\n  setArtifact: (name: string, value: JsonValue) => void;\n};\n\n/**\n * Adapter that executes the system under test and returns a normalized run.\n *\n * @example\n * ```ts\n * const harness: Harness<string, { status: \"approved\" | \"denied\" }> = {\n *   name: \"refund-agent\",\n *   async run(input, context) {\n *     return normalizeHarnessRun(input, await runRefundFlow(input), context);\n *   },\n * };\n * ```\n */\nexport type Harness<\n  TInput = unknown,\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n> = {\n  /** Stable harness name used in reports. */\n  name: string;\n  /** Executes the system under test and returns a normalized run. */\n  run: (\n    input: TInput,\n    context: HarnessContext<TMetadata>,\n  ) => Promise<HarnessRun<TOutput>>;\n};\n\n/** Value or promise accepted by lightweight harness callbacks. */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** Lightweight tool-call record accepted by `createHarness(...)` results. */\nexport type SimpleToolCallRecord = Omit<\n  ToolCallRecord,\n  \"arguments\" | \"result\" | \"error\" | \"metadata\"\n> & {\n  /** Raw tool arguments accepted by `createHarness(...)` before normalization. */\n  arguments?: unknown;\n  /** Raw tool result accepted by `createHarness(...)` before normalization. */\n  result?: unknown;\n  /** Raw tool error accepted by `createHarness(...)` before normalization. */\n  error?: unknown;\n  /** Raw tool metadata accepted by `createHarness(...)` before normalization. */\n  metadata?: Record<string, unknown>;\n};\n\n/** Lightweight span event accepted by `createHarness(...)` results. */\nexport type SimpleSpanEvent = Omit<NormalizedSpanEvent, \"attributes\"> & {\n  /** Raw event attributes accepted by `createHarness(...)` before normalization. */\n  attributes?: Record<string, unknown>;\n};\n\n/** Lightweight span record accepted by `createHarness(...)` results. */\nexport type SimpleSpanRecord = Omit<\n  NormalizedSpan,\n  \"attributes\" | \"error\" | \"events\"\n> & {\n  /** Raw span attributes accepted by `createHarness(...)` before normalization. */\n  attributes?: Record<string, unknown>;\n  /** Raw span error accepted by `createHarness(...)` before normalization. */\n  error?: unknown;\n  /** Raw span events accepted by `createHarness(...)` before normalization. */\n  events?: SimpleSpanEvent[];\n};\n\n/** Lightweight trace record accepted by `createHarness(...)` results. */\nexport type SimpleTraceRecord = Omit<NormalizedTrace, \"metadata\" | \"spans\"> & {\n  /** Raw trace metadata accepted by `createHarness(...)` before normalization. */\n  metadata?: Record<string, unknown>;\n  /** Lightweight spans to normalize into the trace. */\n  spans: SimpleSpanRecord[];\n};\n\n/**\n * Lightweight result shape normalized by `createHarness(...)`.\n *\n * @example\n * ```ts\n * const result: SimpleHarnessResult<{ status: \"approved\" }> = {\n *   output: { status: \"approved\" },\n *   toolCalls: [{ name: \"lookupInvoice\", arguments: { invoiceId: \"inv_123\" } }],\n *   usage: { totalTokens: 260 },\n * };\n * ```\n */\nexport type SimpleHarnessResult<\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n> = OutputField<TOutput> & {\n  /** Pre-normalized transcript messages. When omitted, a default user/assistant transcript is created. */\n  messages?: NormalizedMessage[];\n  /** Lightweight tool-call records to normalize into the session. */\n  toolCalls?: SimpleToolCallRecord[];\n  /** Usage summary to attach to the run. */\n  usage?: UsageSummary;\n  /** Timing summary to attach to the run. */\n  timings?: TimingSummary;\n  /** Raw artifact values to normalize and merge into the run. */\n  artifacts?: Record<string, unknown>;\n  /** Lightweight traces and spans to normalize into the run. */\n  traces?: SimpleTraceRecord[];\n  /** Raw session metadata to normalize into the session. */\n  metadata?: Record<string, unknown>;\n  /** Raw errors to normalize into the run. */\n  errors?: unknown[];\n};\n\n/** Either a complete normalized run or a lightweight result to normalize. */\nexport type HarnessResultLike<\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n> = HarnessRun<TOutput> | SimpleHarnessResult<TOutput>;\n\n/** Arguments passed to the `createHarness(...)` convenience callback. */\nexport type CreateHarnessRunArgs<TInput, TMetadata extends HarnessMetadata> = {\n  /** Original input passed to `run(input)`. */\n  input: TInput;\n  /** Read-only metadata passed to `run(input, { metadata })`. */\n  metadata: Readonly<TMetadata>;\n  /** Abort signal from Vitest when available. */\n  signal?: AbortSignal;\n  /** Mutable run artifact bag. */\n  artifacts: HarnessContext<TMetadata>[\"artifacts\"];\n  /** Stores one JSON-safe artifact on the current run. */\n  setArtifact: HarnessContext<TMetadata>[\"setArtifact\"];\n};\n\n/**\n * Options for creating a lightweight custom application harness.\n *\n * @example\n * ```ts\n * const options: CreateHarnessOptions<string, { status: \"approved\" }> = {\n *   name: \"refund-agent\",\n *   run: async ({ input }) => ({\n *     output: await classifyRefund(input),\n *   }),\n * };\n * ```\n */\nexport type CreateHarnessOptions<\n  TInput = unknown,\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n> = {\n  /** Stable harness name used in reports. */\n  name: string;\n  /** Executes application code and returns either a lightweight result or full `HarnessRun`. */\n  run: (\n    args: CreateHarnessRunArgs<TInput, TMetadata>,\n  ) => MaybePromise<HarnessResultLike<TOutput>>;\n};\n\nfunction isJsonPrimitive(value: unknown): value is JsonPrimitive {\n  return (\n    value === null ||\n    typeof value === \"string\" ||\n    typeof value === \"boolean\" ||\n    (typeof value === \"number\" && Number.isFinite(value))\n  );\n}\n\nfunction isJsonRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction normalizeJsonArray(value: unknown[], seen: WeakSet<object>) {\n  if (seen.has(value)) {\n    return undefined;\n  }\n\n  seen.add(value);\n  const normalized = value.map((item) => {\n    const normalized = toJsonValueInternal(item, seen);\n    return normalized === undefined ? null : normalized;\n  });\n  seen.delete(value);\n\n  return normalized;\n}\n\nfunction normalizeJsonObject(\n  value: Record<string, unknown>,\n  seen: WeakSet<object>,\n): Record<string, JsonValue> {\n  const normalized: Record<string, JsonValue> = {};\n\n  if (seen.has(value)) {\n    return normalized;\n  }\n\n  seen.add(value);\n  try {\n    for (const [key, entryValue] of Object.entries(value)) {\n      const entry = toJsonValueInternal(entryValue, seen);\n      if (entry !== undefined) {\n        normalized[key] = entry;\n      }\n    }\n  } finally {\n    seen.delete(value);\n  }\n\n  return normalized;\n}\n\n/** Returns true when a value exposes a callable method with the given name. */\nexport function hasCallableMethod(value: unknown, methodName: string) {\n  return (\n    value !== null &&\n    (typeof value === \"object\" || typeof value === \"function\") &&\n    methodName in value &&\n    typeof (value as Record<string, unknown>)[methodName] === \"function\"\n  );\n}\n\n/** Normalizes an unknown value into the JSON-safe shape used by harness runs. */\nexport function toJsonValue(value: unknown): JsonValue | undefined {\n  return toJsonValueInternal(value, new WeakSet());\n}\n\nfunction toJsonValueInternal(\n  value: unknown,\n  seen: WeakSet<object>,\n): JsonValue | undefined {\n  if (isJsonPrimitive(value)) {\n    return value;\n  }\n\n  if (\n    value !== null &&\n    typeof value === \"object\" &&\n    seen.has(value as object)\n  ) {\n    return undefined;\n  }\n\n  if (Array.isArray(value)) {\n    return normalizeJsonArray(value, seen);\n  }\n\n  if (isJsonRecord(value)) {\n    return normalizeJsonObject(value, seen);\n  }\n\n  return undefined;\n}\n\n/** Drops non-JSON properties from a record while preserving valid values. */\nexport function normalizeRecord(\n  value: Record<string, unknown>,\n): Record<string, JsonValue> {\n  return normalizeJsonObject(value, new WeakSet());\n}\n\n/** Normalizes metadata and omits the field entirely when nothing survives. */\nexport function normalizeMetadata(\n  value: Record<string, unknown>,\n): Record<string, JsonValue> | undefined {\n  const normalized = normalizeRecord(value);\n  return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n\n/** Converts arbitrary content into the JSON-safe message content shape. */\nexport function normalizeContent(value: unknown): JsonValue {\n  const normalized = toJsonValue(value);\n  return normalized !== undefined ? normalized : String(value);\n}\n\n/**\n * Creates a harness from the common \"run app code and return output\" shape.\n *\n * @param options - Harness name plus the callback that executes app code.\n *\n * @example\n * ```ts\n * import { createHarness } from \"vitest-evals\";\n *\n * export const refundHarness = createHarness<\n *   string,\n *   { status: \"approved\" | \"denied\" },\n *   { expected: { status: \"approved\" | \"denied\" } }\n * >({\n *   name: \"refund-agent\",\n *   run: async ({ input, metadata, setArtifact }) => {\n *     const result = await runRefundFlow(input, metadata);\n *     const output = { status: result.status };\n *\n *     setArtifact(\"case\", { expected: metadata.expected.status });\n *\n *     return {\n *       output,\n *       toolCalls: result.toolCalls,\n *       usage: { provider: \"openai\", model: \"gpt-4o-mini\" },\n *     };\n *   },\n * });\n * ```\n */\nexport function createHarness<\n  TInput = unknown,\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n>(\n  options: CreateHarnessOptions<TInput, TOutput, TMetadata>,\n): Harness<TInput, TOutput, TMetadata>;\nexport function createHarness<\n  TInput = unknown,\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n>(\n  options: CreateHarnessOptions<TInput, TOutput, TMetadata>,\n): Harness<TInput, TOutput, TMetadata> {\n  const harness: Harness<TInput, TOutput, TMetadata> = {\n    name: options.name,\n    run: async (input, context) => {\n      const startedAt = new Date();\n\n      try {\n        const result = await options.run({\n          input,\n          metadata: context.metadata,\n          signal: context.signal,\n          artifacts: context.artifacts,\n          setArtifact: context.setArtifact,\n        });\n        const run = normalizeHarnessRun(input, result, context);\n        ensureRunTrace(run, {\n          name: options.name,\n          startedAt,\n          finishedAt: new Date(),\n        });\n\n        return run;\n      } catch (error) {\n        const partialRun = getHarnessRunFromError(error);\n        if (partialRun) {\n          if (\n            Object.keys(context.artifacts).length > 0 &&\n            !partialRun.artifacts\n          ) {\n            partialRun.artifacts = context.artifacts;\n          }\n          ensureRunTrace(partialRun, {\n            name: options.name,\n            startedAt,\n            finishedAt: new Date(),\n          });\n          throw attachHarnessRunToError(error, partialRun);\n        }\n\n        const failedRun = createFailedHarnessRun(input, error, {\n          artifacts: context.artifacts,\n        });\n        ensureRunTrace(failedRun, {\n          name: options.name,\n          startedAt,\n          finishedAt: new Date(),\n        });\n\n        throw attachHarnessRunToError(error, failedRun);\n      }\n    },\n  };\n\n  return harness;\n}\n\n/**\n * Normalizes a lightweight harness result into the reporter-facing run shape.\n *\n * @param input - Original input passed to the harness.\n * @param result - Lightweight result or pre-normalized harness run.\n * @param context - Optional per-run context used to merge artifacts.\n *\n * @example\n * ```ts\n * const run = normalizeHarnessRun(\"Refund invoice inv_123\", {\n *   output: { status: \"approved\" },\n *   toolCalls: [{ name: \"lookupInvoice\", arguments: { invoiceId: \"inv_123\" } }],\n *   usage: { provider: \"openai\", model: \"gpt-4o-mini\" },\n * });\n *\n * expect(toolCalls(run.session)).toHaveLength(1);\n * ```\n */\nexport function normalizeHarnessRun<\n  TInput = unknown,\n  TMetadata extends HarnessMetadata = HarnessMetadata,\n  TOutput extends JsonValue | undefined = JsonValue | undefined,\n>(\n  input: TInput,\n  result: HarnessResultLike<TOutput>,\n  context?: HarnessContext<TMetadata>,\n): HarnessRun<TOutput> {\n  if (isHarnessRun(result)) {\n    if (\n      context &&\n      Object.keys(context.artifacts).length > 0 &&\n      !result.artifacts\n    ) {\n      return {\n        ...result,\n        artifacts: context.artifacts,\n      };\n    }\n\n    return result;\n  }\n\n  const output = result.output;\n  const toolCalls = normalizeSimpleToolCalls(result.toolCalls);\n  const usage = result.usage ?? {};\n  const messages =\n    result.messages ??\n    createDefaultSessionMessages({\n      input,\n      output,\n      toolCalls,\n    });\n  const metadata = result.metadata\n    ? normalizeMetadata(result.metadata)\n    : undefined;\n  const artifacts = normalizeMergedArtifacts(\n    context?.artifacts,\n    result.artifacts,\n  );\n  const traces = normalizeSimpleTraces(result.traces);\n\n  return {\n    session: {\n      messages,\n      ...(usage.provider ? { provider: usage.provider } : {}),\n      ...(usage.model ? { model: usage.model } : {}),\n      ...(metadata ? { metadata } : {}),\n    },\n    ...(output !== undefined ? { output } : {}),\n    usage,\n    ...(result.timings ? { timings: result.timings } : {}),\n    ...(artifacts ? { artifacts } : {}),\n    ...(traces ? { traces } : {}),\n    errors: normalizeSimpleErrors(result.errors),\n  } as HarnessRun<TOutput>;\n}\n\n/**\n * Builds a JSON-safe failed run for errors that happen before a harness can return.\n *\n * @param input - Original input passed to the harness.\n * @param error - Error thrown by setup or execution.\n * @param options - Optional artifacts to preserve on the failed run.\n */\nexport function createFailedHarnessRun(\n  input: unknown,\n  error: unknown,\n  options: { artifacts?: Record<string, JsonValue> } = {},\n): HarnessRun {\n  const artifacts = options.artifacts;\n\n  return {\n    session: {\n      messages: [\n        {\n          role: \"user\",\n          content: normalizeContent(input),\n        },\n      ],\n    },\n    usage: {},\n    ...(artifacts && Object.keys(artifacts).length > 0 ? { artifacts } : {}),\n    errors: [serializeError(error)],\n  };\n}\n\nfunction createDefaultSessionMessages<TInput>({\n  input,\n  output,\n  toolCalls: normalizedToolCalls,\n}: {\n  input: TInput;\n  output: JsonValue | undefined;\n  toolCalls: ToolCallRecord[];\n}): NormalizedMessage[] {\n  const messages: NormalizedMessage[] = [\n    {\n      role: \"user\",\n      content: normalizeContent(input),\n    },\n  ];\n\n  if (output !== undefined || normalizedToolCalls.length > 0) {\n    messages.push({\n      role: \"assistant\",\n      ...(output !== undefined ? { content: normalizeContent(output) } : {}),\n      ...(normalizedToolCalls.length > 0\n        ? { toolCalls: normalizedToolCalls }\n        : {}),\n    });\n  }\n\n  return messages;\n}\n\nfunction normalizeSimpleToolCalls(\n  calls: SimpleToolCallRecord[] | undefined,\n): ToolCallRecord[] {\n  return (calls ?? []).map((call) => {\n    const {\n      arguments: rawArguments,\n      result: rawResult,\n      error: rawError,\n      metadata: rawMetadata,\n      ...toolCall\n    } = call;\n    const args = normalizeToolCallArguments(rawArguments);\n    const result = toJsonValue(rawResult);\n    const error = normalizeToolCallError(rawError);\n    const metadata = rawMetadata ? normalizeMetadata(rawMetadata) : undefined;\n\n    return {\n      ...toolCall,\n      ...(args ? { arguments: args } : {}),\n      ...(result !== undefined ? { result } : {}),\n      ...(error ? { error } : {}),\n      ...(metadata ? { metadata } : {}),\n    };\n  });\n}\n\nfunction normalizeToolCallArguments(\n  value: unknown,\n): Record<string, JsonValue> | undefined {\n  if (value === undefined) {\n    return undefined;\n  }\n\n  const normalized = toJsonValue(value);\n  return normalized &&\n    typeof normalized === \"object\" &&\n    !Array.isArray(normalized)\n    ? normalized\n    : undefined;\n}\n\nfunction normalizeToolCallError(\n  value: unknown,\n): ToolCallRecord[\"error\"] | undefined {\n  if (value === undefined) {\n    return undefined;\n  }\n\n  const serialized = serializeError(value);\n  const { message, type, ...details } = serialized;\n\n  return {\n    ...details,\n    message: typeof message === \"string\" ? message : String(message),\n    ...(typeof type === \"string\" ? { type } : {}),\n  };\n}\n\nfunction normalizeMergedArtifacts(\n  contextArtifacts: Record<string, JsonValue> | undefined,\n  resultArtifacts: Record<string, unknown> | undefined,\n) {\n  const artifacts = {\n    ...(contextArtifacts ?? {}),\n    ...(resultArtifacts ? normalizeRecord(resultArtifacts) : {}),\n  };\n\n  return Object.keys(artifacts).length > 0 ? artifacts : undefined;\n}\n\nfunction normalizeSimpleErrors(\n  errors: unknown[] | undefined,\n): Array<Record<string, JsonValue>> {\n  return (errors ?? []).map((error) => {\n    const normalized = toJsonValue(error);\n\n    if (\n      normalized &&\n      typeof normalized === \"object\" &&\n      !Array.isArray(normalized) &&\n      Object.keys(normalized).length > 0\n    ) {\n      return normalized;\n    }\n\n    return serializeError(error);\n  });\n}\n\nfunction normalizeSimpleTraces(\n  traces: SimpleTraceRecord[] | undefined,\n): NormalizedTrace[] | undefined {\n  if (!Array.isArray(traces)) {\n    return undefined;\n  }\n\n  const normalized = traces\n    .map(normalizeSimpleTrace)\n    .filter((trace): trace is NormalizedTrace => Boolean(trace));\n\n  return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeSimpleTrace(trace: unknown): NormalizedTrace | undefined {\n  if (!isJsonRecord(trace)) {\n    return undefined;\n  }\n\n  const {\n    metadata: rawMetadata,\n    spans: rawSpans,\n    ...traceFields\n  } = trace as Partial<SimpleTraceRecord>;\n  const spans = (Array.isArray(rawSpans) ? rawSpans : [])\n    .map((span) => normalizeSimpleSpan(span))\n    .filter((span): span is NormalizedSpan => Boolean(span));\n  const metadata = isJsonRecord(rawMetadata)\n    ? normalizeMetadata(rawMetadata)\n    : undefined;\n\n  if (spans.length === 0 && !traceFields.id && !traceFields.name) {\n    return undefined;\n  }\n\n  return {\n    ...traceFields,\n    ...(metadata ? { metadata } : {}),\n    spans,\n  };\n}\n\nfunction normalizeSimpleSpan(span: unknown): NormalizedSpan | undefined {\n  if (!isJsonRecord(span) || typeof span.name !== \"string\" || !span.name) {\n    return undefined;\n  }\n\n  const {\n    attributes: rawAttributes,\n    error: rawError,\n    events: rawEvents,\n    ...spanFields\n  } = span as Partial<SimpleSpanRecord> & { name: string };\n  const attributes = rawAttributes\n    ? isJsonRecord(rawAttributes)\n      ? normalizeMetadata(rawAttributes)\n      : undefined\n    : undefined;\n  const error = normalizeSpanError(rawError);\n  const events = normalizeSimpleSpanEvents(rawEvents);\n\n  return {\n    ...spanFields,\n    ...(attributes\n      ? { attributes: attributes as NormalizedSpanAttributes }\n      : {}),\n    ...(error ? { error } : {}),\n    ...(events ? { events } : {}),\n  };\n}\n\nfunction normalizeSimpleSpanEvents(\n  events: unknown,\n): NormalizedSpanEvent[] | undefined {\n  if (!Array.isArray(events)) {\n    return undefined;\n  }\n\n  const normalized = events\n    .map(normalizeSimpleSpanEvent)\n    .filter((event): event is NormalizedSpanEvent => Boolean(event));\n\n  return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeSimpleSpanEvent(\n  event: unknown,\n): NormalizedSpanEvent | undefined {\n  if (!isJsonRecord(event) || typeof event.name !== \"string\" || !event.name) {\n    return undefined;\n  }\n\n  const { attributes: rawAttributes, ...eventFields } =\n    event as Partial<SimpleSpanEvent> & { name: string };\n  const attributes = rawAttributes\n    ? isJsonRecord(rawAttributes)\n      ? normalizeMetadata(rawAttributes)\n      : undefined\n    : undefined;\n\n  return {\n    ...eventFields,\n    ...(attributes\n      ? { attributes: attributes as NormalizedSpanAttributes }\n      : {}),\n  };\n}\n\n/** Normalizes arbitrary span errors while preserving object-shaped messages. */\nexport function normalizeSpanError(\n  error: unknown,\n): NormalizedSpan[\"error\"] | undefined {\n  if (error === undefined) {\n    return undefined;\n  }\n\n  if (error instanceof Error) {\n    const details = normalizeMetadata(\n      error as unknown as Record<string, unknown>,\n    );\n\n    return {\n      ...(details ?? {}),\n      type: error.name,\n      message: error.message,\n    };\n  }\n\n  if (\n    error &&\n    typeof error === \"object\" &&\n    !Array.isArray(error) &&\n    typeof (error as { message?: unknown }).message === \"string\"\n  ) {\n    const normalized = normalizeMetadata(error as Record<string, unknown>);\n    const { message, type, ...details } = normalized ?? {};\n\n    return {\n      ...details,\n      message: message as string,\n      ...(typeof type === \"string\" ? { type } : {}),\n    };\n  }\n\n  const serialized = serializeError(error);\n  const { message, type, ...details } = serialized;\n\n  return {\n    ...details,\n    message: typeof message === \"string\" ? message : String(message),\n    ...(typeof type === \"string\" ? { type } : {}),\n  };\n}\n\n/** Normalizes raw span attributes into the JSON-safe span attribute shape. */\nexport function normalizeSpanAttributes(\n  attributes: Record<string, unknown>,\n): NormalizedSpanAttributes | undefined {\n  return normalizeMetadata(attributes) as NormalizedSpanAttributes | undefined;\n}\n\n/** Builds common OpenTelemetry GenAI usage attributes from a usage summary. */\nexport function createGenAiUsageAttributes(\n  usage: UsageSummary | undefined,\n  options: { provider?: string } = {},\n) {\n  return {\n    \"gen_ai.provider.name\": usage?.provider ?? options.provider,\n    \"gen_ai.request.model\": usage?.model,\n    \"gen_ai.response.model\": usage?.model,\n    \"gen_ai.usage.input_tokens\": usage?.inputTokens,\n    \"gen_ai.usage.output_tokens\": usage?.outputTokens,\n    \"gen_ai.usage.reasoning.output_tokens\": usage?.reasoningTokens,\n  } satisfies Record<string, unknown>;\n}\n\n/**\n * Converts normalized tool-call records into trace spans.\n *\n * Tool-call ids are preserved as GenAI attributes. Pass `spanIdPrefix` when the\n * spans belong to a known trace so span ids stay internally unique.\n */\nexport function createToolCallSpans(\n  calls: ToolCallRecord[],\n  options: CreateToolCallSpansOptions = {},\n): NormalizedSpan[] {\n  return calls.map((call, index) => {\n    const spanError = call.error ? normalizeSpanError(call.error) : undefined;\n    const spanId = options.spanIdPrefix\n      ? `${options.spanIdPrefix}:${index + 1}`\n      : call.id;\n\n    return {\n      ...(spanId ? { id: spanId } : {}),\n      ...(options.traceId ? { traceId: options.traceId } : {}),\n      ...(options.parentId ? { parentId: options.parentId } : {}),\n      name: call.name,\n      kind: \"tool\",\n      ...(call.startedAt ? { startedAt: call.startedAt } : {}),\n      ...(call.finishedAt ? { finishedAt: call.finishedAt } : {}),\n      ...(call.durationMs !== undefined ? { durationMs: call.durationMs } : {}),\n      status: spanError ? \"error\" : \"ok\",\n      ...(spanError ? { error: spanError } : {}),\n      attributes: normalizeSpanAttributes({\n        \"gen_ai.operation.name\": \"execute_tool\",\n        \"gen_ai.tool.name\": call.name,\n        \"gen_ai.tool.type\": \"function\",\n        ...(call.id ? { \"gen_ai.tool.call.id\": call.id } : {}),\n        ...(call.arguments !== undefined\n          ? { \"gen_ai.tool.call.arguments\": call.arguments }\n          : {}),\n        ...(call.result !== undefined\n          ? { \"gen_ai.tool.call.result\": call.result }\n          : {}),\n      }),\n    } satisfies NormalizedSpan;\n  });\n}\n\n/**\n * Attaches a fallback run trace when a harness result does not already contain spans.\n *\n * This keeps custom harnesses inspectable while first-party harness packages\n * remain free to attach richer native traces.\n */\nexport function ensureRunTrace(\n  run: HarnessRun,\n  options: EnsureRunTraceOptions,\n): NormalizedTrace | undefined {\n  if (spans(run).length > 0) {\n    return undefined;\n  }\n\n  const traceId = options.id ?? createGeneratedTraceId();\n  const rootSpanId = `${traceId}:run`;\n  const durationMs = options.finishedAt.getTime() - options.startedAt.getTime();\n  const rootError =\n    run.errors.length > 0 ? normalizeSpanError(run.errors[0]) : undefined;\n  const runSpan: NormalizedSpan = {\n    id: rootSpanId,\n    traceId,\n    name: options.name,\n    kind: \"run\",\n    startedAt: options.startedAt.toISOString(),\n    finishedAt: options.finishedAt.toISOString(),\n    durationMs,\n    status: rootError ? \"error\" : \"ok\",\n    ...(rootError ? { error: rootError } : {}),\n    attributes: normalizeSpanAttributes({\n      \"gen_ai.operation.name\": options.operationName ?? \"invoke_workflow\",\n      \"gen_ai.workflow.name\": options.name,\n      ...createGenAiUsageAttributes(run.usage),\n    }),\n  };\n  const toolSpans = createToolCallSpans(toolCalls(run.session), {\n    traceId,\n    parentId: rootSpanId,\n    spanIdPrefix: `${traceId}:tool`,\n  });\n  const trace: NormalizedTrace = {\n    id: traceId,\n    name: options.name,\n    startedAt: options.startedAt.toISOString(),\n    finishedAt: options.finishedAt.toISOString(),\n    durationMs,\n    ...(options.source ? { metadata: { source: options.source } } : {}),\n    spans: [runSpan, ...toolSpans],\n  };\n\n  run.traces = [trace];\n  return trace;\n}\n\nlet nextGeneratedTraceId = 0;\n\nfunction createGeneratedTraceId() {\n  nextGeneratedTraceId += 1;\n  return `trace_${nextGeneratedTraceId}`;\n}\n\n/**\n * Attaches a partial or complete harness run to an arbitrary thrown error.\n *\n * @param error - Thrown value to wrap.\n * @param run - Partial or complete normalized harness run to preserve.\n *\n * @example\n * ```ts\n * try {\n *   return await runAgent(input);\n * } catch (error) {\n *   throw attachHarnessRunToError(error, partialRun);\n * }\n * ```\n */\nexport function attachHarnessRunToError(\n  error: unknown,\n  run: HarnessRun,\n): HarnessRunError {\n  const baseError =\n    error instanceof Error\n      ? error\n      : new Error(String(error ?? \"Unknown error\"));\n  return Object.assign(baseError, {\n    vitestEvalsRun: run,\n  });\n}\n\n/**\n * Reads an attached harness run back off a previously wrapped error value.\n *\n * @param error - Unknown thrown value that may contain a harness run.\n *\n * @example\n * ```ts\n * const partialRun = getHarnessRunFromError(error);\n *\n * if (partialRun) {\n *   console.log(toolCalls(partialRun.session));\n * }\n * ```\n */\nexport function getHarnessRunFromError(error: unknown): HarnessRun | undefined {\n  if (\n    error &&\n    typeof error === \"object\" &&\n    \"vitestEvalsRun\" in error &&\n    isHarnessRun((error as { vitestEvalsRun?: unknown }).vitestEvalsRun)\n  ) {\n    return (error as { vitestEvalsRun: HarnessRun }).vitestEvalsRun;\n  }\n\n  return undefined;\n}\n\n/** Returns true when a value matches the normalized `HarnessRun` contract. */\nexport function isHarnessRun(value: unknown): value is HarnessRun {\n  if (!value || typeof value !== \"object\") {\n    return false;\n  }\n\n  const candidate = value as {\n    session?: unknown;\n    usage?: unknown;\n    errors?: unknown;\n  };\n\n  return (\n    isNormalizedSession(candidate.session) &&\n    Boolean(candidate.usage) &&\n    typeof candidate.usage === \"object\" &&\n    !Array.isArray(candidate.usage) &&\n    Array.isArray(candidate.errors)\n  );\n}\n\n/** Returns true when a value matches the normalized session contract. */\nexport function isNormalizedSession(\n  value: unknown,\n): value is NormalizedSession {\n  return (\n    Boolean(value) &&\n    typeof value === \"object\" &&\n    value !== null &&\n    \"messages\" in value &&\n    Array.isArray((value as { messages?: unknown }).messages)\n  );\n}\n\n/** Reuses pre-normalized harness errors when a runtime already returns them. */\nexport function resolveHarnessRunErrors(\n  result: unknown,\n): Array<Record<string, JsonValue>> {\n  if (\n    result &&\n    typeof result === \"object\" &&\n    Array.isArray((result as Record<string, unknown>).errors)\n  ) {\n    return (result as { errors: Array<Record<string, JsonValue>> }).errors;\n  }\n\n  return [];\n}\n\n/** Serializes an arbitrary thrown value into the normalized error shape. */\nexport function serializeError(error: unknown): Record<string, JsonValue> {\n  if (error instanceof Error) {\n    return {\n      type: error.name,\n      message: error.message,\n    };\n  }\n\n  return {\n    type: \"Error\",\n    message: String(error),\n  };\n}\n"],"mappings":"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","names":["assistantMessages","failedSpans","latestAssistantMessageContent","messagesByRole","spans","spansByKind","systemMessages","toolCalls","toolMessages","userMessages","toolCalls"]}