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
package/README.md
CHANGED
|
@@ -23,11 +23,16 @@ This starts a local server on port 8020 and opens the dashboard in your browser.
|
|
|
23
23
|
### Options
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
--projects-path <path> Path to Claude projects directory (default: ~/.claude/projects)
|
|
27
|
-
--port <number>
|
|
28
|
-
--
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
--projects-path, -p <path> Path to Claude projects directory (default: ~/.claude/projects)
|
|
27
|
+
--port <number> Preferred port (default: 8020)
|
|
28
|
+
--host <address> Host to bind to (default: localhost)
|
|
29
|
+
Use 0.0.0.0 for LAN access
|
|
30
|
+
--evals <path> Path to an evals/enrichments file (see Custom Evals below)
|
|
31
|
+
--cache <on|off> Enable/disable result caching (default: on)
|
|
32
|
+
--cache-path <path> Custom cache directory (default: ~/.claudeye/cache)
|
|
33
|
+
--cache-clear Clear all cached results and exit
|
|
34
|
+
--no-open Don't auto-open the browser
|
|
35
|
+
-h, --help Show help
|
|
31
36
|
```
|
|
32
37
|
|
|
33
38
|
### Examples
|
|
@@ -38,6 +43,21 @@ claudeye --projects-path /path/to/projects
|
|
|
38
43
|
|
|
39
44
|
# Run on a different port without opening the browser
|
|
40
45
|
claudeye --port 3000 --no-open
|
|
46
|
+
|
|
47
|
+
# Bind to all interfaces for LAN access
|
|
48
|
+
claudeye --host 0.0.0.0
|
|
49
|
+
|
|
50
|
+
# Load custom evals and enrichments
|
|
51
|
+
claudeye --evals ./my-evals.js
|
|
52
|
+
|
|
53
|
+
# Disable caching
|
|
54
|
+
claudeye --cache off
|
|
55
|
+
|
|
56
|
+
# Use a custom cache directory
|
|
57
|
+
claudeye --cache-path /tmp/claudeye-cache
|
|
58
|
+
|
|
59
|
+
# Clear cached results
|
|
60
|
+
claudeye --cache-clear
|
|
41
61
|
```
|
|
42
62
|
|
|
43
63
|
## Features
|
|
@@ -60,6 +80,7 @@ Open a session to get a full breakdown of the agent execution:
|
|
|
60
80
|
- **Subagent logs** — nested subagent executions load on demand
|
|
61
81
|
- **Thinking blocks** — collapsible reasoning traces
|
|
62
82
|
- **Virtual scrolling** — handles large logs without performance issues
|
|
83
|
+
- **Download** — export session logs as JSONL files
|
|
63
84
|
|
|
64
85
|
### Filtering
|
|
65
86
|
|
|
@@ -67,10 +88,73 @@ Open a session to get a full breakdown of the agent execution:
|
|
|
67
88
|
- **Keyword search**: add multiple keywords as chips, AND logic, case-insensitive
|
|
68
89
|
- Filters combine — both time and keyword conditions must match
|
|
69
90
|
|
|
91
|
+
### Auto-Refresh
|
|
92
|
+
|
|
93
|
+
The navbar includes a refresh button with an auto-refresh dropdown (Off, 5s, 10s, 30s). It works globally across all pages — projects, sessions, and log views all refresh when triggered.
|
|
94
|
+
|
|
70
95
|
### Theme Support
|
|
71
96
|
|
|
72
97
|
Light and dark mode with system preference detection. Toggle from the navbar. Preference is saved to `localStorage`.
|
|
73
98
|
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Caching
|
|
102
|
+
|
|
103
|
+
Claudeye caches eval and enrichment results to avoid recomputing them on every page load. Caching is **on by default** and requires zero configuration.
|
|
104
|
+
|
|
105
|
+
### How It Works
|
|
106
|
+
|
|
107
|
+
- Results are cached to `~/.claudeye/cache/` as JSON files, keyed by project and session
|
|
108
|
+
- Cache invalidation is **content-hash-based**, not TTL-based:
|
|
109
|
+
- The session file's modification time and size are hashed (fast, no full file read)
|
|
110
|
+
- The evals module file content is hashed
|
|
111
|
+
- The set of registered eval/enricher names is tracked
|
|
112
|
+
- A cache hit is only valid when **all three** match — if the session log grows, the evals file changes, or evals are added/removed, the cache is automatically invalidated
|
|
113
|
+
- Clicking **Re-run** in the dashboard always bypasses the cache and runs fresh computation
|
|
114
|
+
- Cached results show a subtle "cached" badge next to the duration in the results panel
|
|
115
|
+
|
|
116
|
+
### Running Without Cache
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Disable caching entirely
|
|
120
|
+
claudeye --cache off
|
|
121
|
+
|
|
122
|
+
# Or during development
|
|
123
|
+
npm run dev -- --cache off
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
When caching is disabled, no cache files are created or read. The startup log will show `Cache: disabled`.
|
|
127
|
+
|
|
128
|
+
### Custom Cache Location
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
claudeye --cache-path /path/to/custom/cache
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Clearing the Cache
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Clear the default cache directory and exit
|
|
138
|
+
claudeye --cache-clear
|
|
139
|
+
|
|
140
|
+
# Clear a custom cache directory
|
|
141
|
+
claudeye --cache-clear --cache-path /path/to/custom/cache
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Startup Log
|
|
145
|
+
|
|
146
|
+
When the server starts, the cache status is shown alongside other configuration:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Starting Claudeye dashboard...
|
|
150
|
+
Projects: /home/user/.claude/projects
|
|
151
|
+
Evals: /home/user/my-evals.js
|
|
152
|
+
Cache: local (/home/user/.claudeye/cache/)
|
|
153
|
+
URL: http://localhost:8020
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
74
158
|
## Custom Evals & Enrichments
|
|
75
159
|
|
|
76
160
|
Claudeye supports two types of custom extensions you can define in a single JS/TS file and load with the `--evals` flag:
|
|
@@ -165,107 +249,510 @@ node my-evals.js
|
|
|
165
249
|
|
|
166
250
|
This spawns the Claudeye dashboard as a child process. When the same file is loaded via `--evals`, `listen()` automatically becomes a no-op so you won't get a duplicate server.
|
|
167
251
|
|
|
168
|
-
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Conditional Evals & Enrichments
|
|
255
|
+
|
|
256
|
+
Conditions let you control when evals and enrichments run. There are two levels:
|
|
257
|
+
|
|
258
|
+
1. **Global condition** — gates _all_ evals and enrichments at once
|
|
259
|
+
2. **Per-eval / per-enrichment conditions** — gate individual functions
|
|
169
260
|
|
|
170
|
-
|
|
261
|
+
### Global Condition
|
|
171
262
|
|
|
172
|
-
|
|
263
|
+
Use `app.condition()` to set a global condition. If it returns `false` (or throws), every registered eval and enrichment is skipped.
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
import { createApp } from 'claudeye';
|
|
267
|
+
|
|
268
|
+
const app = createApp();
|
|
173
269
|
|
|
174
|
-
|
|
270
|
+
// Only run evals/enrichments for sessions with actual content
|
|
271
|
+
app.condition(({ entries }) => entries.length > 0);
|
|
175
272
|
|
|
176
|
-
|
|
273
|
+
// Only run for non-test projects
|
|
274
|
+
app.condition(({ projectName }) => !projectName.includes('test'));
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The global condition receives the same `EvalContext` as evals and enrichments, so you can inspect entries, stats, project name, and session ID.
|
|
278
|
+
|
|
279
|
+
> **Note:** Calling `app.condition()` multiple times replaces the previous condition — only the last one is active.
|
|
280
|
+
|
|
281
|
+
### Per-Eval Conditions
|
|
282
|
+
|
|
283
|
+
Pass a `condition` function in the options object (third argument) to `app.eval()`:
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
app.eval('budget-check',
|
|
287
|
+
({ stats }) => ({
|
|
288
|
+
pass: stats.toolCallCount <= 50,
|
|
289
|
+
score: 1 - Math.min(stats.toolCallCount / 100, 1),
|
|
290
|
+
message: `${stats.toolCallCount} tool call(s)`,
|
|
291
|
+
}),
|
|
292
|
+
{
|
|
293
|
+
condition: ({ stats }) => stats.toolCallCount > 0,
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
If the condition returns `false`, the eval is marked as **skipped** in the results panel. If the condition throws, the eval is marked as **errored** with the message `Condition error: <message>`.
|
|
299
|
+
|
|
300
|
+
### Per-Enrichment Conditions
|
|
301
|
+
|
|
302
|
+
Same pattern — pass a `condition` in the options to `app.enrich()`:
|
|
303
|
+
|
|
304
|
+
```js
|
|
305
|
+
app.enrich('error-analysis',
|
|
306
|
+
({ entries }) => {
|
|
307
|
+
const errors = entries.filter(e => e.raw?.is_error === true);
|
|
308
|
+
return {
|
|
309
|
+
'Total Errors': errors.length,
|
|
310
|
+
'Error Rate': `${((errors.length / entries.length) * 100).toFixed(1)}%`,
|
|
311
|
+
};
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
condition: ({ entries }) => entries.some(e => e.raw?.is_error === true),
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Evaluation Order
|
|
320
|
+
|
|
321
|
+
When a session is loaded, conditions are evaluated in this order:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
1. Global condition checked
|
|
325
|
+
├─ Returns false or throws → ALL evals/enrichments marked "skipped"
|
|
326
|
+
└─ Returns true → proceed to step 2
|
|
327
|
+
|
|
328
|
+
2. For each eval/enrichment:
|
|
329
|
+
├─ Has per-item condition?
|
|
330
|
+
│ ├─ Returns false → that item marked "skipped"
|
|
331
|
+
│ ├─ Throws → that item marked "errored" (not skipped)
|
|
332
|
+
│ └─ Returns true → run the function
|
|
333
|
+
└─ No condition → run the function
|
|
334
|
+
|
|
335
|
+
3. Function executes
|
|
336
|
+
├─ Returns result → recorded normally
|
|
337
|
+
└─ Throws → marked "errored", other items still run
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Condition Function Signature
|
|
341
|
+
|
|
342
|
+
Both global and per-item conditions use the same type:
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
type ConditionFunction = (context: EvalContext) => boolean | Promise<boolean>;
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Conditions can be async — the runner awaits them.
|
|
349
|
+
|
|
350
|
+
### Combining Global and Per-Item Conditions
|
|
351
|
+
|
|
352
|
+
Global and per-item conditions stack. The global condition runs first; if it passes, per-item conditions are checked individually:
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
const app = createApp();
|
|
356
|
+
|
|
357
|
+
// Global: skip everything for empty sessions
|
|
358
|
+
app.condition(({ entries }) => entries.length > 0);
|
|
359
|
+
|
|
360
|
+
// Per-eval: only check turn count for sessions with tool calls
|
|
361
|
+
app.eval('efficient-tools',
|
|
362
|
+
({ stats }) => ({
|
|
363
|
+
pass: stats.toolCallCount <= stats.turnCount * 2,
|
|
364
|
+
score: Math.max(0, 1 - (stats.toolCallCount / (stats.turnCount * 4))),
|
|
365
|
+
}),
|
|
366
|
+
{ condition: ({ stats }) => stats.toolCallCount > 0 }
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// Per-enrichment: only compute cost for sessions that used a model
|
|
370
|
+
app.enrich('cost',
|
|
371
|
+
({ entries }) => {
|
|
372
|
+
const tokens = entries.reduce((s, e) => s + (e.raw?.usage?.total_tokens || 0), 0);
|
|
373
|
+
return { 'Total Tokens': tokens, 'Est. Cost': `$${(tokens * 0.00001).toFixed(4)}` };
|
|
374
|
+
},
|
|
375
|
+
{ condition: ({ stats }) => stats.models.length > 0 }
|
|
376
|
+
);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### UI Behavior
|
|
380
|
+
|
|
381
|
+
In the dashboard, conditional results appear as follows:
|
|
382
|
+
|
|
383
|
+
| Status | Evals Panel | Enrichments Panel |
|
|
384
|
+
|--------|-------------|-------------------|
|
|
385
|
+
| **Skipped** | Grayed-out row with "skipped" label | Grayed-out row with "skipped" label |
|
|
386
|
+
| **Condition error** | Row with warning icon and error message | Row with warning icon and error message |
|
|
387
|
+
| **Passed / Data** | Green check with score bar | Key-value pairs grouped by enricher |
|
|
388
|
+
| **Failed** | Red X with score bar | N/A |
|
|
389
|
+
|
|
390
|
+
Skipped items are counted separately in the summary bar (e.g. "2 passed, 1 skipped").
|
|
391
|
+
|
|
392
|
+
### Full Example with Conditions
|
|
393
|
+
|
|
394
|
+
```js
|
|
395
|
+
import { createApp } from 'claudeye';
|
|
396
|
+
|
|
397
|
+
const app = createApp();
|
|
398
|
+
|
|
399
|
+
// ── Global condition: require at least one user message ──
|
|
400
|
+
app.condition(({ entries }) =>
|
|
401
|
+
entries.some(e => e.type === 'user')
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// ── Evals ──
|
|
405
|
+
|
|
406
|
+
// Always runs (when global condition passes)
|
|
407
|
+
app.eval('has-completion', ({ entries }) => {
|
|
408
|
+
const lastAssistant = [...entries].reverse().find(e => e.type === 'assistant');
|
|
409
|
+
const hasText = lastAssistant?.message?.content?.some?.(b => b.type === 'text');
|
|
410
|
+
return {
|
|
411
|
+
pass: !!hasText,
|
|
412
|
+
score: hasText ? 1.0 : 0,
|
|
413
|
+
message: hasText ? 'Session completed with text response' : 'No final text response',
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Only runs for sessions with tool calls
|
|
418
|
+
app.eval('tool-success-rate',
|
|
419
|
+
({ entries }) => {
|
|
420
|
+
const toolResults = entries.filter(e => e.type === 'tool');
|
|
421
|
+
const errors = toolResults.filter(e => e.raw?.is_error === true);
|
|
422
|
+
const rate = toolResults.length > 0 ? 1 - (errors.length / toolResults.length) : 1;
|
|
423
|
+
return {
|
|
424
|
+
pass: rate >= 0.9,
|
|
425
|
+
score: rate,
|
|
426
|
+
message: `${errors.length}/${toolResults.length} tool errors`,
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
{ condition: ({ stats }) => stats.toolCallCount > 0 }
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Only runs for longer sessions
|
|
433
|
+
app.eval('under-budget',
|
|
434
|
+
({ stats }) => ({
|
|
435
|
+
pass: stats.turnCount <= 30,
|
|
436
|
+
score: Math.max(0, 1 - stats.turnCount / 60),
|
|
437
|
+
message: `${stats.turnCount} turns`,
|
|
438
|
+
}),
|
|
439
|
+
{ condition: ({ stats }) => stats.turnCount >= 5 }
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
// ── Enrichments ──
|
|
443
|
+
|
|
444
|
+
// Always runs
|
|
445
|
+
app.enrich('overview', ({ stats }) => ({
|
|
446
|
+
'Turns': stats.turnCount,
|
|
447
|
+
'Tool Calls': stats.toolCallCount,
|
|
448
|
+
'Duration': stats.duration,
|
|
449
|
+
'Models': stats.models.join(', ') || 'none',
|
|
450
|
+
}));
|
|
451
|
+
|
|
452
|
+
// Only when subagents were spawned
|
|
453
|
+
app.enrich('subagent-info',
|
|
454
|
+
({ entries, stats }) => {
|
|
455
|
+
const subagentEntries = entries.filter(e => e.type === 'assistant' && e.parentUuid);
|
|
456
|
+
return {
|
|
457
|
+
'Subagent Count': stats.subagentCount,
|
|
458
|
+
'Subagent Messages': subagentEntries.length,
|
|
459
|
+
};
|
|
460
|
+
},
|
|
461
|
+
{ condition: ({ stats }) => stats.subagentCount > 0 }
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Async condition example
|
|
465
|
+
app.enrich('advanced-metrics',
|
|
466
|
+
({ entries }) => ({
|
|
467
|
+
'Entry Count': entries.length,
|
|
468
|
+
'Avg Entry Size': Math.round(
|
|
469
|
+
entries.reduce((s, e) => s + JSON.stringify(e).length, 0) / entries.length
|
|
470
|
+
),
|
|
471
|
+
}),
|
|
472
|
+
{
|
|
473
|
+
condition: async ({ entries }) => {
|
|
474
|
+
// Conditions can be async
|
|
475
|
+
return entries.length > 10;
|
|
476
|
+
},
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Subagent-Scoped Evals & Enrichments
|
|
484
|
+
|
|
485
|
+
By default, evals and enrichments run at the session level. You can also register evals and enrichments that run against individual subagent logs using the `scope` option.
|
|
486
|
+
|
|
487
|
+
### Scope Options
|
|
488
|
+
|
|
489
|
+
The `scope` option controls when an eval or enrichment runs:
|
|
490
|
+
|
|
491
|
+
| Scope | Runs at session level | Runs at subagent level |
|
|
492
|
+
|-------|----------------------|----------------------|
|
|
493
|
+
| `'session'` (default) | Yes | No |
|
|
494
|
+
| `'subagent'` | No | Yes |
|
|
495
|
+
| `'both'` | Yes | Yes |
|
|
496
|
+
|
|
497
|
+
### Basic Usage
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
import { createApp } from 'claudeye';
|
|
501
|
+
|
|
502
|
+
const app = createApp();
|
|
503
|
+
|
|
504
|
+
// Session-only (default, backward-compatible)
|
|
505
|
+
app.eval('no-errors', fn);
|
|
506
|
+
|
|
507
|
+
// Subagent-only, runs for all subagent types
|
|
508
|
+
app.eval('agent-quality', fn, { scope: 'subagent' });
|
|
509
|
+
|
|
510
|
+
// Subagent-only, only runs for Explore subagents
|
|
511
|
+
app.eval('explore-depth', fn, { scope: 'subagent', subagentType: 'Explore' });
|
|
512
|
+
|
|
513
|
+
// Runs at both session and subagent level
|
|
514
|
+
app.eval('general-check', fn, { scope: 'both' });
|
|
515
|
+
|
|
516
|
+
// Enrichments work the same way
|
|
517
|
+
app.enrich('agent-cost', fn, { scope: 'subagent' });
|
|
518
|
+
app.enrich('agent-tokens', fn, { scope: 'subagent', subagentType: 'Explore' });
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Subagent Context
|
|
522
|
+
|
|
523
|
+
When running at subagent level, the `EvalContext` includes additional metadata:
|
|
524
|
+
|
|
525
|
+
```js
|
|
526
|
+
app.eval('adaptive-eval', (ctx) => {
|
|
527
|
+
if (ctx.scope === 'subagent') {
|
|
528
|
+
// Running against a subagent's log
|
|
529
|
+
console.log(ctx.subagentId); // e.g. 'a1b2c3'
|
|
530
|
+
console.log(ctx.subagentType); // e.g. 'Explore'
|
|
531
|
+
console.log(ctx.subagentDescription); // e.g. 'Search for auth code'
|
|
532
|
+
console.log(ctx.parentSessionId); // parent session ID
|
|
533
|
+
}
|
|
534
|
+
return { pass: true };
|
|
535
|
+
}, { scope: 'both' });
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### SubagentType Filtering
|
|
539
|
+
|
|
540
|
+
When you specify `subagentType`, the eval/enrichment only runs for subagents of that type. Subagents of other types will not see the eval panel at all.
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
// Only runs for Explore subagents
|
|
544
|
+
app.eval('explore-thoroughness', ({ entries }) => ({
|
|
545
|
+
pass: entries.length > 5,
|
|
546
|
+
score: Math.min(entries.length / 20, 1),
|
|
547
|
+
}), { scope: 'subagent', subagentType: 'Explore' });
|
|
548
|
+
|
|
549
|
+
// Runs for all subagent types
|
|
550
|
+
app.eval('agent-efficiency', ({ stats }) => ({
|
|
551
|
+
pass: stats.turnCount <= 10,
|
|
552
|
+
score: Math.max(0, 1 - stats.turnCount / 20),
|
|
553
|
+
}), { scope: 'subagent' });
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### UI Behavior
|
|
557
|
+
|
|
558
|
+
When a subagent is expanded in the log viewer, eval and enrichment panels appear below the stats bar (only if matching subagent-scoped evals/enrichments are registered). Panels use a compact layout to fit within the nested subagent view. If no subagent-scoped evals match the subagent's type, the panels are not rendered.
|
|
559
|
+
|
|
560
|
+
### Caching
|
|
561
|
+
|
|
562
|
+
Subagent eval/enrichment results are cached separately from session results:
|
|
563
|
+
|
|
564
|
+
```
|
|
565
|
+
~/.claudeye/cache/evals/{project}/{sessionId}.json # session-level
|
|
566
|
+
~/.claudeye/cache/evals/{project}/{sessionId}/agent-{id}.json # subagent-level
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Cache invalidation works the same way — based on the subagent log file's mtime+size and the evals module content hash.
|
|
570
|
+
|
|
571
|
+
### Edge Cases
|
|
572
|
+
|
|
573
|
+
- **No subagents in session**: Subagent-scoped evals never run — panels only mount inside expanded subagent cards
|
|
574
|
+
- **`scope: 'both'` with `subagentType`**: At session level, the `subagentType` filter is ignored. At subagent level, it applies.
|
|
575
|
+
- **Conditions + scope**: Scope filtering happens first (registry level), then conditions run with the full `EvalContext` including scope metadata
|
|
576
|
+
- **Backward compatibility**: Existing evals with no `scope` option default to `'session'` — behavior is unchanged
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## API Reference
|
|
581
|
+
|
|
582
|
+
### `createApp()`
|
|
583
|
+
|
|
584
|
+
Returns a `ClaudeyeApp` instance. All methods are chainable.
|
|
585
|
+
|
|
586
|
+
```ts
|
|
587
|
+
import { createApp } from 'claudeye';
|
|
588
|
+
const app = createApp();
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### `app.condition(fn)`
|
|
592
|
+
|
|
593
|
+
Set a global condition that gates all evals and enrichments. Calling this multiple times replaces the previous condition.
|
|
594
|
+
|
|
595
|
+
```ts
|
|
596
|
+
app.condition(({ entries, stats, projectName, sessionId }) => boolean | Promise<boolean>);
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### `app.eval(name, fn, options?)`
|
|
600
|
+
|
|
601
|
+
Register an eval function.
|
|
177
602
|
|
|
178
603
|
- **`name`** — unique string identifier for the eval
|
|
179
604
|
- **`fn`** — function receiving an `EvalContext` and returning an `EvalResult`
|
|
605
|
+
- **`options.condition`** — optional condition function to gate this eval
|
|
606
|
+
- **`options.scope`** — `'session'` (default), `'subagent'`, or `'both'`
|
|
607
|
+
- **`options.subagentType`** — only run for subagents of this type (e.g. `'Explore'`)
|
|
180
608
|
|
|
181
609
|
```ts
|
|
182
610
|
app.eval('my-eval', (ctx) => ({ pass: true, score: 1.0 }));
|
|
611
|
+
|
|
612
|
+
app.eval('conditional-eval', evalFn, {
|
|
613
|
+
condition: (ctx) => ctx.stats.turnCount > 0,
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
app.eval('subagent-eval', evalFn, {
|
|
617
|
+
scope: 'subagent',
|
|
618
|
+
subagentType: 'Explore',
|
|
619
|
+
});
|
|
183
620
|
```
|
|
184
621
|
|
|
185
|
-
|
|
622
|
+
### `app.enrich(name, fn, options?)`
|
|
186
623
|
|
|
187
|
-
Register an enricher function.
|
|
624
|
+
Register an enricher function.
|
|
188
625
|
|
|
189
626
|
- **`name`** — unique string identifier for the enricher
|
|
190
627
|
- **`fn`** — function receiving an `EvalContext` and returning a `Record<string, string | number | boolean>`
|
|
628
|
+
- **`options.condition`** — optional condition function to gate this enricher
|
|
629
|
+
- **`options.scope`** — `'session'` (default), `'subagent'`, or `'both'`
|
|
630
|
+
- **`options.subagentType`** — only run for subagents of this type (e.g. `'Explore'`)
|
|
191
631
|
|
|
192
632
|
```ts
|
|
193
633
|
app.enrich('metrics', (ctx) => ({
|
|
194
634
|
'Turn Count': ctx.stats.turnCount,
|
|
195
635
|
'Models Used': ctx.stats.models.join(', '),
|
|
196
636
|
}));
|
|
637
|
+
|
|
638
|
+
app.enrich('conditional-enricher', enrichFn, {
|
|
639
|
+
condition: (ctx) => ctx.entries.length > 0,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
app.enrich('agent-cost', enrichFn, {
|
|
643
|
+
scope: 'subagent',
|
|
644
|
+
});
|
|
197
645
|
```
|
|
198
646
|
|
|
199
|
-
|
|
647
|
+
### `app.listen(port?, options?)`
|
|
200
648
|
|
|
201
649
|
Start the Claudeye dashboard server.
|
|
202
650
|
|
|
203
651
|
- **`port`** — port number (default: 8020)
|
|
652
|
+
- **`options.host`** — bind address (default: "localhost", use "0.0.0.0" for LAN)
|
|
204
653
|
- **`options.open`** — auto-open browser (default: true)
|
|
205
654
|
|
|
206
655
|
When the file is loaded via `--evals` or `CLAUDEYE_EVALS_MODULE`, `listen()` is a no-op.
|
|
207
656
|
|
|
208
|
-
|
|
657
|
+
### `EvalContext`
|
|
209
658
|
|
|
210
659
|
Both evals and enrichers receive the same context object:
|
|
211
660
|
|
|
212
661
|
```ts
|
|
213
662
|
interface EvalContext {
|
|
214
|
-
entries: EvalLogEntry[];
|
|
215
|
-
stats: EvalLogStats;
|
|
216
|
-
projectName: string;
|
|
217
|
-
sessionId: string;
|
|
663
|
+
entries: EvalLogEntry[]; // Parsed log entries for the session or subagent
|
|
664
|
+
stats: EvalLogStats; // Computed stats (turn count, tool calls, etc.)
|
|
665
|
+
projectName: string; // Encoded project folder name
|
|
666
|
+
sessionId: string; // Session UUID
|
|
667
|
+
scope: 'session' | 'subagent'; // Whether running at session or subagent level
|
|
668
|
+
subagentId?: string; // Hex ID of the subagent (subagent scope only)
|
|
669
|
+
subagentType?: string; // e.g. 'Explore', 'Bash' (subagent scope only)
|
|
670
|
+
subagentDescription?: string; // Short description (subagent scope only)
|
|
671
|
+
parentSessionId?: string; // Parent session ID (subagent scope only)
|
|
218
672
|
}
|
|
219
673
|
```
|
|
220
674
|
|
|
221
|
-
|
|
675
|
+
### `EvalLogEntry`
|
|
676
|
+
|
|
677
|
+
```ts
|
|
678
|
+
interface EvalLogEntry {
|
|
679
|
+
type: string; // 'user' | 'assistant' | 'tool' | 'queue-operation' | ...
|
|
680
|
+
uuid: string; // Unique entry ID
|
|
681
|
+
parentUuid: string | null; // Parent ID for nested entries (subagents)
|
|
682
|
+
timestamp: string; // ISO timestamp
|
|
683
|
+
timestampMs: number; // Epoch milliseconds
|
|
684
|
+
timestampFormatted: string; // Human-readable timestamp
|
|
685
|
+
message?: {
|
|
686
|
+
role: string; // 'user' | 'assistant'
|
|
687
|
+
content: string | EvalContentBlock[]; // Text or structured content blocks
|
|
688
|
+
model?: string; // Model ID (assistant messages only)
|
|
689
|
+
};
|
|
690
|
+
raw?: Record<string, unknown>; // Raw JSONL data (usage, errors, etc.)
|
|
691
|
+
label?: string; // Custom label
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### `EvalLogStats`
|
|
222
696
|
|
|
223
697
|
```ts
|
|
224
698
|
interface EvalLogStats {
|
|
225
|
-
turnCount: number;
|
|
226
|
-
userCount: number;
|
|
227
|
-
assistantCount: number;
|
|
228
|
-
toolCallCount: number;
|
|
229
|
-
subagentCount: number;
|
|
230
|
-
duration: string;
|
|
231
|
-
models: string[];
|
|
699
|
+
turnCount: number; // Number of conversation turns
|
|
700
|
+
userCount: number; // Number of user messages
|
|
701
|
+
assistantCount: number; // Number of assistant responses
|
|
702
|
+
toolCallCount: number; // Total tool invocations
|
|
703
|
+
subagentCount: number; // Number of subagent spawns
|
|
704
|
+
duration: string; // Formatted duration (e.g. "2m 15s")
|
|
705
|
+
models: string[]; // Distinct model IDs used
|
|
232
706
|
}
|
|
233
707
|
```
|
|
234
708
|
|
|
235
|
-
|
|
709
|
+
### `EvalResult`
|
|
236
710
|
|
|
237
711
|
```ts
|
|
238
712
|
interface EvalResult {
|
|
239
|
-
pass: boolean;
|
|
240
|
-
score?: number;
|
|
241
|
-
message?: string;
|
|
242
|
-
metadata?: Record<string, unknown>;
|
|
713
|
+
pass: boolean; // Did the eval pass?
|
|
714
|
+
score?: number; // 0-1, clamped automatically (default: 1.0)
|
|
715
|
+
message?: string; // Shown in the UI
|
|
716
|
+
metadata?: Record<string, unknown>; // Arbitrary data
|
|
243
717
|
}
|
|
244
718
|
```
|
|
245
719
|
|
|
246
|
-
|
|
720
|
+
### `EnrichmentResult`
|
|
247
721
|
|
|
248
722
|
```ts
|
|
249
723
|
// Enrichers return a flat key-value map
|
|
250
724
|
type EnrichmentResult = Record<string, string | number | boolean>;
|
|
251
725
|
```
|
|
252
726
|
|
|
253
|
-
###
|
|
727
|
+
### `ConditionFunction`
|
|
728
|
+
|
|
729
|
+
```ts
|
|
730
|
+
type ConditionFunction = (context: EvalContext) => boolean | Promise<boolean>;
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## How It Works
|
|
254
736
|
|
|
255
|
-
1. `createApp()` + `app.eval()` / `app.enrich()` register functions in global registries
|
|
256
|
-
2. When you run `claudeye --evals ./my-file.js`, the server dynamically imports your file, populating
|
|
737
|
+
1. `createApp()` + `app.eval()` / `app.enrich()` / `app.condition()` register functions in global registries
|
|
738
|
+
2. When you run `claudeye --evals ./my-file.js`, the server dynamically imports your file, populating the eval, enricher, and condition registries
|
|
257
739
|
3. When the dashboard loads a session, server actions run all registered evals and enrichers against the session's parsed log entries
|
|
258
|
-
4.
|
|
259
|
-
5.
|
|
740
|
+
4. The global condition is checked first — if it fails, everything is skipped
|
|
741
|
+
5. Per-item conditions are checked individually — skipped items don't block others
|
|
742
|
+
6. Each function is individually error-isolated — if one throws, the others still run
|
|
743
|
+
7. Results are serialized and displayed in separate panels in the dashboard UI
|
|
260
744
|
|
|
261
|
-
|
|
745
|
+
## Tips
|
|
262
746
|
|
|
263
747
|
- Each eval and enricher is wrapped in a try/catch. If one throws, the others still run and the error is shown in the UI.
|
|
264
|
-
- Eval scores are clamped to 0-1. If you don't provide a score,
|
|
748
|
+
- Eval scores are clamped to 0-1. If you don't provide a score, it defaults to 1.0.
|
|
265
749
|
- Both eval and enricher functions can be async.
|
|
750
|
+
- Condition functions can also be async.
|
|
266
751
|
- Re-registering with the same name replaces the previous function.
|
|
267
|
-
- Click "Re-run" in either panel to re-execute against the current session.
|
|
268
|
-
- You can mix `app.eval()` and `app.
|
|
752
|
+
- Click "Re-run" in either panel to re-execute against the current session (always bypasses cache).
|
|
753
|
+
- You can mix `app.eval()`, `app.enrich()`, and `app.condition()` calls freely in the same file.
|
|
754
|
+
- The global condition applies to both evals and enrichments — there's no way to set separate global conditions for each.
|
|
755
|
+
- Per-item condition errors are treated as eval/enrichment errors (not skips), so you'll see the error message in the UI.
|
|
269
756
|
|
|
270
757
|
## License
|
|
271
758
|
|