diff --git a/apps/cli/src/commands/results/serve.ts b/apps/cli/src/commands/results/serve.ts index 354990faa..4851f11be 100644 --- a/apps/cli/src/commands/results/serve.ts +++ b/apps/cli/src/commands/results/serve.ts @@ -695,6 +695,23 @@ async function handleCompare(c: C, { searchDir, agentvDir }: DataContext) { } } + // ── Baseline delta / normalized-gain computation ───────────────────── + const baselineTarget = c.req.query('baseline') ?? ''; + if (baselineTarget && !targetsSet.has(baselineTarget)) { + return c.json({ error: `Baseline target "${baselineTarget}" does not exist in the data` }, 400); + } + + // Build baseline lookup before constructing cells so we can include + // delta/normalized_gain in the initial cell objects (no mutation needed). + const baselineScores = new Map(); + if (baselineTarget) { + for (const entry of cellMap.values()) { + if (entry.target === baselineTarget && entry.evalCount > 0) { + baselineScores.set(entry.experiment, entry.scoreSum / entry.evalCount); + } + } + } + const cells = [...cellMap.values()].map((entry) => { // Deduplicate tests: keep only the latest entry per test_id (last wins by insertion order) const dedupMap = new Map(); @@ -706,15 +723,28 @@ async function handleCompare(c: C, { searchDir, agentvDir }: DataContext) { // Cap to most recent entries to prevent unbounded payloads const cappedTests = dedupedTests.slice(-MAX_TESTS_PER_CELL); - return { + const avgScore = entry.evalCount > 0 ? entry.scoreSum / entry.evalCount : 0; + const cell: Record = { experiment: entry.experiment, target: entry.target, eval_count: entry.evalCount, passed_count: entry.passedCount, pass_rate: entry.evalCount > 0 ? entry.passedCount / entry.evalCount : 0, - avg_score: entry.evalCount > 0 ? entry.scoreSum / entry.evalCount : 0, + avg_score: avgScore, tests: cappedTests, }; + + // Append baseline comparison fields when a baseline is selected + if (baselineTarget && entry.target !== baselineTarget) { + const baseAvg = baselineScores.get(entry.experiment); + if (baseAvg !== undefined) { + cell.delta = Math.round((avgScore - baseAvg) * 1000) / 1000; + cell.normalized_gain = + baseAvg >= 1.0 ? null : Math.round(((avgScore - baseAvg) / (1 - baseAvg)) * 1000) / 1000; + } + } + + return cell; }); // Per-run entries sorted by timestamp descending (newest first). diff --git a/apps/studio/package.json b/apps/studio/package.json index 110f5c5ee..c1b4f2107 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -13,7 +13,8 @@ "@tanstack/react-query": "^5.75.5", "@tanstack/react-router": "^1.120.3", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "recharts": "^3.8.1" }, "devDependencies": { "@tailwindcss/vite": "^4.1.7", diff --git a/apps/studio/src/components/AnalyticsCharts.tsx b/apps/studio/src/components/AnalyticsCharts.tsx new file mode 100644 index 000000000..3bbd831ab --- /dev/null +++ b/apps/studio/src/components/AnalyticsCharts.tsx @@ -0,0 +1,567 @@ +/** + * Analytics charts section for the Compare (Analytics) tab. + * + * Renders a collapsible section below the aggregated matrix with a + * baseline-target selector dropdown. When a baseline is selected, + * the component fetches comparison data with delta / normalized-gain + * fields and renders the following charts: + * + * 1. Normalized gain bar chart (horizontal bars, g per task × target) + * 2. Domain/tag heatmap (pass rate by tag × target) + * 3. Negative delta table (tasks where non-baseline scored worse) + * 4. Score distribution histogram (score variance for a single run) + * 5. Trend-over-time line chart (mean score per target over time) + * + * All charts use recharts styled with Tailwind-matching colors to + * respect the Studio dark theme (gray-950 canvas, cyan accents, + * emerald/red data tones). + */ + +import { useQuery } from '@tanstack/react-query'; +import { useMemo, useState } from 'react'; +import { + Bar, + BarChart, + CartesianGrid, + Cell, + Legend, + Line, + LineChart, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; + +import { benchmarkCompareOptions, compareOptionsWithBaseline } from '~/lib/api'; +import type { CompareResponse, CompareRunEntry } from '~/lib/types'; + +// ── Color palette matching Studio DESIGN.md ──────────────────────────── + +const COLORS = { + green: '#34d399', // emerald-400 + red: '#f87171', // red-400 + yellow: '#facc15', // yellow-400 + gray: '#4b5563', // gray-600 + cyan: '#22d3ee', // cyan-400 + gridLine: '#1f2937', // gray-800 + labelText: '#9ca3af', // gray-400 + tooltipBg: '#111827', // gray-900 + tooltipBorder: '#374151', // gray-700 +}; + +// A set of distinguishable target colors for multi-target charts +const TARGET_COLORS = [ + '#22d3ee', // cyan-400 + '#34d399', // emerald-400 + '#facc15', // yellow-400 + '#f87171', // red-400 + '#a78bfa', // violet-400 + '#fb923c', // orange-400 + '#f472b6', // pink-400 + '#60a5fa', // blue-400 +]; + +function targetColor(idx: number): string { + return TARGET_COLORS[idx % TARGET_COLORS.length]; +} + +// ── Types ────────────────────────────────────────────────────────────── + +interface AnalyticsChartsProps { + /** Unfiltered compare response (no baseline). Used for tag heatmap, histogram, etc. */ + data: CompareResponse; + /** Benchmark scope. Undefined for unscoped root view. */ + benchmarkId?: string; +} + +// ── Main component ───────────────────────────────────────────────────── + +export function AnalyticsCharts({ data, benchmarkId }: AnalyticsChartsProps) { + const [collapsed, setCollapsed] = useState(true); + const [baseline, setBaseline] = useState(''); + const targets = data.targets; + + // Fetch compare data with baseline param when a baseline is selected + const baselineQuery = useQuery( + benchmarkId + ? benchmarkCompareOptions(benchmarkId, baseline || undefined) + : compareOptionsWithBaseline(baseline || undefined), + ); + const baselineData = baseline ? baselineQuery.data : undefined; + + // Trend data from existing runs (sorted by timestamp) + const trendData = useMemo(() => buildTrendData(data.runs ?? []), [data.runs]); + + return ( +
+ + + {!collapsed && ( +
+ {/* Baseline selector */} +
+ + + {baseline && baselineQuery.isLoading && ( + Loading… + )} +
+ + {/* 1. Normalized gain bar chart */} + {baseline && baselineData && ( + + )} + + {/* 2. Tag heatmap */} + {data.runs && data.runs.length > 0 && targets.length > 1 && ( + + )} + + {/* 3. Negative delta table */} + {baseline && baselineData && ( + + )} + + {/* 4. Score distribution histogram */} + + + {/* 5. Trend over time */} + {trendData.length > 1 && targets.length > 0 && ( + + )} + + {!baseline && ( +

+ Select a baseline target above to see gain and delta charts. +

+ )} +
+ )} +
+ ); +} + +// ── Custom tooltip ───────────────────────────────────────────────────── + +function ChartTooltip({ + active, + payload, + label, +}: { + active?: boolean; + payload?: Array<{ name: string; value: number; color: string }>; + label?: string; +}) { + if (!active || !payload?.length) return null; + return ( +
+ {label &&
{label}
} + {payload.map((p, i) => ( +
+ + {p.name}: + + {typeof p.value === 'number' ? p.value.toFixed(3) : p.value} + +
+ ))} +
+ ); +} + +// ── Section wrapper ──────────────────────────────────────────────────── + +function ChartSection({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
+

{title}

+ {children} +
+ ); +} + +// ── 1. Normalized gain bar chart ─────────────────────────────────────── + +interface GainRow { + experiment: string; + target: string; + g: number | null; + delta: number; +} + +function NormalizedGainChart({ data, baseline }: { data: CompareResponse; baseline: string }) { + const rows = useMemo(() => { + const result: GainRow[] = []; + for (const cell of data.cells) { + if (cell.target === baseline) continue; + if (cell.delta === undefined) continue; + result.push({ + experiment: cell.experiment, + target: cell.target, + g: cell.normalized_gain ?? null, + delta: cell.delta, + }); + } + // Sort by absolute gain descending + result.sort((a, b) => Math.abs(b.g ?? 0) - Math.abs(a.g ?? 0)); + return result; + }, [data.cells, baseline]); + + if (rows.length === 0) { + return ( + +

+ No gain data available. Ensure multiple targets exist. +

+
+ ); + } + + const chartData = rows.map((r) => ({ + name: `${r.target} · ${r.experiment}`, + g: r.g ?? 0, + isNull: r.g === null, + })); + + return ( + +
+ + + + + + } /> + + {chartData.map((entry) => ( + 0 + ? COLORS.green + : entry.g < 0 + ? COLORS.red + : COLORS.gray + } + /> + ))} + + + +
+
+ ); +} + +// ── 2. Tag heatmap ───────────────────────────────────────────────────── + +function TagHeatmap({ runs, targets }: { runs: CompareRunEntry[]; targets: string[] }) { + const { tags, grid } = useMemo(() => { + // Collect all tags and compute pass rate per (tag, target) + const tagTargetMap = new Map>(); + for (const run of runs) { + for (const tag of run.tags ?? []) { + let targetMap = tagTargetMap.get(tag); + if (!targetMap) { + targetMap = new Map(); + tagTargetMap.set(tag, targetMap); + } + let entry = targetMap.get(run.target); + if (!entry) { + entry = { passed: 0, total: 0 }; + targetMap.set(run.target, entry); + } + entry.passed += run.passed_count; + entry.total += run.eval_count; + } + } + const allTags = [...tagTargetMap.keys()].sort(); + const gridData = allTags.map((tag) => { + const row: Record = { tag }; + const targetMap = tagTargetMap.get(tag); + if (!targetMap) return row; + for (const target of targets) { + const entry = targetMap.get(target); + row[target] = entry && entry.total > 0 ? entry.passed / entry.total : -1; + } + return row; + }); + return { tags: allTags, grid: gridData }; + }, [runs, targets]); + + if (tags.length === 0) return null; + + return ( + +
+ + + + + {targets.map((t) => ( + + ))} + + + + {grid.map((row) => ( + + + {targets.map((target) => { + const val = row[target] as number; + if (val < 0) { + return ( + + ); + } + const pct = Math.round(val * 100); + const colorClass = + val >= 0.8 + ? 'bg-emerald-400/20 text-emerald-400' + : val >= 0.5 + ? 'bg-yellow-400/20 text-yellow-400' + : 'bg-red-400/20 text-red-400'; + return ( + + ); + })} + + ))} + +
Tag + {t} +
{row.tag as string} + — + + + {pct}% + +
+
+
+ ); +} + +// ── 3. Negative delta table ──────────────────────────────────────────── + +function NegativeDeltaTable({ data, baseline }: { data: CompareResponse; baseline: string }) { + const negatives = useMemo(() => { + return data.cells + .filter((c) => c.target !== baseline && c.delta !== undefined && c.delta < 0) + .sort((a, b) => (a.delta ?? 0) - (b.delta ?? 0)); + }, [data.cells, baseline]); + + if (negatives.length === 0) return null; + + return ( + +
+ + + + + + + + + + + + {negatives.map((cell) => ( + + + + + + + + ))} + +
ExperimentTargetAvg ScoreDeltag
{cell.experiment}{cell.target} + {Math.round(cell.avg_score * 100)}% + + {cell.delta?.toFixed(3)} + + {cell.normalized_gain !== undefined && cell.normalized_gain !== null + ? cell.normalized_gain.toFixed(3) + : '—'} +
+
+
+ ); +} + +// ── 4. Score distribution histogram ──────────────────────────────────── + +function ScoreDistribution({ data }: { data: CompareResponse }) { + const histData = useMemo(() => { + // Collect all test scores from all cells + const scores: number[] = []; + for (const cell of data.cells) { + for (const t of cell.tests) { + scores.push(t.score); + } + } + if (scores.length === 0) return []; + + // Build 10-bucket histogram (0-10%, 10-20%, ..., 90-100%) + const buckets = Array.from({ length: 10 }, (_, i) => ({ + range: `${i * 10}–${(i + 1) * 10}%`, + count: 0, + })); + for (const s of scores) { + const idx = Math.min(Math.floor(s * 10), 9); + buckets[idx].count++; + } + return buckets; + }, [data.cells]); + + if (histData.length === 0) return null; + + return ( + +
+ + + + + + } /> + + + +
+
+ ); +} + +// ── 5. Trend over time ───────────────────────────────────────────────── + +interface TrendPoint { + date: string; + [target: string]: number | string; +} + +function buildTrendData(runs: CompareRunEntry[]): TrendPoint[] { + if (runs.length === 0) return []; + + // Group runs by date (day granularity) and target + const dateMap = new Map>(); + for (const run of runs) { + const date = run.started_at.slice(0, 10); // YYYY-MM-DD + let targetMap = dateMap.get(date); + if (!targetMap) { + targetMap = new Map(); + dateMap.set(date, targetMap); + } + let entry = targetMap.get(run.target); + if (!entry) { + entry = { scoreSum: 0, count: 0 }; + targetMap.set(run.target, entry); + } + entry.scoreSum += run.avg_score; + entry.count++; + } + + // Convert to array sorted by date + const sortedDates = [...dateMap.keys()].sort(); + return sortedDates.map((date) => { + const row: TrendPoint = { date }; + const targetMap = dateMap.get(date); + if (!targetMap) return row; + for (const [target, entry] of targetMap) { + row[target] = Math.round((entry.scoreSum / entry.count) * 1000) / 1000; + } + return row; + }); +} + +function TrendOverTime({ data, targets }: { data: TrendPoint[]; targets: string[] }) { + return ( + +
+ + + + + + } /> + + {targets.map((target, idx) => ( + + ))} + + +
+
+ ); +} diff --git a/apps/studio/src/components/CompareTab.tsx b/apps/studio/src/components/AnalyticsTab.tsx similarity index 98% rename from apps/studio/src/components/CompareTab.tsx rename to apps/studio/src/components/AnalyticsTab.tsx index 994816a8f..4461b30fe 100644 --- a/apps/studio/src/components/CompareTab.tsx +++ b/apps/studio/src/components/AnalyticsTab.tsx @@ -1,5 +1,5 @@ /** - * Cross-model comparison view. + * Analytics tab — cross-model comparison view. * * Two modes: * 1. Aggregated (default) — `(experiment, target)` matrix, one cell per pair. @@ -29,9 +29,10 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { deleteRunTagsApi, saveRunTagsApi } from '~/lib/api'; import type { CompareCell, CompareResponse, CompareRunEntry, CompareTestResult } from '~/lib/types'; +import { AnalyticsCharts } from './AnalyticsCharts'; import { PassRatePill } from './PassRatePill'; -interface CompareTabProps { +interface AnalyticsTabProps { data: CompareResponse | undefined; isLoading: boolean; isError?: boolean; @@ -46,14 +47,14 @@ type ViewMode = 'aggregated' | 'per-run'; // ── Top-level container ───────────────────────────────────────────────── -export function CompareTab({ +export function AnalyticsTab({ data, isLoading, isError, error, benchmarkId, readOnly, -}: CompareTabProps) { +}: AnalyticsTabProps) { const [mode, setMode] = useState('aggregated'); const [filterTags, setFilterTags] = useState([]); @@ -178,7 +179,9 @@ export function CompareTab({ ) : ( filteredData && ( <> - {mode === 'aggregated' && } + {mode === 'aggregated' && ( + + )} {mode === 'per-run' && (
-

Compare runs

+

Analyze runs

Study one experiment against another, or pit individual runs head-to-head.

@@ -355,7 +358,7 @@ function ModeButton({ // ── Aggregated (matrix) view ──────────────────────────────────────────── -function AggregatedView({ data }: { data: CompareResponse }) { +function AggregatedView({ data, benchmarkId }: { data: CompareResponse; benchmarkId?: string }) { const { experiments, targets, cells } = data; // Hooks must run on every render regardless of the early-return below, @@ -407,6 +410,7 @@ function AggregatedView({ data }: { data: CompareResponse }) {
+ ); } diff --git a/apps/studio/src/lib/api.ts b/apps/studio/src/lib/api.ts index c770bb98a..e302096d9 100644 --- a/apps/studio/src/lib/api.ts +++ b/apps/studio/src/lib/api.ts @@ -92,6 +92,15 @@ export const experimentsOptions = queryOptions({ queryFn: () => fetchJson('/api/experiments'), }); +export function compareOptionsWithBaseline(baseline?: string) { + return queryOptions({ + queryKey: ['compare', 'baseline', baseline ?? ''], + queryFn: () => + fetchJson(`/api/compare?baseline=${encodeURIComponent(baseline ?? '')}`), + enabled: !!baseline, + }); +} + export const compareOptions = queryOptions({ queryKey: ['compare'], queryFn: () => fetchJson('/api/compare'), @@ -402,10 +411,18 @@ export function benchmarkExperimentsOptions(benchmarkId: string) { }); } -export function benchmarkCompareOptions(benchmarkId: string) { +export function benchmarkCompareOptions(benchmarkId: string, baseline?: string) { + const base = `${benchmarkApiBase(benchmarkId)}/compare`; + if (baseline) { + return queryOptions({ + queryKey: ['benchmarks', benchmarkId, 'compare', 'baseline', baseline], + queryFn: () => fetchJson(`${base}?baseline=${encodeURIComponent(baseline)}`), + enabled: !!benchmarkId, + }); + } return queryOptions({ queryKey: ['benchmarks', benchmarkId, 'compare'], - queryFn: () => fetchJson(`${benchmarkApiBase(benchmarkId)}/compare`), + queryFn: () => fetchJson(base), enabled: !!benchmarkId, }); } diff --git a/apps/studio/src/lib/types.ts b/apps/studio/src/lib/types.ts index 1f2d81ec6..98dfd03b0 100644 --- a/apps/studio/src/lib/types.ts +++ b/apps/studio/src/lib/types.ts @@ -148,6 +148,10 @@ export interface CompareCell { pass_rate: number; avg_score: number; tests: CompareTestResult[]; + /** Score delta vs baseline (avg_score − baseline avg_score). Present when ?baseline= is set. */ + delta?: number; + /** Normalized gain `g` vs baseline. Null when baseline score is 1.0 (no headroom). */ + normalized_gain?: number | null; } /** diff --git a/apps/studio/src/routes/index.tsx b/apps/studio/src/routes/index.tsx index 136c1123a..7c2cf9786 100644 --- a/apps/studio/src/routes/index.tsx +++ b/apps/studio/src/routes/index.tsx @@ -1,6 +1,6 @@ /** * Home route: shows the multi-project dashboard when the server enables it, - * or the existing tabbed landing page (Runs, Experiments, Compare, Targets) + * or the existing tabbed landing page (Runs, Experiments, Analytics, Targets) * in single-project mode. * * Uses URL search param `?tab=` for tab persistence. @@ -10,7 +10,7 @@ import { Link, createFileRoute, useNavigate, useRouterState } from '@tanstack/re import { useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; -import { CompareTab } from '~/components/CompareTab'; +import { AnalyticsTab } from '~/components/AnalyticsTab'; import { ExperimentsTab } from '~/components/ExperimentsTab'; import { ProjectCard } from '~/components/ProjectCard'; import { RunEvalModal } from '~/components/RunEvalModal'; @@ -29,12 +29,12 @@ import { useStudioConfig, } from '~/lib/api'; -type TabId = 'runs' | 'experiments' | 'compare' | 'targets'; +type TabId = 'runs' | 'experiments' | 'analytics' | 'targets'; const tabs: { id: TabId; label: string }[] = [ { id: 'runs', label: '🏃 Recent Runs' }, { id: 'experiments', label: '🧪 Experiments' }, - { id: 'compare', label: '📊 Compare' }, + { id: 'analytics', label: '📊 Analytics' }, { id: 'targets', label: '🤖 Targets' }, ]; @@ -274,7 +274,7 @@ function SingleProjectHome() { /> )} {activeTab === 'experiments' && } - {activeTab === 'compare' && } + {activeTab === 'analytics' && } {activeTab === 'targets' && } {!isReadOnly && setShowRunEval(false)} />} @@ -282,10 +282,10 @@ function SingleProjectHome() { ); } -function CompareTabContent({ readOnly }: { readOnly: boolean }) { +function AnalyticsTabContent({ readOnly }: { readOnly: boolean }) { const { data, isLoading, isError, error } = useCompare(); return ( - } {activeTab === 'experiments' && } - {activeTab === 'compare' && ( - + {activeTab === 'analytics' && ( + )} {activeTab === 'targets' && } @@ -211,7 +211,7 @@ function ProjectExperimentsTab({ benchmarkId }: { benchmarkId: string }) { ); } -function ProjectCompareTab({ +function ProjectAnalyticsTab({ benchmarkId, readOnly, }: { @@ -220,7 +220,7 @@ function ProjectCompareTab({ }) { const { data, isLoading, isError, error } = useQuery(benchmarkCompareOptions(benchmarkId)); return ( - -## Compare +## Analytics -The **Compare** tab has two modes: **Aggregated** for the classic experiment × target matrix, and **Per run** for selecting individual runs and pitting them side-by-side. Toggle between them from the mode switch on the right of the masthead. +The **Analytics** tab has two modes: **Aggregated** for the classic experiment × target matrix, and **Per run** for selecting individual runs and pitting them side-by-side. Toggle between them from the mode switch on the right of the masthead. AgentV Studio side-by-side comparison of two runs tagged improved-prompt and baseline, with per-test pass rates @@ -80,16 +83,16 @@ The **Compare** tab has two modes: **Aggregated** for the classic experiment × The default view shows a cross-experiment, cross-target performance matrix. Numbers are colour-coded by pass rate — green (80%+), amber (50–80%), red (below 50%) — and each cell shows `passed/total` and the mean score. Click any cell to expand the per-test-case breakdown. -AgentV Studio aggregated compare matrix showing experiment × target pass rates +AgentV Studio Analytics tab showing aggregated experiment × target matrix with pass rates for baseline, optimized-prompt, and with-rag across claude-sonnet, gemini-pro, and gpt-4o -Run the same eval against multiple providers or experiment variants, then open the Compare tab: +Run the same eval against multiple providers or experiment variants, then open the Analytics tab: ```bash agentv eval my.EVAL.yaml --target azure --experiment baseline agentv eval my.EVAL.yaml --target azure --experiment with-caching agentv eval my.EVAL.yaml --target gemini --experiment baseline agentv eval my.EVAL.yaml --target gemini --experiment with-caching -agentv studio # Compare tab shows 2x2 matrix +agentv studio # Analytics tab shows 2x2 matrix ``` ### Per-run comparison @@ -118,6 +121,23 @@ Once runs are tagged, a chip row appears above the compare view listing every di The same filter is available to API consumers via `GET /api/compare?tags=baseline,v2-prompt`, which returns only the cells and runs whose tags intersect the query. +### Analytics charts + +Below the aggregated matrix, a collapsible **Analytics** section provides visual charts for deeper comparison. Select a **baseline target** from the dropdown to compute deltas and normalized gain metrics against that target. + +AgentV Studio analytics charts showing normalized gain bar chart with baseline selector and score distribution histogram + +The section includes the following visualizations: + +- **Normalized Gain (g)** — horizontal bar chart showing how much of the remaining headroom each experiment × target captured relative to the baseline. Bars are colour-coded green (positive gain), red (regression), or grey (null / no headroom). See [Normalized Gain](/docs/tools/compare#normalized-gain-g) for the formula. +- **Tag × Target Heatmap** — pass-rate grid across tags and targets, colour-coded by performance (emerald for high, amber for medium, red for low). +- **Negative Delta Table** — filtered list of experiment × target pairs that scored worse than the baseline, sorted by largest regression. +- **Score Distribution** — histogram showing the variance of scores across all test cases, binned by 10% intervals. +- **Score Trend Over Time** — line chart plotting mean score per target across runs over time, with a colour-coded legend for each target. + +AgentV Studio analytics showing score distribution histogram and score trend over time line chart with multi-target legend + +The baseline comparison is also available via the API: `GET /api/compare?baseline=` adds `delta` and `normalized_gain` fields to each non-baseline cell in the response. ## Benchmarks Dashboard diff --git a/bun.lock b/bun.lock index aa371c3fb..50d2b5bd1 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "apps/cli": { "name": "agentv", - "version": "4.15.0", + "version": "4.16.0", "bin": { "agentv": "./dist/cli.js", }, @@ -64,6 +64,7 @@ "@tanstack/react-router": "^1.120.3", "react": "^19.1.0", "react-dom": "^19.1.0", + "recharts": "^3.8.1", }, "devDependencies": { "@tailwindcss/vite": "^4.1.7", @@ -86,7 +87,7 @@ }, "packages/core": { "name": "@agentv/core", - "version": "4.15.0", + "version": "4.16.0", "dependencies": { "@agentclientprotocol/sdk": "^0.14.1", "@agentv/eval": "workspace:*", @@ -127,7 +128,7 @@ }, "packages/eval": { "name": "@agentv/eval", - "version": "4.15.0", + "version": "4.16.0", "dependencies": { "zod": "^3.23.8", }, @@ -512,6 +513,8 @@ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], @@ -580,6 +583,8 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], @@ -644,6 +649,24 @@ "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -678,6 +701,8 @@ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], @@ -830,8 +855,32 @@ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], @@ -896,6 +945,8 @@ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + "es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="], + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], @@ -1048,10 +1099,14 @@ "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -1382,12 +1437,18 @@ "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "react-is": ["react-is@19.2.5", "", {}, "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="], + + "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="], + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], + "recharts": ["recharts@3.8.1", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg=="], + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], @@ -1396,6 +1457,10 @@ "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], @@ -1430,6 +1495,8 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], @@ -1596,6 +1663,8 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], @@ -1670,6 +1739,8 @@ "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="], + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@rollup/pluginutils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],