claudeye 0.2.2 → 0.3.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.
Potentially problematic release.
This version of claudeye might be problematic. Click here for more details.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/routes-manifest.json +9 -0
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +5 -5
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +5 -5
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +6 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/icon.png/route.js +2 -1
- package/.next/standalone/.next/server/app/icon.png/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page.js +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +35 -5
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f408c708._.js +21 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fde83e67._.js +3 -0
- package/.next/standalone/.next/server/chunks/ce889_server_app_api_download_[project]_[session]_route_actions_bbdd823f.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_64175717.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__070e2009._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0a745465._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__bc37261c._.js → [root-of-the-server]__14f58da3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__164d9311._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2822fd21._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__31b4c2fd._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__45656df2._.js → [root-of-the-server]__4e339665._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__55018089._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__6313e929._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__7e21395a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__ee388ee0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0b4924bd._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_9e089768._.js → _1404b353._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_3d21dde5._.js +8 -0
- package/.next/standalone/.next/server/chunks/ssr/_fd9b1ff7._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_7769b563._.js → node_modules_next_dist_esm_eedfc1fd._.js} +2 -2
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +35 -5
- package/.next/standalone/.next/static/chunks/0e266948a26a3cdf.js +1 -0
- package/.next/standalone/.next/static/chunks/2774382cf796393c.js +4 -0
- package/.next/standalone/.next/static/chunks/6189ca16caad4352.js +3 -0
- package/.next/standalone/.next/static/chunks/8111dbe882e31821.js +1 -0
- package/.next/standalone/.next/static/chunks/{5a424275276f2bb9.js → bdeaeb8c9876394b.js} +1 -1
- package/.next/standalone/.next/static/chunks/cdbb6932218650fd.js +1 -0
- package/.next/standalone/.next/static/chunks/ea03555bb726c073.css +1 -0
- package/.next/standalone/.next/static/chunks/f091501564eb2ea3.js +4 -0
- package/.next/standalone/.next/static/chunks/{turbopack-2315171089e56fad.js → turbopack-fc1f23734a087d36.js} +1 -1
- package/.next/standalone/README.md +528 -41
- package/.next/standalone/app/actions/run-enrichments.ts +26 -5
- package/.next/standalone/app/actions/run-evals.ts +26 -5
- package/.next/standalone/app/actions/run-subagent-enrichments.ts +89 -0
- package/.next/standalone/app/actions/run-subagent-evals.ts +88 -0
- package/.next/standalone/app/api/download/[project]/[session]/route.ts +49 -0
- package/.next/standalone/app/components/copy-button.tsx +37 -0
- package/.next/standalone/app/components/enrichment-results-panel.tsx +33 -13
- package/.next/standalone/app/components/eval-results-panel.tsx +33 -13
- package/.next/standalone/app/components/log-viewer/entry-row.tsx +43 -14
- package/.next/standalone/app/components/log-viewer/queue-divider.tsx +50 -7
- package/.next/standalone/app/components/log-viewer/tool-input-output.tsx +13 -3
- package/.next/standalone/app/components/project-list.tsx +11 -11
- package/.next/standalone/app/components/raw-log-viewer.tsx +80 -11
- package/.next/standalone/app/components/refresh-button.tsx +79 -0
- package/.next/standalone/app/components/sessions-list.tsx +23 -14
- package/.next/standalone/app/project/[name]/session/[sessionId]/page.tsx +23 -12
- package/.next/standalone/bin/claudeye.mjs +112 -25
- package/.next/standalone/components/navbar.tsx +2 -0
- package/.next/standalone/dist/app.js +10 -4
- package/.next/standalone/dist/condition-registry.js +20 -0
- package/.next/standalone/dist/enrich-registry.js +26 -3
- package/.next/standalone/dist/enrich-runner.js +68 -13
- package/.next/standalone/dist/registry.js +26 -3
- package/.next/standalone/dist/runner.js +78 -20
- package/.next/standalone/dist/server-spawn.js +58 -34
- package/.next/standalone/lib/cache/hash.ts +67 -0
- package/.next/standalone/lib/cache/index.ts +9 -0
- package/.next/standalone/lib/cache/local-backend.ts +81 -0
- package/.next/standalone/lib/cache/manager.ts +127 -0
- package/.next/standalone/lib/cache/types.ts +19 -0
- package/.next/standalone/lib/evals/app.ts +30 -7
- package/.next/standalone/lib/evals/condition-registry.ts +26 -0
- package/.next/standalone/lib/evals/enrich-registry.ts +29 -3
- package/.next/standalone/lib/evals/enrich-runner.ts +68 -14
- package/.next/standalone/lib/evals/enrich-types.ts +6 -1
- package/.next/standalone/lib/evals/index.ts +3 -1
- package/.next/standalone/lib/evals/registry.ts +29 -4
- package/.next/standalone/lib/evals/runner.ts +77 -20
- package/.next/standalone/lib/evals/server-spawn.ts +67 -41
- package/.next/standalone/lib/evals/types.ts +16 -0
- package/.next/standalone/lib/log-format.ts +22 -1
- package/.next/standalone/package-lock.json +244 -308
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/dev.ts +3 -1
- package/.next/standalone/scripts/parse-script-args.ts +30 -2
- package/.next/standalone/scripts/start.ts +3 -1
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/README.md +528 -41
- package/bin/claudeye.mjs +112 -25
- package/dist/app.d.ts +17 -3
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +10 -4
- package/dist/app.js.map +1 -1
- package/dist/condition-registry.d.ts +9 -0
- package/dist/condition-registry.d.ts.map +1 -0
- package/dist/condition-registry.js +20 -0
- package/dist/condition-registry.js.map +1 -0
- package/dist/enrich-registry.d.ts +5 -1
- package/dist/enrich-registry.d.ts.map +1 -1
- package/dist/enrich-registry.js +26 -3
- package/dist/enrich-registry.js.map +1 -1
- package/dist/enrich-runner.d.ts +3 -3
- package/dist/enrich-runner.d.ts.map +1 -1
- package/dist/enrich-runner.js +68 -13
- package/dist/enrich-runner.js.map +1 -1
- package/dist/enrich-types.d.ts +6 -1
- package/dist/enrich-types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/registry.d.ts +5 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +26 -3
- package/dist/registry.js.map +1 -1
- package/dist/runner.d.ts +2 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +78 -20
- package/dist/runner.js.map +1 -1
- package/dist/server-spawn.d.ts +2 -1
- package/dist/server-spawn.d.ts.map +1 -1
- package/dist/server-spawn.js +58 -34
- package/dist/server-spawn.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__24a1e50a._.js +0 -21
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__32f115c9._.js +0 -6
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__476a1712._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__4ddcabf2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__ad593585._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__afd8e13b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__dd7ee810._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__ff3004de._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_53472598._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_863b6ca8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_f7347c74._.js +0 -5
- package/.next/standalone/.next/static/chunks/2243ff2814e7a781.js +0 -3
- package/.next/standalone/.next/static/chunks/50531467396cea91.css +0 -1
- package/.next/standalone/.next/static/chunks/8f288c01f8d7ef2d.js +0 -1
- package/.next/standalone/.next/static/chunks/abab1b00b2788443.js +0 -4
- package/.next/standalone/.next/static/chunks/d250d7f6f0a8c325.js +0 -1
- package/.next/standalone/.next/static/chunks/d7a572a8b7eb1ec8.js +0 -1
- package/.next/standalone/.next/static/chunks/fb1b0b9da3f03023.js +0 -4
- /package/.next/standalone/.next/static/{LoGIEEP4cORCqcFv-Ywg0 → 5JsV7rfAEOIwNOQPaX3UP}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LoGIEEP4cORCqcFv-Ywg0 → 5JsV7rfAEOIwNOQPaX3UP}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{LoGIEEP4cORCqcFv-Ywg0 → 5JsV7rfAEOIwNOQPaX3UP}/_ssgManifest.js +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
|
|
3
3
|
import { ensureEvalsLoaded } from "@/lib/evals/loader";
|
|
4
|
-
import {
|
|
4
|
+
import { getSessionScopedEnrichers } from "@/lib/evals/enrich-registry";
|
|
5
5
|
import { runAllEnrichers } from "@/lib/evals/enrich-runner";
|
|
6
6
|
import { getCachedSessionLog } from "@/lib/log-entries";
|
|
7
7
|
import { calculateLogStats } from "@/lib/log-stats";
|
|
8
|
+
import { getCachedResult, setCachedResult } from "@/lib/cache";
|
|
8
9
|
import type { EnrichRunSummary } from "@/lib/evals/enrich-types";
|
|
9
10
|
import type { EvalLogEntry } from "@/lib/evals/types";
|
|
10
11
|
|
|
11
12
|
export type EnrichActionResult =
|
|
12
|
-
| { ok: true; summary: EnrichRunSummary; hasEnrichers: true }
|
|
13
|
+
| { ok: true; summary: EnrichRunSummary; hasEnrichers: true; cached: boolean }
|
|
13
14
|
| { ok: true; hasEnrichers: false }
|
|
14
15
|
| { ok: false; error: string };
|
|
15
16
|
|
|
@@ -20,20 +21,40 @@ export type EnrichActionResult =
|
|
|
20
21
|
export async function runEnrichments(
|
|
21
22
|
projectName: string,
|
|
22
23
|
sessionId: string,
|
|
24
|
+
forceRefresh: boolean = false,
|
|
23
25
|
): Promise<EnrichActionResult> {
|
|
24
26
|
try {
|
|
25
27
|
await ensureEvalsLoaded();
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
const sessionEnrichers = getSessionScopedEnrichers();
|
|
30
|
+
if (sessionEnrichers.length === 0) {
|
|
28
31
|
return { ok: true, hasEnrichers: false };
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
const registeredNames = sessionEnrichers.map((e) => e.name);
|
|
35
|
+
|
|
36
|
+
// Check cache unless force refresh requested
|
|
37
|
+
if (!forceRefresh) {
|
|
38
|
+
const cached = await getCachedResult<EnrichRunSummary>(
|
|
39
|
+
"enrichments",
|
|
40
|
+
projectName,
|
|
41
|
+
sessionId,
|
|
42
|
+
registeredNames,
|
|
43
|
+
);
|
|
44
|
+
if (cached) {
|
|
45
|
+
return { ok: true, summary: cached.value, hasEnrichers: true, cached: true };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
const entries = await getCachedSessionLog(projectName, sessionId);
|
|
32
50
|
const stats = calculateLogStats(entries);
|
|
33
51
|
// Cast is safe: LogEntry is structurally compatible with EvalLogEntry
|
|
34
|
-
const summary = await runAllEnrichers(entries as unknown as EvalLogEntry[], stats, projectName, sessionId);
|
|
52
|
+
const summary = await runAllEnrichers(entries as unknown as EvalLogEntry[], stats, projectName, sessionId, sessionEnrichers, { scope: 'session' });
|
|
53
|
+
|
|
54
|
+
// Store in cache (fire-and-forget)
|
|
55
|
+
setCachedResult("enrichments", projectName, sessionId, summary, registeredNames);
|
|
35
56
|
|
|
36
|
-
return { ok: true, summary, hasEnrichers: true };
|
|
57
|
+
return { ok: true, summary, hasEnrichers: true, cached: false };
|
|
37
58
|
} catch (err) {
|
|
38
59
|
return {
|
|
39
60
|
ok: false,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
|
|
3
3
|
import { ensureEvalsLoaded } from "@/lib/evals/loader";
|
|
4
|
-
import {
|
|
4
|
+
import { getSessionScopedEvals } from "@/lib/evals/registry";
|
|
5
5
|
import { runAllEvals } from "@/lib/evals/runner";
|
|
6
6
|
import { getCachedSessionLog } from "@/lib/log-entries";
|
|
7
7
|
import { calculateLogStats } from "@/lib/log-stats";
|
|
8
|
+
import { getCachedResult, setCachedResult } from "@/lib/cache";
|
|
8
9
|
import type { EvalRunSummary, EvalLogEntry } from "@/lib/evals/types";
|
|
9
10
|
|
|
10
11
|
export type EvalActionResult =
|
|
11
|
-
| { ok: true; summary: EvalRunSummary; hasEvals: true }
|
|
12
|
+
| { ok: true; summary: EvalRunSummary; hasEvals: true; cached: boolean }
|
|
12
13
|
| { ok: true; hasEvals: false }
|
|
13
14
|
| { ok: false; error: string };
|
|
14
15
|
|
|
@@ -19,21 +20,41 @@ export type EvalActionResult =
|
|
|
19
20
|
export async function runEvals(
|
|
20
21
|
projectName: string,
|
|
21
22
|
sessionId: string,
|
|
23
|
+
forceRefresh: boolean = false,
|
|
22
24
|
): Promise<EvalActionResult> {
|
|
23
25
|
try {
|
|
24
26
|
await ensureEvalsLoaded();
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
const sessionEvals = getSessionScopedEvals();
|
|
29
|
+
if (sessionEvals.length === 0) {
|
|
27
30
|
return { ok: true, hasEvals: false };
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
const registeredNames = sessionEvals.map((e) => e.name);
|
|
34
|
+
|
|
35
|
+
// Check cache unless force refresh requested
|
|
36
|
+
if (!forceRefresh) {
|
|
37
|
+
const cached = await getCachedResult<EvalRunSummary>(
|
|
38
|
+
"evals",
|
|
39
|
+
projectName,
|
|
40
|
+
sessionId,
|
|
41
|
+
registeredNames,
|
|
42
|
+
);
|
|
43
|
+
if (cached) {
|
|
44
|
+
return { ok: true, summary: cached.value, hasEvals: true, cached: true };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
const entries = await getCachedSessionLog(projectName, sessionId);
|
|
31
49
|
const stats = calculateLogStats(entries);
|
|
32
50
|
// Cast is safe: LogEntry is structurally compatible with EvalLogEntry
|
|
33
51
|
// (the structural types in types.ts are intentionally loose for the dist build)
|
|
34
|
-
const summary = await runAllEvals(entries as unknown as EvalLogEntry[], stats, projectName, sessionId);
|
|
52
|
+
const summary = await runAllEvals(entries as unknown as EvalLogEntry[], stats, projectName, sessionId, sessionEvals, { scope: 'session' });
|
|
53
|
+
|
|
54
|
+
// Store in cache (fire-and-forget)
|
|
55
|
+
setCachedResult("evals", projectName, sessionId, summary, registeredNames);
|
|
35
56
|
|
|
36
|
-
return { ok: true, summary, hasEvals: true };
|
|
57
|
+
return { ok: true, summary, hasEvals: true, cached: false };
|
|
37
58
|
} catch (err) {
|
|
38
59
|
return {
|
|
39
60
|
ok: false,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { ensureEvalsLoaded } from "@/lib/evals/loader";
|
|
4
|
+
import { getSubagentScopedEnrichers } from "@/lib/evals/enrich-registry";
|
|
5
|
+
import { runAllEnrichers } from "@/lib/evals/enrich-runner";
|
|
6
|
+
import { loadSubagentLog } from "@/app/actions/load-subagent-log";
|
|
7
|
+
import { calculateLogStats } from "@/lib/log-stats";
|
|
8
|
+
import { getCachedResult, setCachedResult, hashSubagentFile } from "@/lib/cache";
|
|
9
|
+
import type { EvalLogEntry } from "@/lib/evals/types";
|
|
10
|
+
import type { EnrichRunSummary } from "@/lib/evals/enrich-types";
|
|
11
|
+
|
|
12
|
+
export type SubagentEnrichActionResult =
|
|
13
|
+
| { ok: true; summary: EnrichRunSummary; hasEnrichers: true; cached: boolean }
|
|
14
|
+
| { ok: true; hasEnrichers: false }
|
|
15
|
+
| { ok: false; error: string };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Server action that runs subagent-scoped enrichers against a subagent's log entries.
|
|
19
|
+
*/
|
|
20
|
+
export async function runSubagentEnrichments(
|
|
21
|
+
projectName: string,
|
|
22
|
+
sessionId: string,
|
|
23
|
+
agentId: string,
|
|
24
|
+
subagentType?: string,
|
|
25
|
+
subagentDescription?: string,
|
|
26
|
+
forceRefresh: boolean = false,
|
|
27
|
+
): Promise<SubagentEnrichActionResult> {
|
|
28
|
+
try {
|
|
29
|
+
await ensureEvalsLoaded();
|
|
30
|
+
|
|
31
|
+
const subagentEnrichers = getSubagentScopedEnrichers(subagentType);
|
|
32
|
+
if (subagentEnrichers.length === 0) {
|
|
33
|
+
return { ok: true, hasEnrichers: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const registeredNames = subagentEnrichers.map((e) => e.name);
|
|
37
|
+
const sessionKey = `${sessionId}/agent-${agentId}`;
|
|
38
|
+
const contentHash = await hashSubagentFile(projectName, sessionId, agentId);
|
|
39
|
+
|
|
40
|
+
// Check cache unless force refresh requested
|
|
41
|
+
if (!forceRefresh && contentHash) {
|
|
42
|
+
const cached = await getCachedResult<EnrichRunSummary>(
|
|
43
|
+
"enrichments",
|
|
44
|
+
projectName,
|
|
45
|
+
sessionKey,
|
|
46
|
+
registeredNames,
|
|
47
|
+
contentHash,
|
|
48
|
+
);
|
|
49
|
+
if (cached) {
|
|
50
|
+
return { ok: true, summary: cached.value, hasEnrichers: true, cached: true };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const logResult = await loadSubagentLog(projectName, sessionId, agentId);
|
|
55
|
+
if (!logResult.ok) {
|
|
56
|
+
return { ok: false, error: logResult.error };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const entries = logResult.entries;
|
|
60
|
+
const stats = calculateLogStats(entries);
|
|
61
|
+
|
|
62
|
+
const summary = await runAllEnrichers(
|
|
63
|
+
entries as unknown as EvalLogEntry[],
|
|
64
|
+
stats,
|
|
65
|
+
projectName,
|
|
66
|
+
sessionId,
|
|
67
|
+
subagentEnrichers,
|
|
68
|
+
{
|
|
69
|
+
scope: 'subagent',
|
|
70
|
+
subagentId: agentId,
|
|
71
|
+
subagentType,
|
|
72
|
+
subagentDescription,
|
|
73
|
+
parentSessionId: sessionId,
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Store in cache (fire-and-forget)
|
|
78
|
+
if (contentHash) {
|
|
79
|
+
setCachedResult("enrichments", projectName, sessionKey, summary, registeredNames, contentHash);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { ok: true, summary, hasEnrichers: true, cached: false };
|
|
83
|
+
} catch (err) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
error: err instanceof Error ? err.message : String(err),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { ensureEvalsLoaded } from "@/lib/evals/loader";
|
|
4
|
+
import { getSubagentScopedEvals } from "@/lib/evals/registry";
|
|
5
|
+
import { runAllEvals } from "@/lib/evals/runner";
|
|
6
|
+
import { loadSubagentLog } from "@/app/actions/load-subagent-log";
|
|
7
|
+
import { calculateLogStats } from "@/lib/log-stats";
|
|
8
|
+
import { getCachedResult, setCachedResult, hashSubagentFile } from "@/lib/cache";
|
|
9
|
+
import type { EvalRunSummary, EvalLogEntry } from "@/lib/evals/types";
|
|
10
|
+
|
|
11
|
+
export type SubagentEvalActionResult =
|
|
12
|
+
| { ok: true; summary: EvalRunSummary; hasEvals: true; cached: boolean }
|
|
13
|
+
| { ok: true; hasEvals: false }
|
|
14
|
+
| { ok: false; error: string };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Server action that runs subagent-scoped evals against a subagent's log entries.
|
|
18
|
+
*/
|
|
19
|
+
export async function runSubagentEvals(
|
|
20
|
+
projectName: string,
|
|
21
|
+
sessionId: string,
|
|
22
|
+
agentId: string,
|
|
23
|
+
subagentType?: string,
|
|
24
|
+
subagentDescription?: string,
|
|
25
|
+
forceRefresh: boolean = false,
|
|
26
|
+
): Promise<SubagentEvalActionResult> {
|
|
27
|
+
try {
|
|
28
|
+
await ensureEvalsLoaded();
|
|
29
|
+
|
|
30
|
+
const subagentEvals = getSubagentScopedEvals(subagentType);
|
|
31
|
+
if (subagentEvals.length === 0) {
|
|
32
|
+
return { ok: true, hasEvals: false };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const registeredNames = subagentEvals.map((e) => e.name);
|
|
36
|
+
const sessionKey = `${sessionId}/agent-${agentId}`;
|
|
37
|
+
const contentHash = await hashSubagentFile(projectName, sessionId, agentId);
|
|
38
|
+
|
|
39
|
+
// Check cache unless force refresh requested
|
|
40
|
+
if (!forceRefresh && contentHash) {
|
|
41
|
+
const cached = await getCachedResult<EvalRunSummary>(
|
|
42
|
+
"evals",
|
|
43
|
+
projectName,
|
|
44
|
+
sessionKey,
|
|
45
|
+
registeredNames,
|
|
46
|
+
contentHash,
|
|
47
|
+
);
|
|
48
|
+
if (cached) {
|
|
49
|
+
return { ok: true, summary: cached.value, hasEvals: true, cached: true };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const logResult = await loadSubagentLog(projectName, sessionId, agentId);
|
|
54
|
+
if (!logResult.ok) {
|
|
55
|
+
return { ok: false, error: logResult.error };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const entries = logResult.entries;
|
|
59
|
+
const stats = calculateLogStats(entries);
|
|
60
|
+
|
|
61
|
+
const summary = await runAllEvals(
|
|
62
|
+
entries as unknown as EvalLogEntry[],
|
|
63
|
+
stats,
|
|
64
|
+
projectName,
|
|
65
|
+
sessionId,
|
|
66
|
+
subagentEvals,
|
|
67
|
+
{
|
|
68
|
+
scope: 'subagent',
|
|
69
|
+
subagentId: agentId,
|
|
70
|
+
subagentType,
|
|
71
|
+
subagentDescription,
|
|
72
|
+
parentSessionId: sessionId,
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Store in cache (fire-and-forget)
|
|
77
|
+
if (contentHash) {
|
|
78
|
+
setCachedResult("evals", projectName, sessionKey, summary, registeredNames, contentHash);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { ok: true, summary, hasEvals: true, cached: false };
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
error: err instanceof Error ? err.message : String(err),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import { join, relative } from "path";
|
|
4
|
+
import { getClaudeProjectsPath } from "@/lib/paths";
|
|
5
|
+
|
|
6
|
+
const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
7
|
+
const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
|
|
8
|
+
|
|
9
|
+
function jsonError(message: string, status: number): NextResponse {
|
|
10
|
+
return NextResponse.json({ error: message }, { status });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function GET(
|
|
14
|
+
_request: NextRequest,
|
|
15
|
+
{ params }: { params: Promise<{ project: string; session: string }> }
|
|
16
|
+
): Promise<NextResponse> {
|
|
17
|
+
const { project, session } = await params;
|
|
18
|
+
|
|
19
|
+
if (!UUID_RE.test(session)) {
|
|
20
|
+
return jsonError("Invalid session ID", 400);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!project || PATH_TRAVERSAL_RE.test(project)) {
|
|
24
|
+
return jsonError("Invalid project name", 400);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const projectsPath = getClaudeProjectsPath();
|
|
28
|
+
const filePath = join(projectsPath, project, `${session}.jsonl`);
|
|
29
|
+
|
|
30
|
+
if (relative(projectsPath, filePath).startsWith("..")) {
|
|
31
|
+
return jsonError("Path traversal detected", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const content = await readFile(filePath, "utf-8");
|
|
36
|
+
return new NextResponse(content, {
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/x-ndjson",
|
|
39
|
+
"Content-Disposition": `attachment; filename="${session}.jsonl"`,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
} catch (e) {
|
|
43
|
+
const code = (e as NodeJS.ErrnoException).code;
|
|
44
|
+
if (code === "ENOENT") {
|
|
45
|
+
return jsonError("Session log not found", 404);
|
|
46
|
+
}
|
|
47
|
+
return jsonError("Failed to read session log", 500);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { Copy, Check } from "lucide-react";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
interface CopyButtonProps {
|
|
8
|
+
text: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function CopyButton({ text, className }: CopyButtonProps) {
|
|
13
|
+
const [copied, setCopied] = useState(false);
|
|
14
|
+
|
|
15
|
+
const handleCopy = useCallback(async () => {
|
|
16
|
+
await navigator.clipboard.writeText(text);
|
|
17
|
+
setCopied(true);
|
|
18
|
+
setTimeout(() => setCopied(false), 2000);
|
|
19
|
+
}, [text]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
onClick={handleCopy}
|
|
24
|
+
title="Copy to clipboard"
|
|
25
|
+
className={cn(
|
|
26
|
+
"inline-flex items-center justify-center rounded p-1 text-muted-foreground hover:text-foreground hover:bg-muted/80 transition-colors",
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
>
|
|
30
|
+
{copied ? (
|
|
31
|
+
<Check className="w-3.5 h-3.5 text-green-500" />
|
|
32
|
+
) : (
|
|
33
|
+
<Copy className="w-3.5 h-3.5" />
|
|
34
|
+
)}
|
|
35
|
+
</button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
RefreshCw,
|
|
10
10
|
} from "lucide-react";
|
|
11
11
|
import { runEnrichments } from "@/app/actions/run-enrichments";
|
|
12
|
+
import { runSubagentEnrichments } from "@/app/actions/run-subagent-enrichments";
|
|
12
13
|
import type { EnrichRunSummary, EnrichRunResult } from "@/lib/evals/enrich-types";
|
|
13
14
|
|
|
14
15
|
interface EnrichmentResultsPanelProps {
|
|
15
16
|
projectName: string;
|
|
16
17
|
sessionId: string;
|
|
18
|
+
agentId?: string;
|
|
19
|
+
subagentType?: string;
|
|
20
|
+
subagentDescription?: string;
|
|
21
|
+
compact?: boolean;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
function formatValue(value: string | number | boolean): string {
|
|
@@ -60,14 +65,15 @@ function EnricherGroup({ result }: { result: EnrichRunResult }) {
|
|
|
60
65
|
);
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
export default function EnrichmentResultsPanel({ projectName, sessionId }: EnrichmentResultsPanelProps) {
|
|
68
|
+
export default function EnrichmentResultsPanel({ projectName, sessionId, agentId, subagentType, subagentDescription, compact }: EnrichmentResultsPanelProps) {
|
|
64
69
|
const [summary, setSummary] = useState<EnrichRunSummary | null>(null);
|
|
65
70
|
const [loading, setLoading] = useState(false);
|
|
66
71
|
const [error, setError] = useState<string | null>(null);
|
|
67
72
|
const [noEnrichers, setNoEnrichers] = useState(false);
|
|
73
|
+
const [cached, setCached] = useState(false);
|
|
68
74
|
const abortRef = useRef<AbortController | null>(null);
|
|
69
75
|
|
|
70
|
-
const run = useCallback(async () => {
|
|
76
|
+
const run = useCallback(async (forceRefresh = false) => {
|
|
71
77
|
abortRef.current?.abort();
|
|
72
78
|
const controller = new AbortController();
|
|
73
79
|
abortRef.current = controller;
|
|
@@ -75,7 +81,9 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
75
81
|
setLoading(true);
|
|
76
82
|
setError(null);
|
|
77
83
|
try {
|
|
78
|
-
const result =
|
|
84
|
+
const result = agentId
|
|
85
|
+
? await runSubagentEnrichments(projectName, sessionId, agentId, subagentType, subagentDescription, forceRefresh)
|
|
86
|
+
: await runEnrichments(projectName, sessionId, forceRefresh);
|
|
79
87
|
if (controller.signal.aborted) return;
|
|
80
88
|
if (!result.ok) {
|
|
81
89
|
setError(result.error);
|
|
@@ -83,6 +91,7 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
83
91
|
setNoEnrichers(true);
|
|
84
92
|
} else {
|
|
85
93
|
setSummary(result.summary);
|
|
94
|
+
setCached(result.cached);
|
|
86
95
|
}
|
|
87
96
|
} catch (e) {
|
|
88
97
|
if (controller.signal.aborted) return;
|
|
@@ -92,11 +101,11 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
92
101
|
setLoading(false);
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
|
-
}, [projectName, sessionId]);
|
|
104
|
+
}, [projectName, sessionId, agentId, subagentType, subagentDescription]);
|
|
96
105
|
|
|
97
|
-
// Auto-run on mount; abort on unmount
|
|
106
|
+
// Auto-run on mount (uses cache); abort on unmount
|
|
98
107
|
useEffect(() => {
|
|
99
|
-
run();
|
|
108
|
+
run(false);
|
|
100
109
|
return () => {
|
|
101
110
|
abortRef.current?.abort();
|
|
102
111
|
};
|
|
@@ -105,11 +114,14 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
105
114
|
// No enrichers registered — render nothing
|
|
106
115
|
if (noEnrichers) return null;
|
|
107
116
|
|
|
117
|
+
const panelPadding = compact ? "p-2.5" : "p-4";
|
|
118
|
+
const fontSize = compact ? "text-xs" : "text-sm";
|
|
119
|
+
|
|
108
120
|
// Loading state before first result
|
|
109
121
|
if (loading && !summary) {
|
|
110
122
|
return (
|
|
111
|
-
<div className=
|
|
112
|
-
<div className=
|
|
123
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding}`}>
|
|
124
|
+
<div className={`flex items-center gap-2 ${fontSize} text-muted-foreground`}>
|
|
113
125
|
<RefreshCw className="w-4 h-4 animate-spin" />
|
|
114
126
|
Running enrichments...
|
|
115
127
|
</div>
|
|
@@ -120,8 +132,8 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
120
132
|
// Error state
|
|
121
133
|
if (error && !summary) {
|
|
122
134
|
return (
|
|
123
|
-
<div className=
|
|
124
|
-
<div className=
|
|
135
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding}`}>
|
|
136
|
+
<div className={`flex items-center gap-2 ${fontSize} text-destructive`}>
|
|
125
137
|
<AlertTriangle className="w-4 h-4" />
|
|
126
138
|
{error}
|
|
127
139
|
</div>
|
|
@@ -131,8 +143,11 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
131
143
|
|
|
132
144
|
if (!summary) return null;
|
|
133
145
|
|
|
146
|
+
const visibleResults = summary.results.filter((r) => !r.skipped);
|
|
147
|
+
if (visibleResults.length === 0) return null;
|
|
148
|
+
|
|
134
149
|
return (
|
|
135
|
-
<div className=
|
|
150
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding} space-y-3`}>
|
|
136
151
|
{/* Header */}
|
|
137
152
|
<div className="flex items-center justify-between">
|
|
138
153
|
<div className="flex items-center gap-2">
|
|
@@ -149,9 +164,14 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
149
164
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
150
165
|
<Clock className="w-3.5 h-3.5" />
|
|
151
166
|
<span>{summary.totalDurationMs}ms</span>
|
|
167
|
+
{cached && (
|
|
168
|
+
<span className="text-[10px] font-medium text-muted-foreground/70 bg-muted px-1.5 py-0.5 rounded">
|
|
169
|
+
cached
|
|
170
|
+
</span>
|
|
171
|
+
)}
|
|
152
172
|
</div>
|
|
153
173
|
<button
|
|
154
|
-
onClick={run}
|
|
174
|
+
onClick={() => run(true)}
|
|
155
175
|
disabled={loading}
|
|
156
176
|
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded border border-primary/30 bg-primary/10 text-primary hover:bg-primary/20 transition-colors disabled:opacity-50"
|
|
157
177
|
>
|
|
@@ -167,7 +187,7 @@ export default function EnrichmentResultsPanel({ projectName, sessionId }: Enric
|
|
|
167
187
|
|
|
168
188
|
{/* Enricher groups */}
|
|
169
189
|
<div className="space-y-3 divide-y divide-border/50">
|
|
170
|
-
{
|
|
190
|
+
{visibleResults.map((result) => (
|
|
171
191
|
<div key={result.name} className="pt-2 first:pt-0">
|
|
172
192
|
<EnricherGroup result={result} />
|
|
173
193
|
</div>
|
|
@@ -11,11 +11,16 @@ import {
|
|
|
11
11
|
RefreshCw,
|
|
12
12
|
} from "lucide-react";
|
|
13
13
|
import { runEvals } from "@/app/actions/run-evals";
|
|
14
|
+
import { runSubagentEvals } from "@/app/actions/run-subagent-evals";
|
|
14
15
|
import type { EvalRunSummary, EvalRunResult } from "@/lib/evals/types";
|
|
15
16
|
|
|
16
17
|
interface EvalResultsPanelProps {
|
|
17
18
|
projectName: string;
|
|
18
19
|
sessionId: string;
|
|
20
|
+
agentId?: string;
|
|
21
|
+
subagentType?: string;
|
|
22
|
+
subagentDescription?: string;
|
|
23
|
+
compact?: boolean;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
function ScoreBar({ score }: { score: number }) {
|
|
@@ -72,14 +77,15 @@ function EvalResultRow({ result }: { result: EvalRunResult }) {
|
|
|
72
77
|
);
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
export default function EvalResultsPanel({ projectName, sessionId }: EvalResultsPanelProps) {
|
|
80
|
+
export default function EvalResultsPanel({ projectName, sessionId, agentId, subagentType, subagentDescription, compact }: EvalResultsPanelProps) {
|
|
76
81
|
const [summary, setSummary] = useState<EvalRunSummary | null>(null);
|
|
77
82
|
const [loading, setLoading] = useState(false);
|
|
78
83
|
const [error, setError] = useState<string | null>(null);
|
|
79
84
|
const [noEvals, setNoEvals] = useState(false);
|
|
85
|
+
const [cached, setCached] = useState(false);
|
|
80
86
|
const abortRef = useRef<AbortController | null>(null);
|
|
81
87
|
|
|
82
|
-
const run = useCallback(async () => {
|
|
88
|
+
const run = useCallback(async (forceRefresh = false) => {
|
|
83
89
|
// Abort any in-flight request
|
|
84
90
|
abortRef.current?.abort();
|
|
85
91
|
const controller = new AbortController();
|
|
@@ -88,7 +94,9 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
88
94
|
setLoading(true);
|
|
89
95
|
setError(null);
|
|
90
96
|
try {
|
|
91
|
-
const result =
|
|
97
|
+
const result = agentId
|
|
98
|
+
? await runSubagentEvals(projectName, sessionId, agentId, subagentType, subagentDescription, forceRefresh)
|
|
99
|
+
: await runEvals(projectName, sessionId, forceRefresh);
|
|
92
100
|
if (controller.signal.aborted) return;
|
|
93
101
|
if (!result.ok) {
|
|
94
102
|
setError(result.error);
|
|
@@ -96,6 +104,7 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
96
104
|
setNoEvals(true);
|
|
97
105
|
} else {
|
|
98
106
|
setSummary(result.summary);
|
|
107
|
+
setCached(result.cached);
|
|
99
108
|
}
|
|
100
109
|
} catch (e) {
|
|
101
110
|
if (controller.signal.aborted) return;
|
|
@@ -105,11 +114,11 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
105
114
|
setLoading(false);
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
|
-
}, [projectName, sessionId]);
|
|
117
|
+
}, [projectName, sessionId, agentId, subagentType, subagentDescription]);
|
|
109
118
|
|
|
110
|
-
// Auto-run on mount; abort on unmount
|
|
119
|
+
// Auto-run on mount (uses cache); abort on unmount
|
|
111
120
|
useEffect(() => {
|
|
112
|
-
run();
|
|
121
|
+
run(false);
|
|
113
122
|
return () => {
|
|
114
123
|
abortRef.current?.abort();
|
|
115
124
|
};
|
|
@@ -118,11 +127,14 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
118
127
|
// No evals registered — render nothing
|
|
119
128
|
if (noEvals) return null;
|
|
120
129
|
|
|
130
|
+
const panelPadding = compact ? "p-2.5" : "p-4";
|
|
131
|
+
const fontSize = compact ? "text-xs" : "text-sm";
|
|
132
|
+
|
|
121
133
|
// Loading state before first result
|
|
122
134
|
if (loading && !summary) {
|
|
123
135
|
return (
|
|
124
|
-
<div className=
|
|
125
|
-
<div className=
|
|
136
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding}`}>
|
|
137
|
+
<div className={`flex items-center gap-2 ${fontSize} text-muted-foreground`}>
|
|
126
138
|
<RefreshCw className="w-4 h-4 animate-spin" />
|
|
127
139
|
Running evals...
|
|
128
140
|
</div>
|
|
@@ -133,8 +145,8 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
133
145
|
// Error state
|
|
134
146
|
if (error && !summary) {
|
|
135
147
|
return (
|
|
136
|
-
<div className=
|
|
137
|
-
<div className=
|
|
148
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding}`}>
|
|
149
|
+
<div className={`flex items-center gap-2 ${fontSize} text-destructive`}>
|
|
138
150
|
<AlertTriangle className="w-4 h-4" />
|
|
139
151
|
{error}
|
|
140
152
|
</div>
|
|
@@ -144,8 +156,11 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
144
156
|
|
|
145
157
|
if (!summary) return null;
|
|
146
158
|
|
|
159
|
+
const visibleResults = summary.results.filter((r) => !r.skipped);
|
|
160
|
+
if (visibleResults.length === 0) return null;
|
|
161
|
+
|
|
147
162
|
return (
|
|
148
|
-
<div className=
|
|
163
|
+
<div className={`bg-card border border-border rounded-lg ${panelPadding} space-y-3`}>
|
|
149
164
|
{/* Header */}
|
|
150
165
|
<div className="flex items-center justify-between">
|
|
151
166
|
<div className="flex items-center gap-2">
|
|
@@ -153,7 +168,7 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
153
168
|
<span className="text-sm font-medium">Eval Results</span>
|
|
154
169
|
</div>
|
|
155
170
|
<button
|
|
156
|
-
onClick={run}
|
|
171
|
+
onClick={() => run(true)}
|
|
157
172
|
disabled={loading}
|
|
158
173
|
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded border border-primary/30 bg-primary/10 text-primary hover:bg-primary/20 transition-colors disabled:opacity-50"
|
|
159
174
|
>
|
|
@@ -190,12 +205,17 @@ export default function EvalResultsPanel({ projectName, sessionId }: EvalResults
|
|
|
190
205
|
<div className="flex items-center gap-1.5 ml-auto">
|
|
191
206
|
<Clock className="w-3.5 h-3.5 text-muted-foreground" />
|
|
192
207
|
<span className="text-muted-foreground">{summary.totalDurationMs}ms</span>
|
|
208
|
+
{cached && (
|
|
209
|
+
<span className="text-[10px] font-medium text-muted-foreground/70 bg-muted px-1.5 py-0.5 rounded">
|
|
210
|
+
cached
|
|
211
|
+
</span>
|
|
212
|
+
)}
|
|
193
213
|
</div>
|
|
194
214
|
</div>
|
|
195
215
|
|
|
196
216
|
{/* Results list */}
|
|
197
217
|
<div className="divide-y divide-border/50">
|
|
198
|
-
{
|
|
218
|
+
{visibleResults.map((result) => (
|
|
199
219
|
<EvalResultRow key={result.name} result={result} />
|
|
200
220
|
))}
|
|
201
221
|
</div>
|