vitest-evals 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +112 -66
  2. package/dist/harness.d.mts +12 -1
  3. package/dist/harness.d.ts +12 -1
  4. package/dist/harness.js +8 -0
  5. package/dist/harness.js.map +1 -1
  6. package/dist/harness.mjs +7 -0
  7. package/dist/harness.mjs.map +1 -1
  8. package/dist/index.d.mts +48 -20
  9. package/dist/index.d.ts +48 -20
  10. package/dist/index.js +308 -21
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +304 -21
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/internal/scoring.d.mts +3 -3
  15. package/dist/internal/scoring.d.ts +3 -3
  16. package/dist/internal/scoring.js.map +1 -1
  17. package/dist/internal/toolCallScorer.js +42 -2
  18. package/dist/internal/toolCallScorer.js.map +1 -1
  19. package/dist/internal/toolCallScorer.mjs +42 -2
  20. package/dist/internal/toolCallScorer.mjs.map +1 -1
  21. package/dist/judges/factualityJudge.d.mts +151 -0
  22. package/dist/judges/factualityJudge.d.ts +151 -0
  23. package/dist/judges/factualityJudge.js +235 -0
  24. package/dist/judges/factualityJudge.js.map +1 -0
  25. package/dist/judges/factualityJudge.mjs +208 -0
  26. package/dist/judges/factualityJudge.mjs.map +1 -0
  27. package/dist/judges/index.d.mts +3 -1
  28. package/dist/judges/index.d.ts +3 -1
  29. package/dist/judges/index.js +447 -7
  30. package/dist/judges/index.js.map +1 -1
  31. package/dist/judges/index.mjs +443 -6
  32. package/dist/judges/index.mjs.map +1 -1
  33. package/dist/judges/judgeHarness.d.mts +122 -0
  34. package/dist/judges/judgeHarness.d.ts +122 -0
  35. package/dist/judges/judgeHarness.js +303 -0
  36. package/dist/judges/judgeHarness.js.map +1 -0
  37. package/dist/judges/judgeHarness.mjs +274 -0
  38. package/dist/judges/judgeHarness.mjs.map +1 -0
  39. package/dist/judges/structuredOutputJudge.d.mts +1 -0
  40. package/dist/judges/structuredOutputJudge.d.ts +1 -0
  41. package/dist/judges/toolCallJudge.d.mts +1 -0
  42. package/dist/judges/toolCallJudge.d.ts +1 -0
  43. package/dist/judges/toolCallJudge.js +42 -2
  44. package/dist/judges/toolCallJudge.js.map +1 -1
  45. package/dist/judges/toolCallJudge.mjs +42 -2
  46. package/dist/judges/toolCallJudge.mjs.map +1 -1
  47. package/dist/judges/types.d.mts +33 -6
  48. package/dist/judges/types.d.ts +33 -6
  49. package/dist/judges/types.js.map +1 -1
  50. package/dist/legacy/scorers/index.js +42 -2
  51. package/dist/legacy/scorers/index.js.map +1 -1
  52. package/dist/legacy/scorers/index.mjs +42 -2
  53. package/dist/legacy/scorers/index.mjs.map +1 -1
  54. package/dist/legacy/scorers/toolCallScorer.js +42 -2
  55. package/dist/legacy/scorers/toolCallScorer.js.map +1 -1
  56. package/dist/legacy/scorers/toolCallScorer.mjs +42 -2
  57. package/dist/legacy/scorers/toolCallScorer.mjs.map +1 -1
  58. package/dist/legacy.js +56 -3
  59. package/dist/legacy.js.map +1 -1
  60. package/dist/legacy.mjs +56 -3
  61. package/dist/legacy.mjs.map +1 -1
  62. package/dist/replay.js +1 -1
  63. package/dist/replay.js.map +1 -1
  64. package/dist/replay.mjs +1 -1
  65. package/dist/replay.mjs.map +1 -1
  66. package/dist/reporter.js.map +1 -1
  67. package/dist/reporter.mjs.map +1 -1
  68. package/package.json +1 -1
@@ -1 +1 @@
1
- {"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 TOOL_DETAIL_ENV = \"VITEST_EVALS_TOOL_DETAILS\";\nconst TOOL_DETAIL_LEVEL_ENV = \"VITEST_EVALS_TOOL_DETAILS_LEVEL\";\nconst DEFAULT_TOOL_DETAIL_LEVEL = 0;\n\ntype EvalReporterOptions = {\n isTTY?: boolean;\n silent?: boolean | \"passed-only\";\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.toolDetails);\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(\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","/** Primitive scalar values allowed in normalized JSON-safe eval data. */\nexport type JsonPrimitive = string | number | boolean | null;\n\n/** JSON-safe value shape used by normalized sessions, artifacts, and errors. */\nexport type JsonValue =\n | JsonPrimitive\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/**\n * Normalized record for one tool call observed during a harness run.\n *\n * @example\n * ```ts\n * const call: ToolCallRecord = {\n * name: \"lookupInvoice\",\n * arguments: { invoiceId: \"inv_123\" },\n * result: { refundable: true },\n * };\n * ```\n */\nexport type ToolCallRecord = {\n /** Provider or runtime tool-call id when one is available. */\n id?: string;\n /** Tool name as exposed to the agent or application runtime. */\n name: string;\n /** JSON-safe tool arguments after provider/runtime normalization. */\n arguments?: Record<string, JsonValue>;\n /** JSON-safe tool result returned by the application tool. */\n result?: JsonValue;\n /** Normalized tool error when execution failed. */\n error?: {\n message: string;\n type?: string;\n [key: string]: JsonValue | undefined;\n };\n /** ISO timestamp for the start of tool execution. */\n startedAt?: string;\n /** ISO timestamp for the end of tool execution. */\n finishedAt?: string;\n /** Tool execution duration in milliseconds. */\n durationMs?: number;\n /** Extra JSON-safe tool metadata for reporters and custom judges. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * Normalized message recorded in a harness session transcript.\n *\n * @example\n * ```ts\n * const message: NormalizedMessage = {\n * role: \"assistant\",\n * content: { status: \"approved\" },\n * toolCalls: [{ name: \"lookupInvoice\" }],\n * };\n * ```\n */\nexport type NormalizedMessage = {\n /** Transcript role for the normalized message. */\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n /** JSON-safe message content. */\n content?: JsonValue;\n /** Tool calls associated with this message. */\n toolCalls?: ToolCallRecord[];\n /** Extra JSON-safe message metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * Provider usage summary attached to a normalized harness run.\n *\n * @example\n * ```ts\n * const usage: UsageSummary = {\n * provider: \"openai\",\n * model: \"gpt-4o-mini\",\n * inputTokens: 212,\n * outputTokens: 48,\n * totalTokens: 260,\n * };\n * ```\n */\nexport type UsageSummary = {\n /** Provider that served the application run. */\n provider?: string;\n /** Model used for the application run. */\n model?: string;\n /** Input, prompt, or request tokens consumed by the run. */\n inputTokens?: number;\n /** Output or completion tokens produced by the run. */\n outputTokens?: number;\n /** Reasoning tokens reported by providers that expose them. */\n reasoningTokens?: number;\n /** Total token count reported by the provider or adapter. */\n totalTokens?: number;\n /** Count of tool calls observed during the run. */\n toolCalls?: number;\n /** Retry count observed during the run. */\n retries?: number;\n /** Provider-specific JSON-safe usage details. Cost estimates belong here. */\n metadata?: Record<string, JsonValue>;\n};\n\n/** Timing summary attached to a normalized harness run. */\nexport type TimingSummary = {\n /** End-to-end run duration in milliseconds. */\n totalMs?: number;\n /** Extra JSON-safe timing metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * JSON-serializable transcript produced by the system under test.\n *\n * @example\n * ```ts\n * const session: NormalizedSession = {\n * provider: \"openai\",\n * model: \"gpt-4o-mini\",\n * messages: [\n * { role: \"user\", content: \"Refund invoice inv_123\" },\n * { role: \"assistant\", content: { status: \"approved\" } },\n * ],\n * };\n * ```\n */\nexport type NormalizedSession = {\n /** Ordered normalized transcript messages. */\n messages: NormalizedMessage[];\n /** Provider that produced the session when known. */\n provider?: string;\n /** Model that produced the session when known. */\n model?: string;\n /** Extra JSON-safe session metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\ntype OutputField<TOutput extends JsonValue | undefined> =\n undefined extends TOutput ? { output?: TOutput } : { output: TOutput };\n\n/**\n * Normalized result returned by every harness execution.\n *\n * @example\n * ```ts\n * const run: HarnessRun<{ status: \"approved\" }> = {\n * output: { status: \"approved\" },\n * session: {\n * messages: [\n * { role: \"user\", content: \"Refund invoice inv_123\" },\n * { role: \"assistant\", content: { status: \"approved\" } },\n * ],\n * },\n * usage: { totalTokens: 260 },\n * errors: [],\n * };\n * ```\n */\nexport type HarnessRun<\n TOutput extends JsonValue | undefined = JsonValue | undefined,\n> = OutputField<TOutput> & {\n /** Normalized transcript and provider/session metadata. */\n session: NormalizedSession;\n /** Stable provider usage units such as tokens, tools, and retries. */\n usage: UsageSummary;\n /** Optional timing summary for the run. */\n timings?: TimingSummary;\n /** JSON-safe run artifacts captured by the harness or test context. */\n artifacts?: Record<string, JsonValue>;\n /** Normalized errors captured during execution. */\n errors: Array<Record<string, JsonValue>>;\n};\n\n/** Error value with an attached partial or complete normalized harness run. */\nexport type HarnessRunError = Error & {\n /** Attached normalized harness run recovered by `getHarnessRunFromError(...)`. */\n vitestEvalsRun: HarnessRun;\n};\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/**\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 /** 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 === \"number\" ||\n typeof value === \"boolean\"\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[]): JsonValue[] {\n return value.map((item) => {\n const normalized = toJsonValue(item);\n return normalized === undefined ? null : normalized;\n });\n}\n\nfunction normalizeJsonObject(\n value: Record<string, unknown>,\n): Record<string, JsonValue> {\n const normalized: Record<string, JsonValue> = {};\n\n for (const [key, entryValue] of Object.entries(value)) {\n const entry = toJsonValue(entryValue);\n if (entry !== undefined) {\n normalized[key] = entry;\n }\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 if (isJsonPrimitive(value)) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return normalizeJsonArray(value);\n }\n\n if (isJsonRecord(value)) {\n return normalizeJsonObject(value);\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);\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 result = await options.run({\n input,\n metadata: context.metadata,\n signal: context.signal,\n artifacts: context.artifacts,\n setArtifact: context.setArtifact,\n });\n\n return normalizeHarnessRun(input, result, context);\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\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 errors: normalizeSimpleErrors(result.errors),\n } as HarnessRun<TOutput>;\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\n/**\n * Flattens every recorded tool call from a normalized session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const names = toolCalls(result.session).map((call) => call.name);\n *\n * expect(names).toEqual([\"lookupInvoice\", \"createRefund\"]);\n * ```\n */\nexport function toolCalls(session: NormalizedSession): ToolCallRecord[] {\n return session.messages.flatMap((message) => message.toolCalls ?? []);\n}\n\n/**\n * Filters normalized session messages by role.\n *\n * @param session - Normalized session produced by a harness run.\n * @param role - Message role to keep.\n *\n * @example\n * ```ts\n * const assistantText = messagesByRole(result.session, \"assistant\")\n * .map((message) => message.content)\n * .join(\"\\n\");\n * ```\n */\nexport function messagesByRole(\n session: NormalizedSession,\n role: NormalizedMessage[\"role\"],\n): NormalizedMessage[] {\n return session.messages.filter((message) => message.role === role);\n}\n\n/**\n * Returns every normalized system message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const systemPrompts = systemMessages(result.session);\n * ```\n */\nexport function systemMessages(session: NormalizedSession) {\n return messagesByRole(session, \"system\");\n}\n\n/**\n * Returns every normalized user message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const firstPrompt = userMessages(result.session)[0]?.content;\n * ```\n */\nexport function userMessages(session: NormalizedSession) {\n return messagesByRole(session, \"user\");\n}\n\n/**\n * Returns every normalized assistant message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const finalAnswer = assistantMessages(result.session).at(-1)?.content;\n * ```\n */\nexport function assistantMessages(session: NormalizedSession) {\n return messagesByRole(session, \"assistant\");\n}\n\n/**\n * Returns every normalized tool message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const toolOutputs = toolMessages(result.session).map((message) => message.content);\n * ```\n */\nexport function toolMessages(session: NormalizedSession) {\n return messagesByRole(session, \"tool\");\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":[]}
1
+ {"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 TOOL_DETAIL_ENV = \"VITEST_EVALS_TOOL_DETAILS\";\nconst TOOL_DETAIL_LEVEL_ENV = \"VITEST_EVALS_TOOL_DETAILS_LEVEL\";\nconst DEFAULT_TOOL_DETAIL_LEVEL = 0;\n\ntype EvalReporterOptions = {\n isTTY?: boolean;\n silent?: boolean | \"passed-only\";\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.toolDetails);\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(\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","/** Primitive scalar values allowed in normalized JSON-safe eval data. */\nexport type JsonPrimitive = string | number | boolean | null;\n\n/** JSON-safe value shape used by normalized sessions, artifacts, and errors. */\nexport type JsonValue =\n | JsonPrimitive\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/**\n * Normalized record for one tool call observed during a harness run.\n *\n * @example\n * ```ts\n * const call: ToolCallRecord = {\n * name: \"lookupInvoice\",\n * arguments: { invoiceId: \"inv_123\" },\n * result: { refundable: true },\n * };\n * ```\n */\nexport type ToolCallRecord = {\n /** Provider or runtime tool-call id when one is available. */\n id?: string;\n /** Tool name as exposed to the agent or application runtime. */\n name: string;\n /** JSON-safe tool arguments after provider/runtime normalization. */\n arguments?: Record<string, JsonValue>;\n /** JSON-safe tool result returned by the application tool. */\n result?: JsonValue;\n /** Normalized tool error when execution failed. */\n error?: {\n message: string;\n type?: string;\n [key: string]: JsonValue | undefined;\n };\n /** ISO timestamp for the start of tool execution. */\n startedAt?: string;\n /** ISO timestamp for the end of tool execution. */\n finishedAt?: string;\n /** Tool execution duration in milliseconds. */\n durationMs?: number;\n /** Extra JSON-safe tool metadata for reporters and custom judges. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * Normalized message recorded in a harness session transcript.\n *\n * @example\n * ```ts\n * const message: NormalizedMessage = {\n * role: \"assistant\",\n * content: { status: \"approved\" },\n * toolCalls: [{ name: \"lookupInvoice\" }],\n * };\n * ```\n */\nexport type NormalizedMessage = {\n /** Transcript role for the normalized message. */\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n /** JSON-safe message content. */\n content?: JsonValue;\n /** Tool calls associated with this message. */\n toolCalls?: ToolCallRecord[];\n /** Extra JSON-safe message metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * Provider usage summary attached to a normalized harness run.\n *\n * @example\n * ```ts\n * const usage: UsageSummary = {\n * provider: \"openai\",\n * model: \"gpt-4o-mini\",\n * inputTokens: 212,\n * outputTokens: 48,\n * totalTokens: 260,\n * };\n * ```\n */\nexport type UsageSummary = {\n /** Provider that served the application run. */\n provider?: string;\n /** Model used for the application run. */\n model?: string;\n /** Input, prompt, or request tokens consumed by the run. */\n inputTokens?: number;\n /** Output or completion tokens produced by the run. */\n outputTokens?: number;\n /** Reasoning tokens reported by providers that expose them. */\n reasoningTokens?: number;\n /** Total token count reported by the provider or adapter. */\n totalTokens?: number;\n /** Count of tool calls observed during the run. */\n toolCalls?: number;\n /** Retry count observed during the run. */\n retries?: number;\n /** Provider-specific JSON-safe usage details. Cost estimates belong here. */\n metadata?: Record<string, JsonValue>;\n};\n\n/** Timing summary attached to a normalized harness run. */\nexport type TimingSummary = {\n /** End-to-end run duration in milliseconds. */\n totalMs?: number;\n /** Extra JSON-safe timing metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\n/**\n * JSON-serializable transcript produced by the system under test.\n *\n * @example\n * ```ts\n * const session: NormalizedSession = {\n * provider: \"openai\",\n * model: \"gpt-4o-mini\",\n * messages: [\n * { role: \"user\", content: \"Refund invoice inv_123\" },\n * { role: \"assistant\", content: { status: \"approved\" } },\n * ],\n * };\n * ```\n */\nexport type NormalizedSession = {\n /** Ordered normalized transcript messages. */\n messages: NormalizedMessage[];\n /** Provider that produced the session when known. */\n provider?: string;\n /** Model that produced the session when known. */\n model?: string;\n /** Extra JSON-safe session metadata. */\n metadata?: Record<string, JsonValue>;\n};\n\ntype OutputField<TOutput extends JsonValue | undefined> =\n undefined extends TOutput ? { output?: TOutput } : { output: TOutput };\n\n/**\n * Normalized result returned by every harness execution.\n *\n * @example\n * ```ts\n * const run: HarnessRun<{ status: \"approved\" }> = {\n * output: { status: \"approved\" },\n * session: {\n * messages: [\n * { role: \"user\", content: \"Refund invoice inv_123\" },\n * { role: \"assistant\", content: { status: \"approved\" } },\n * ],\n * },\n * usage: { totalTokens: 260 },\n * errors: [],\n * };\n * ```\n */\nexport type HarnessRun<\n TOutput extends JsonValue | undefined = JsonValue | undefined,\n> = OutputField<TOutput> & {\n /** Normalized transcript and provider/session metadata. */\n session: NormalizedSession;\n /** Stable provider usage units such as tokens, tools, and retries. */\n usage: UsageSummary;\n /** Optional timing summary for the run. */\n timings?: TimingSummary;\n /** JSON-safe run artifacts captured by the harness or test context. */\n artifacts?: Record<string, JsonValue>;\n /** Normalized errors captured during execution. */\n errors: Array<Record<string, JsonValue>>;\n};\n\n/** Error value with an attached partial or complete normalized harness run. */\nexport type HarnessRunError = Error & {\n /** Attached normalized harness run recovered by `getHarnessRunFromError(...)`. */\n vitestEvalsRun: HarnessRun;\n};\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/**\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 /** 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 === \"number\" ||\n typeof value === \"boolean\"\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[]): JsonValue[] {\n return value.map((item) => {\n const normalized = toJsonValue(item);\n return normalized === undefined ? null : normalized;\n });\n}\n\nfunction normalizeJsonObject(\n value: Record<string, unknown>,\n): Record<string, JsonValue> {\n const normalized: Record<string, JsonValue> = {};\n\n for (const [key, entryValue] of Object.entries(value)) {\n const entry = toJsonValue(entryValue);\n if (entry !== undefined) {\n normalized[key] = entry;\n }\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 if (isJsonPrimitive(value)) {\n return value;\n }\n\n if (Array.isArray(value)) {\n return normalizeJsonArray(value);\n }\n\n if (isJsonRecord(value)) {\n return normalizeJsonObject(value);\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);\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 result = await options.run({\n input,\n metadata: context.metadata,\n signal: context.signal,\n artifacts: context.artifacts,\n setArtifact: context.setArtifact,\n });\n\n return normalizeHarnessRun(input, result, context);\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\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 errors: normalizeSimpleErrors(result.errors),\n } as HarnessRun<TOutput>;\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\n/**\n * Flattens every recorded tool call from a normalized session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const names = toolCalls(result.session).map((call) => call.name);\n *\n * expect(names).toEqual([\"lookupInvoice\", \"createRefund\"]);\n * ```\n */\nexport function toolCalls(session: NormalizedSession): ToolCallRecord[] {\n return session.messages.flatMap((message) => message.toolCalls ?? []);\n}\n\n/**\n * Filters normalized session messages by role.\n *\n * @param session - Normalized session produced by a harness run.\n * @param role - Message role to keep.\n *\n * @example\n * ```ts\n * const assistantText = messagesByRole(result.session, \"assistant\")\n * .map((message) => message.content)\n * .join(\"\\n\");\n * ```\n */\nexport function messagesByRole(\n session: NormalizedSession,\n role: NormalizedMessage[\"role\"],\n): NormalizedMessage[] {\n return session.messages.filter((message) => message.role === role);\n}\n\nfunction hasNonEmptyMessageContent(message: NormalizedMessage) {\n return (\n message.content !== undefined &&\n (typeof message.content !== \"string\" || message.content.trim().length > 0)\n );\n}\n\n/**\n * Returns every normalized system message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const systemPrompts = systemMessages(result.session);\n * ```\n */\nexport function systemMessages(session: NormalizedSession) {\n return messagesByRole(session, \"system\");\n}\n\n/**\n * Returns every normalized user message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const firstPrompt = userMessages(result.session)[0]?.content;\n * ```\n */\nexport function userMessages(session: NormalizedSession) {\n return messagesByRole(session, \"user\");\n}\n\n/**\n * Returns every normalized assistant message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const finalAnswer = assistantMessages(result.session).at(-1)?.content;\n * ```\n */\nexport function assistantMessages(session: NormalizedSession) {\n return messagesByRole(session, \"assistant\");\n}\n\n/**\n * Returns the latest assistant message content, ignoring empty text messages.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const finalAnswer = latestAssistantMessageContent(result.session);\n * ```\n */\nexport function latestAssistantMessageContent(session: NormalizedSession) {\n return [...assistantMessages(session)]\n .reverse()\n .find(hasNonEmptyMessageContent)?.content;\n}\n\n/**\n * Returns every normalized tool message from a session.\n *\n * @param session - Normalized session produced by a harness run.\n *\n * @example\n * ```ts\n * const toolOutputs = toolMessages(result.session).map((message) => message.content);\n * ```\n */\nexport function toolMessages(session: NormalizedSession) {\n return messagesByRole(session, \"tool\");\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-evals",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Harness-backed AI testing on top of Vitest.",
5
5
  "repository": {
6
6
  "type": "git",