@opensearch-project/agent-health 0.5.2 → 0.7.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.
- package/README.md +1 -0
- package/cli/dist/index.js +2325 -961
- package/dist/assets/index-D-Np_l_T.js +246 -0
- package/dist/assets/index-vZt9QZKf.css +1 -0
- package/dist/index.html +2 -2
- package/docs/CLI.md +183 -3
- package/docs/CONFIGURATION.md +1 -1
- package/docs/CONNECTORS.md +1 -1
- package/docs/INSTRUMENT_WITH_OTEL.md +7 -0
- package/docs/SDK.md +178 -5
- package/docs/STORAGE_INDEX_FIELD_LIMITS.md +216 -0
- package/docs/skills/AGENT_HEALTH.md +55 -1
- package/docs/skills/add-connector/SKILL.md +5 -1
- package/examples/eval-files/demo.eval.js +1 -1
- package/examples/eval-files/ops-rca-classification.eval.js +71 -0
- package/examples/eval-files/ops-rca-evaluator.json +15 -0
- package/examples/eval-files/sdk-demo.eval.js +72 -0
- package/examples/eval-files/sdk-describe-demo.eval.js +50 -0
- package/examples/eval-files/sdk-hooks-demo.eval.js +1 -1
- package/lib/dist/lib/agentTrends.d.ts +210 -0
- package/lib/dist/lib/agentTrends.d.ts.map +1 -0
- package/lib/dist/lib/agentTrends.js +360 -0
- package/lib/dist/lib/agentTrends.js.map +1 -0
- package/lib/dist/lib/bedrockCompat.d.ts +27 -0
- package/lib/dist/lib/bedrockCompat.d.ts.map +1 -0
- package/lib/dist/lib/bedrockCompat.js +83 -0
- package/lib/dist/lib/bedrockCompat.js.map +1 -0
- package/lib/dist/lib/benchmarkCaseReview.d.ts +114 -0
- package/lib/dist/lib/benchmarkCaseReview.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkCaseReview.js +177 -0
- package/lib/dist/lib/benchmarkCaseReview.js.map +1 -0
- package/lib/dist/lib/benchmarkImage.d.ts +52 -0
- package/lib/dist/lib/benchmarkImage.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkImage.js +113 -0
- package/lib/dist/lib/benchmarkImage.js.map +1 -0
- package/lib/dist/lib/benchmarkRunsTable.d.ts +109 -0
- package/lib/dist/lib/benchmarkRunsTable.d.ts.map +1 -0
- package/lib/dist/lib/benchmarkRunsTable.js +212 -0
- package/lib/dist/lib/benchmarkRunsTable.js.map +1 -0
- package/lib/dist/lib/benchmarkVersionUtils.d.ts +13 -0
- package/lib/dist/lib/benchmarkVersionUtils.d.ts.map +1 -1
- package/lib/dist/lib/benchmarkVersionUtils.js +21 -0
- package/lib/dist/lib/benchmarkVersionUtils.js.map +1 -1
- package/lib/dist/lib/chunkedFetch.d.ts +18 -0
- package/lib/dist/lib/chunkedFetch.d.ts.map +1 -0
- package/lib/dist/lib/chunkedFetch.js +40 -0
- package/lib/dist/lib/chunkedFetch.js.map +1 -0
- package/lib/dist/lib/comparisonInsights.d.ts +151 -0
- package/lib/dist/lib/comparisonInsights.d.ts.map +1 -0
- package/lib/dist/lib/comparisonInsights.js +270 -0
- package/lib/dist/lib/comparisonInsights.js.map +1 -0
- package/lib/dist/lib/config/loader.d.ts.map +1 -1
- package/lib/dist/lib/config/loader.js +16 -1
- package/lib/dist/lib/config/loader.js.map +1 -1
- package/lib/dist/lib/config/types.d.ts +14 -0
- package/lib/dist/lib/config/types.d.ts.map +1 -1
- package/lib/dist/lib/constants.d.ts +11 -0
- package/lib/dist/lib/constants.d.ts.map +1 -1
- package/lib/dist/lib/constants.js +10 -1
- package/lib/dist/lib/constants.js.map +1 -1
- package/lib/dist/lib/contextFormat.d.ts +26 -0
- package/lib/dist/lib/contextFormat.d.ts.map +1 -0
- package/lib/dist/lib/contextFormat.js +28 -0
- package/lib/dist/lib/contextFormat.js.map +1 -0
- package/lib/dist/lib/dashboardMetrics.d.ts +11 -2
- package/lib/dist/lib/dashboardMetrics.d.ts.map +1 -1
- package/lib/dist/lib/dashboardMetrics.js +38 -3
- package/lib/dist/lib/dashboardMetrics.js.map +1 -1
- package/lib/dist/lib/envCompat.d.ts.map +1 -1
- package/lib/dist/lib/envCompat.js +14 -5
- package/lib/dist/lib/envCompat.js.map +1 -1
- package/lib/dist/lib/evaluationRerun.d.ts +102 -0
- package/lib/dist/lib/evaluationRerun.d.ts.map +1 -0
- package/lib/dist/lib/evaluationRerun.js +134 -0
- package/lib/dist/lib/evaluationRerun.js.map +1 -0
- package/lib/dist/lib/judgeFailureSummary.d.ts +66 -0
- package/lib/dist/lib/judgeFailureSummary.d.ts.map +1 -0
- package/lib/dist/lib/judgeFailureSummary.js +68 -0
- package/lib/dist/lib/judgeFailureSummary.js.map +1 -0
- package/lib/dist/lib/judgeStrategies.d.ts +108 -0
- package/lib/dist/lib/judgeStrategies.d.ts.map +1 -0
- package/lib/dist/lib/judgeStrategies.js +135 -0
- package/lib/dist/lib/judgeStrategies.js.map +1 -0
- package/lib/dist/lib/matchers/expect.d.ts +21 -1
- package/lib/dist/lib/matchers/expect.d.ts.map +1 -1
- package/lib/dist/lib/matchers/expect.js +51 -0
- package/lib/dist/lib/matchers/expect.js.map +1 -1
- package/lib/dist/lib/matchers/judgeAccessor.d.ts +4 -0
- package/lib/dist/lib/matchers/judgeAccessor.d.ts.map +1 -1
- package/lib/dist/lib/matchers/judgeAccessor.js +13 -2
- package/lib/dist/lib/matchers/judgeAccessor.js.map +1 -1
- package/lib/dist/lib/matchers/judgeReasoningParse.d.ts +49 -0
- package/lib/dist/lib/matchers/judgeReasoningParse.d.ts.map +1 -0
- package/lib/dist/lib/matchers/judgeReasoningParse.js +128 -0
- package/lib/dist/lib/matchers/judgeReasoningParse.js.map +1 -0
- package/lib/dist/lib/matchers/traces.d.ts +17 -2
- package/lib/dist/lib/matchers/traces.d.ts.map +1 -1
- package/lib/dist/lib/matchers/traces.js +136 -16
- package/lib/dist/lib/matchers/traces.js.map +1 -1
- package/lib/dist/lib/matchers/tracesPricing.d.ts +38 -0
- package/lib/dist/lib/matchers/tracesPricing.d.ts.map +1 -0
- package/lib/dist/lib/matchers/tracesPricing.js +64 -0
- package/lib/dist/lib/matchers/tracesPricing.js.map +1 -0
- package/lib/dist/lib/matchers/types.d.ts +25 -0
- package/lib/dist/lib/matchers/types.d.ts.map +1 -1
- package/lib/dist/lib/resolveCanonicalRun.d.ts +23 -0
- package/lib/dist/lib/resolveCanonicalRun.d.ts.map +1 -0
- package/lib/dist/lib/resolveCanonicalRun.js +26 -0
- package/lib/dist/lib/resolveCanonicalRun.js.map +1 -0
- package/lib/dist/lib/runActions.d.ts +120 -0
- package/lib/dist/lib/runActions.d.ts.map +1 -0
- package/lib/dist/lib/runActions.js +130 -0
- package/lib/dist/lib/runActions.js.map +1 -0
- package/lib/dist/lib/runInsights.d.ts +86 -0
- package/lib/dist/lib/runInsights.d.ts.map +1 -0
- package/lib/dist/lib/runInsights.js +185 -0
- package/lib/dist/lib/runInsights.js.map +1 -0
- package/lib/dist/lib/runName.d.ts +29 -0
- package/lib/dist/lib/runName.d.ts.map +1 -0
- package/lib/dist/lib/runName.js +38 -0
- package/lib/dist/lib/runName.js.map +1 -0
- package/lib/dist/lib/runReportPath.d.ts +16 -0
- package/lib/dist/lib/runReportPath.d.ts.map +1 -0
- package/lib/dist/lib/runReportPath.js +22 -0
- package/lib/dist/lib/runReportPath.js.map +1 -0
- package/lib/dist/lib/runSort.d.ts +27 -0
- package/lib/dist/lib/runSort.d.ts.map +1 -0
- package/lib/dist/lib/runSort.js +31 -0
- package/lib/dist/lib/runSort.js.map +1 -0
- package/lib/dist/lib/runStats.d.ts +109 -5
- package/lib/dist/lib/runStats.d.ts.map +1 -1
- package/lib/dist/lib/runStats.js +193 -10
- package/lib/dist/lib/runStats.js.map +1 -1
- package/lib/dist/lib/testCases/define.d.ts.map +1 -1
- package/lib/dist/lib/testCases/define.js +93 -46
- package/lib/dist/lib/testCases/define.js.map +1 -1
- package/lib/dist/lib/testCases/judge.d.ts.map +1 -1
- package/lib/dist/lib/testCases/judge.js +22 -4
- package/lib/dist/lib/testCases/judge.js.map +1 -1
- package/lib/dist/lib/testCases/loader.d.ts +35 -1
- package/lib/dist/lib/testCases/loader.d.ts.map +1 -1
- package/lib/dist/lib/testCases/loader.js +278 -31
- package/lib/dist/lib/testCases/loader.js.map +1 -1
- package/lib/dist/lib/trajectoryStepDisplay.d.ts +25 -0
- package/lib/dist/lib/trajectoryStepDisplay.d.ts.map +1 -0
- package/lib/dist/lib/trajectoryStepDisplay.js +42 -0
- package/lib/dist/lib/trajectoryStepDisplay.js.map +1 -0
- package/lib/dist/lib/utils.d.ts +34 -0
- package/lib/dist/lib/utils.d.ts.map +1 -1
- package/lib/dist/lib/utils.js +49 -0
- package/lib/dist/lib/utils.js.map +1 -1
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts +68 -22
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.d.ts.map +1 -1
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js +148 -111
- package/lib/dist/services/connectors/claude-code/ClaudeCodeConnector.js.map +1 -1
- package/lib/dist/services/connectors/kiro/KiroConnector.d.ts +21 -13
- package/lib/dist/services/connectors/kiro/KiroConnector.d.ts.map +1 -1
- package/lib/dist/services/connectors/kiro/KiroConnector.js +22 -25
- package/lib/dist/services/connectors/kiro/KiroConnector.js.map +1 -1
- package/lib/dist/services/connectors/pi/PiConnector.d.ts +49 -10
- package/lib/dist/services/connectors/pi/PiConnector.d.ts.map +1 -1
- package/lib/dist/services/connectors/pi/PiConnector.js +102 -86
- package/lib/dist/services/connectors/pi/PiConnector.js.map +1 -1
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.d.ts +59 -14
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.d.ts.map +1 -1
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.js +95 -61
- package/lib/dist/services/connectors/subprocess/SubprocessConnector.js.map +1 -1
- package/lib/dist/services/evaluation/bedrockJudge.d.ts +18 -0
- package/lib/dist/services/evaluation/bedrockJudge.d.ts.map +1 -1
- package/lib/dist/services/evaluation/bedrockJudge.js +4 -0
- package/lib/dist/services/evaluation/bedrockJudge.js.map +1 -1
- package/lib/dist/services/evaluation/index.d.ts +18 -1
- package/lib/dist/services/evaluation/index.d.ts.map +1 -1
- package/lib/dist/services/evaluation/index.js +156 -19
- package/lib/dist/services/evaluation/index.js.map +1 -1
- package/lib/dist/services/metrics.d.ts +55 -0
- package/lib/dist/services/metrics.d.ts.map +1 -0
- package/lib/dist/services/metrics.js +89 -0
- package/lib/dist/services/metrics.js.map +1 -0
- package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts +1 -1
- package/lib/dist/services/storage/asyncBenchmarkStorage.d.ts.map +1 -1
- package/lib/dist/services/storage/asyncBenchmarkStorage.js +30 -3
- package/lib/dist/services/storage/asyncBenchmarkStorage.js.map +1 -1
- package/lib/dist/services/storage/asyncRunStorage.d.ts +20 -1
- package/lib/dist/services/storage/asyncRunStorage.d.ts.map +1 -1
- package/lib/dist/services/storage/asyncRunStorage.js +125 -2
- package/lib/dist/services/storage/asyncRunStorage.js.map +1 -1
- package/lib/dist/services/storage/asyncTestCaseStorage.d.ts +18 -4
- package/lib/dist/services/storage/asyncTestCaseStorage.d.ts.map +1 -1
- package/lib/dist/services/storage/asyncTestCaseStorage.js +21 -4
- package/lib/dist/services/storage/asyncTestCaseStorage.js.map +1 -1
- package/lib/dist/services/storage/opensearchClient.d.ts +47 -12
- package/lib/dist/services/storage/opensearchClient.d.ts.map +1 -1
- package/lib/dist/services/storage/opensearchClient.js +12 -5
- package/lib/dist/services/storage/opensearchClient.js.map +1 -1
- package/lib/dist/services/traces/browserRecovery.d.ts +12 -1
- package/lib/dist/services/traces/browserRecovery.d.ts.map +1 -1
- package/lib/dist/services/traces/browserRecovery.js +35 -5
- package/lib/dist/services/traces/browserRecovery.js.map +1 -1
- package/lib/dist/services/traces/index.d.ts +8 -1
- package/lib/dist/services/traces/index.d.ts.map +1 -1
- package/lib/dist/services/traces/index.js +33 -12
- package/lib/dist/services/traces/index.js.map +1 -1
- package/lib/dist/services/traces/judgeAgentsHints.d.ts +98 -3
- package/lib/dist/services/traces/judgeAgentsHints.d.ts.map +1 -1
- package/lib/dist/services/traces/judgeAgentsHints.js +144 -3
- package/lib/dist/services/traces/judgeAgentsHints.js.map +1 -1
- package/lib/dist/services/traces/messageExtraction.d.ts.map +1 -1
- package/lib/dist/services/traces/messageExtraction.js +95 -33
- package/lib/dist/services/traces/messageExtraction.js.map +1 -1
- package/lib/dist/services/traces/spansToTrajectory.d.ts.map +1 -1
- package/lib/dist/services/traces/spansToTrajectory.js +51 -13
- package/lib/dist/services/traces/spansToTrajectory.js.map +1 -1
- package/lib/dist/services/traces/tracePoller.d.ts +27 -3
- package/lib/dist/services/traces/tracePoller.d.ts.map +1 -1
- package/lib/dist/services/traces/tracePoller.js +196 -33
- package/lib/dist/services/traces/tracePoller.js.map +1 -1
- package/lib/dist/services/traces/trajectoryMerge.d.ts +79 -0
- package/lib/dist/services/traces/trajectoryMerge.d.ts.map +1 -0
- package/lib/dist/services/traces/trajectoryMerge.js +109 -0
- package/lib/dist/services/traces/trajectoryMerge.js.map +1 -0
- package/lib/dist/types/index.d.ts +250 -3
- package/lib/dist/types/index.d.ts.map +1 -1
- package/lib/dist/types/index.js +24 -0
- package/lib/dist/types/index.js.map +1 -1
- package/package.json +8 -5
- package/server/dist/app.js +5490 -1557
- package/server/dist/index.js +5490 -1557
- package/dist/assets/index-CCQRDlO0.js +0 -243
- package/dist/assets/index-CNHQVbcj.css +0 -1
package/docs/CLI.md
CHANGED
|
@@ -61,11 +61,13 @@ agent-health list <resource> [-o table|json]
|
|
|
61
61
|
| `models` | | Available models |
|
|
62
62
|
| `test-cases` | `testcases`, `tc` | Stored test cases |
|
|
63
63
|
| `benchmarks` | `bench` | Stored benchmarks |
|
|
64
|
+
| `images` | `img` | Benchmark images (content-addressed evaluation-condition snapshots; runs sharing a digest are directly comparable) |
|
|
64
65
|
|
|
65
66
|
```bash
|
|
66
67
|
agent-health list agents
|
|
67
68
|
agent-health list tc -o json
|
|
68
69
|
agent-health list bench
|
|
70
|
+
agent-health list images
|
|
69
71
|
```
|
|
70
72
|
|
|
71
73
|
---
|
|
@@ -122,20 +124,61 @@ agent-health benchmark [options]
|
|
|
122
124
|
| `--stop-server` | Stop the server after benchmark completes | Keep running |
|
|
123
125
|
|
|
124
126
|
**Modes:**
|
|
125
|
-
- **Quick mode** (no `-n`, no `-f`):
|
|
127
|
+
- **Quick mode** (no `-n`, no `-f`): Runs all stored test cases as an **ad-hoc evaluation run** (no benchmark entity is created)
|
|
126
128
|
- **Named mode** (`-n <name>`): Runs a specific existing benchmark
|
|
127
|
-
- **File mode** (`-f <path>`): Imports test cases from a JSON file **or runs a code SDK file** (`.eval.js` / `.eval.ts` — see [SDK.md](./SDK.md)), creates a benchmark, and runs it
|
|
129
|
+
- **File mode** (`-f <path>`): Imports test cases from a JSON file **or runs a code SDK file** (`.eval.js` / `.eval.ts` — see [SDK.md](./SDK.md)), creates a benchmark, and runs it. `.eval.ts` is executed as synthetic CJS (like `.eval.js`) and works from anywhere on disk; only `.eval.mjs` resolves `@opensearch-project/agent-health` through normal Node module resolution, so an `.eval.mjs` file needs the package reachable as a real dependency from its location (see the note in [SDK.md](./SDK.md#migrating-v1--v2))
|
|
130
|
+
|
|
131
|
+
Every evaluation run is stamped with an **image digest** — a content hash of
|
|
132
|
+
its test-case contents + eval conditions (evaluator, judge model). Runs with
|
|
133
|
+
the same digest ran under identical conditions and are directly comparable;
|
|
134
|
+
re-running the same command converges on the same image instead of creating
|
|
135
|
+
new entities. See `agent-health list images`.
|
|
128
136
|
|
|
129
137
|
```bash
|
|
130
138
|
agent-health benchmark # quick mode
|
|
131
139
|
agent-health benchmark -n "Baseline" -a ml-commons # named mode
|
|
132
140
|
agent-health benchmark -f ./test-cases.json -a pulsar -v # file mode (JSON)
|
|
133
|
-
agent-health benchmark -f ./
|
|
141
|
+
agent-health benchmark -f ./examples/eval-files/demo.eval.js -a observio # file mode (code SDK)
|
|
134
142
|
agent-health benchmark -f ./test-cases.json -n "My Run" -a pulsar --export results.json
|
|
135
143
|
agent-health benchmark -n "Baseline" -e system-tool-usage -c 4 # custom evaluator, 4 in parallel
|
|
136
144
|
agent-health benchmark -n "Baseline" --export report.html --format html
|
|
137
145
|
```
|
|
138
146
|
|
|
147
|
+
#### benchmark doctor
|
|
148
|
+
|
|
149
|
+
Detect and clean up duplicated / debris benchmarks (dry-run by default).
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
agent-health benchmark doctor [--dry-run] [--apply] [--migrate-images] [--json]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Option | Description |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| `--dry-run` | Preview only — this is already the default; use --apply to execute |
|
|
158
|
+
| `--apply` | Execute the plan (default: dry-run report only) |
|
|
159
|
+
| `--migrate-images` | Convert remaining benchmarks into tagged benchmark images |
|
|
160
|
+
| `--json` | Output as JSON instead of the human-readable report |
|
|
161
|
+
|
|
162
|
+
What it detects:
|
|
163
|
+
- **Timestamped debris** — `quick-<ts>` / `*-<epoch-ms>` benchmarks with no
|
|
164
|
+
runs anywhere and older than 24h (deleted).
|
|
165
|
+
- **Content duplicates** — benchmarks with identical test-case sets. Embedded
|
|
166
|
+
runs are merged into the canonical (most runs › most references › oldest),
|
|
167
|
+
evaluation runs are re-pointed, duplicate shells deleted.
|
|
168
|
+
|
|
169
|
+
Runs and reports are **never** deleted. Sample data (`demo-*`) is never touched.
|
|
170
|
+
|
|
171
|
+
**Read-only mode**: When running without `--apply` or `--migrate-images` (pure dry-run),
|
|
172
|
+
the command safely reuses existing foreign servers in read-only mode. This allows
|
|
173
|
+
the diagnostic to run even when another agent-health instance is operating on a
|
|
174
|
+
different worktree/port, with a clear notice that no writes will be issued.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
agent-health benchmark doctor # report what would change (read-only)
|
|
178
|
+
agent-health benchmark doctor --apply # clean up (strict server guard)
|
|
179
|
+
agent-health benchmark doctor --apply --migrate-images
|
|
180
|
+
```
|
|
181
|
+
|
|
139
182
|
---
|
|
140
183
|
|
|
141
184
|
### export
|
|
@@ -429,3 +472,140 @@ Most commands support `-o, --output`:
|
|
|
429
472
|
|--------|----------|
|
|
430
473
|
| `table` | Human-readable (default) |
|
|
431
474
|
| `json` | Machine-readable, scripting |
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Running from Outside the Repo
|
|
479
|
+
|
|
480
|
+
When running `npx @opensearch-project/agent-health` from a directory *outside* the agent-health repository (e.g., from a customer or partner project), you may encounter configuration and environment issues. This section covers the most common friction points and their solutions.
|
|
481
|
+
|
|
482
|
+
### Issue 1: Port Already In Use
|
|
483
|
+
|
|
484
|
+
**Error message:**
|
|
485
|
+
```
|
|
486
|
+
Port 4001 is in use, trying 4002...
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Root cause:** The default ports (4001 for server, 4000 for frontend) are busy, and all fallback ports (4001–4010) are occupied.
|
|
490
|
+
|
|
491
|
+
**Solution:** Set `AH_PORT` to use a different port:
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
AH_PORT=8001 npx @opensearch-project/agent-health
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
The server will listen on port 8001. If your frontend dev port (4000) is also in use, set `AH_DEV_PORT` as well:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
AH_PORT=8001 AH_DEV_PORT=8000 npx @opensearch-project/agent-health
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Issue 2: Config File Loading Fails (`package.json` Type)
|
|
504
|
+
|
|
505
|
+
**Error message:**
|
|
506
|
+
```
|
|
507
|
+
Failed to load config file agent-health.config.ts: ERR_MODULE_NOT_FOUND
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**Root cause:** Your cwd's `package.json` doesn't declare `"type": "module"`, and Node.js cannot load the TypeScript config as an ES module.
|
|
511
|
+
|
|
512
|
+
**Solution:** Add `"type": "module"` to your local `package.json`:
|
|
513
|
+
|
|
514
|
+
```json
|
|
515
|
+
{
|
|
516
|
+
"type": "module",
|
|
517
|
+
"name": "my-project",
|
|
518
|
+
"version": "1.0.0"
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Alternatively, create a minimal `package.json` in the directory where you're running the command:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
echo '{"type":"module"}' > package.json
|
|
526
|
+
npx @opensearch-project/agent-health
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Issue 3: TypeScript Config Parsing Fails (`TSX_TSCONFIG_PATH`)
|
|
530
|
+
|
|
531
|
+
**Error message:**
|
|
532
|
+
```
|
|
533
|
+
Failed to load config file agent-health.config.ts: [TypeScript parsing error]
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Root cause:** The TypeScript loader cannot find your `tsconfig.json` (e.g., if you placed your config in a subdirectory).
|
|
537
|
+
|
|
538
|
+
**Solution:** Set `TSX_TSCONFIG_PATH` to point to the agent-health repo's `tsconfig.json`:
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
TSX_TSCONFIG_PATH=/path/to/agent-health/tsconfig.json npx @opensearch-project/agent-health
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
Or, if you have your own `tsconfig.json` that extends agent-health's:
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
TSX_TSCONFIG_PATH=./tsconfig.json npx @opensearch-project/agent-health
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Issue 4: Claude Code Binary Not Found
|
|
551
|
+
|
|
552
|
+
**Error message:**
|
|
553
|
+
```
|
|
554
|
+
Subprocess timed out or failed: ENOENT: no such file or directory, spawn 'claude'
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Root cause:** The `claude` CLI is not installed or not in your `PATH`.
|
|
558
|
+
|
|
559
|
+
**Solution:** Ensure Claude Code CLI is installed and accessible:
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
which claude # Verify CLI is in PATH
|
|
563
|
+
claude --version # Check installation
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
If the CLI is installed but not in `PATH`, set `CLAUDE_CODE_BIN` to the full path:
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
CLAUDE_CODE_BIN=/usr/local/bin/claude npx @opensearch-project/agent-health
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
Or install Claude Code globally:
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
npm install -g @anthropic-ai/claude-code
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Complete External Invocation Example
|
|
579
|
+
|
|
580
|
+
Here's a full example running from outside the repo with all common overrides:
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
# From ~/my-project/ directory (outside agent-health repo):
|
|
584
|
+
export AH_PORT=8001
|
|
585
|
+
export AH_DEV_PORT=8000
|
|
586
|
+
export TSX_TSCONFIG_PATH=/opt/agent-health/tsconfig.json
|
|
587
|
+
export CLAUDE_CODE_BIN=/usr/local/bin/claude
|
|
588
|
+
export AWS_PROFILE=my-profile
|
|
589
|
+
|
|
590
|
+
# Create minimal package.json if needed
|
|
591
|
+
echo '{"type":"module"}' > package.json
|
|
592
|
+
|
|
593
|
+
# Run the CLI
|
|
594
|
+
npx @opensearch-project/agent-health --no-browser
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Then navigate to http://localhost:8001 in your browser.
|
|
598
|
+
|
|
599
|
+
### Checklist
|
|
600
|
+
|
|
601
|
+
- [ ] `AH_PORT` set if default ports are busy
|
|
602
|
+
- [ ] `package.json` has `"type": "module"`
|
|
603
|
+
- [ ] `TSX_TSCONFIG_PATH` set if config parsing fails
|
|
604
|
+
- [ ] Claude Code CLI installed (if using claude-code connector)
|
|
605
|
+
- [ ] AWS credentials configured (if using Bedrock judge)
|
|
606
|
+
|
|
607
|
+
If issues persist, run with `DEBUG=true` for verbose output:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
DEBUG=true npx @opensearch-project/agent-health
|
|
611
|
+
```
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -113,7 +113,7 @@ Rules of thumb:
|
|
|
113
113
|
`OPENSEARCH_STORAGE_*` or `storage` in your config to use a cluster instead).
|
|
114
114
|
- **`evals/`** is **your test source** — `.eval.js` / `.eval.ts` files you write
|
|
115
115
|
with the [code SDK](./SDK.md) and run with
|
|
116
|
-
`agent-health benchmark -f ./
|
|
116
|
+
`agent-health benchmark -f ./examples/eval-files/demo.eval.js`. They are **not** stored under
|
|
117
117
|
`.agent-health/data/`; *running* them produces run records that land there (or
|
|
118
118
|
in OpenSearch).
|
|
119
119
|
|
package/docs/CONNECTORS.md
CHANGED
|
@@ -529,7 +529,7 @@ See `services/connectors/claude-code/ClaudeCodeConnector.ts` for a complete exam
|
|
|
529
529
|
### Kiro Connector
|
|
530
530
|
|
|
531
531
|
See `services/connectors/kiro/KiroConnector.ts` for a `SubprocessConnector`
|
|
532
|
-
subclass that overrides `parseStderrChunk()` to convert Kiro's stderr-borne
|
|
532
|
+
subclass that overrides `parseStderrChunk(chunk, trajectory, onProgress, state)` to convert Kiro's stderr-borne
|
|
533
533
|
`[tool] Running:` / `[tool] status:` markers into structured `action` +
|
|
534
534
|
`tool_result` steps. The base `SubprocessConnector` also persists `stderr` to
|
|
535
535
|
`rawOutput` and honors per-request `connectorConfig` overrides (`args` /
|
|
@@ -385,6 +385,13 @@ If you run an MCP server, expose this document as a resource at `agent-health://
|
|
|
385
385
|
|
|
386
386
|
## Reference
|
|
387
387
|
|
|
388
|
+
### New to OpenTelemetry?
|
|
389
|
+
|
|
390
|
+
- [What is OpenTelemetry?](https://opentelemetry.io/docs/what-is-opentelemetry/) — start here for the concepts (traces, spans, exporters, collectors).
|
|
391
|
+
- [OpenTelemetry GenAI semantic conventions (repo)](https://github.com/open-telemetry/semantic-conventions-genai) — the source-of-truth for the `gen_ai.*` attributes this guide uses.
|
|
392
|
+
|
|
393
|
+
### Specs used by this guide
|
|
394
|
+
|
|
388
395
|
- [OTel GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
|
|
389
396
|
- [OTel GenAI Agent Spans](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/)
|
|
390
397
|
- [Agent Health Telemetry Setup](./CLAUDE_CODE_TELEMETRY.md)
|
package/docs/SDK.md
CHANGED
|
@@ -64,7 +64,7 @@ async function ({ agent, judge, evaluate, expect, testInfo, provisioned }) { ...
|
|
|
64
64
|
| `agent` | `AgentFixture` | `await agent.run(prompt?, options?)` — invoke the agent **once** (control inversion). Returns an `EvalResult`. |
|
|
65
65
|
| `judge` | `JudgeFn` | Non-throwing LLM judge. `judge(...)` gates; `judge.observe(...)` is observational. Returns a `Verdict`. |
|
|
66
66
|
| `evaluate` | `EvaluateFn` | Run a custom programmatic evaluator registered with `defineEvaluator()`. |
|
|
67
|
-
| `expect` | chai's `expect` with our recording plugin | Synchronous matcher entry-point |
|
|
67
|
+
| `expect` | chai's `expect` with our recording plugin | Synchronous matcher entry-point. `expect.soft(...)` records a failing assertion instead of throwing — see [Always-record guarantee](#5-matchers-record-structured-verdicts). |
|
|
68
68
|
| `testInfo` | `TestInfo` (read-only) | `{ name, benchmarkPath, sourceFile, testCaseId }` |
|
|
69
69
|
| `provisioned` | `Readonly<Record<string, unknown>>` | Values set by `beforeEach` via `provide()` |
|
|
70
70
|
| `result` | `EvalResult` *(legacy eager path)* | Pre-populated agent result — present when the runner invokes before the body. Prefer `await agent.run()`. |
|
|
@@ -166,7 +166,7 @@ Key points:
|
|
|
166
166
|
Hooks are a no-op when no test in the run uses them — the orchestrator
|
|
167
167
|
is short-circuited to a noop variant and existing tests pay zero cost.
|
|
168
168
|
|
|
169
|
-
See the demo at [`
|
|
169
|
+
See the demo at [`examples/eval-files/sdk-hooks-demo.eval.js`](../examples/eval-files/sdk-hooks-demo.eval.js).
|
|
170
170
|
|
|
171
171
|
### 5. Matchers record structured verdicts
|
|
172
172
|
|
|
@@ -187,6 +187,79 @@ Matchers (4/5 passed)
|
|
|
187
187
|
This is the major upgrade over throw-and-fail: every assertion gets its own
|
|
188
188
|
row, status, and detail block.
|
|
189
189
|
|
|
190
|
+
#### Always-record guarantee
|
|
191
|
+
|
|
192
|
+
chai's `expect()` is fail-fast — the first failing assertion throws, and the
|
|
193
|
+
rest of the test body (any later `expect()`/`judge()`/`evaluate()` calls)
|
|
194
|
+
never executes. For a pass/fail verdict that's the intended Playwright-style
|
|
195
|
+
contract, but a report consumed by an optimizer (or by you, comparing runs)
|
|
196
|
+
needs the objective actuals — duration, token usage, USD cost — from every
|
|
197
|
+
axis, even when one gate fails partway through. A token-budget gate failing
|
|
198
|
+
shouldn't erase the cost figure that would otherwise have been on the report.
|
|
199
|
+
|
|
200
|
+
So regardless of where (or whether) the body throws, the runner **always**
|
|
201
|
+
stamps `performanceMetrics.durationMs` / `.agentDurationMs` /
|
|
202
|
+
`.totalTokens` / `.totalCostUsd` onto the report the moment `agent.run()`
|
|
203
|
+
resolves — reading them straight from the same OTel-derived data
|
|
204
|
+
`result.traces`/the `traces` fixture exposes, independent of whether the
|
|
205
|
+
body's own code ever reached a matcher that asserted on them. (`totalTokens`/
|
|
206
|
+
`totalCostUsd` are `undefined`, not `0`, when `useTraces: true` but spans
|
|
207
|
+
never arrived — see the loud-failure accessor in "Traces fixture" below;
|
|
208
|
+
they're real `0`s when `useTraces: false`.)
|
|
209
|
+
|
|
210
|
+
This guarantee has one hard limit: a `judge()`/`evaluate()` call itself is
|
|
211
|
+
already non-throwing and records its score/verdict the instant it's called —
|
|
212
|
+
so a REACHED judge call is always on the report today, throw-or-not. But a
|
|
213
|
+
judge call placed textually *after* a failing `expect()` never executes at
|
|
214
|
+
all — that's a pure source-order problem the runner cannot retroactively fix
|
|
215
|
+
(it can't record a call that never ran). Use `expect.soft(...)` (below) when
|
|
216
|
+
you need later judge/evaluate calls to run even after an earlier assertion
|
|
217
|
+
fails.
|
|
218
|
+
|
|
219
|
+
The matcher panel also gets a distinct **"not reached"** row appended
|
|
220
|
+
whenever the body threw — so "this axis never ran" (grey, not-reached) reads
|
|
221
|
+
differently from "this axis ran and failed" (red). It's excluded from the
|
|
222
|
+
passed/failed counts and gets its own tally: `(3/4 passed, 1 not reached)`.
|
|
223
|
+
|
|
224
|
+
#### `expect.soft(...)` — keep going instead of bailing on the first failure
|
|
225
|
+
|
|
226
|
+
`expect.soft(value)` is the same chai assertion surface as `expect(value)`
|
|
227
|
+
— every built-in BDD matcher plus the custom ones below — except a failing
|
|
228
|
+
assertion **records** the MatcherResult and returns instead of throwing:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
test('budget-aware RCA', { prompt: '...' }, async ({ agent, expect, judge }) => {
|
|
232
|
+
const result = await agent.run();
|
|
233
|
+
expect.soft(result.traces.totalTokens).to.be.lessThan(10_000); // fails — recorded, body continues
|
|
234
|
+
expect.soft(result.traces.totalCost).to.be.lessThan(0.05); // still runs
|
|
235
|
+
await judge(result, 'identifies the root cause'); // still runs — the whole point
|
|
236
|
+
expect(result.agentOutput).to.contain('root cause'); // a HARD expect still bails on ITS OWN failure
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The runner's overall verdict is unaffected by soft vs. hard: a test already
|
|
241
|
+
fails when ANY recorded gate matcher has `pass: false` (that's exactly how
|
|
242
|
+
non-throwing `judge()`/`evaluate()` gates have always worked) — `expect.soft`
|
|
243
|
+
just extends that same non-throwing contract to chai assertions. Mix soft and
|
|
244
|
+
hard freely in one body; a hard `expect()` after a soft failure still bails
|
|
245
|
+
at that point (source order still matters for the calls *after* a HARD
|
|
246
|
+
failure). Reach for `.soft` on measurement/budget-style axis checks where you
|
|
247
|
+
want every number on the report regardless of which gates failed; keep hard
|
|
248
|
+
`expect()` for a precondition that makes the rest of the body meaningless if
|
|
249
|
+
it's false (e.g. "the agent produced any output at all").
|
|
250
|
+
|
|
251
|
+
> **Known limitation: multi-step property chains can record a derivative
|
|
252
|
+
> second failure.** `expect.soft(obj).to.have.property('x').that.equals(5)`
|
|
253
|
+
> is really two chai assertions run back-to-back on the same chain (`property`
|
|
254
|
+
> checks existence, `equals` checks the value) — with a HARD `expect()`, a
|
|
255
|
+
> missing property throws immediately and `.that.equals(5)` never runs. In
|
|
256
|
+
> soft mode nothing throws, so `.that.equals(5)` *does* run against
|
|
257
|
+
> `undefined` and records its own "expected undefined to equal 5" failure —
|
|
258
|
+
> real, but a symptom of the first failure, not independent information.
|
|
259
|
+
> Prefer single-step matchers on primitive/simple values with `.soft` (as in
|
|
260
|
+
> every example above); for a value that might not exist, use a hard
|
|
261
|
+
> `expect()` to check existence first, then `.soft` on the value itself.
|
|
262
|
+
|
|
190
263
|
---
|
|
191
264
|
|
|
192
265
|
## Test options
|
|
@@ -483,8 +556,57 @@ enforced regardless of configuration.
|
|
|
483
556
|
|
|
484
557
|
---
|
|
485
558
|
|
|
559
|
+
## Weighted scoring across criteria
|
|
560
|
+
|
|
561
|
+
Each `expect(...)`, `judge(...)`, and `evaluate(...)` call records **one
|
|
562
|
+
per-matcher pass/fail** — they are not weighted against each other. When you
|
|
563
|
+
need a **single aggregate score** that weights criteria differently (e.g. root
|
|
564
|
+
cause 60%, SOP selection 20%, relevant-metrics check 10%, latency 10%), the
|
|
565
|
+
weighting lives in a **custom evaluator's `scoringConfig`**, not in the test
|
|
566
|
+
body. Define the weighted metrics once and attach the evaluator to the run via
|
|
567
|
+
`evaluatorId`:
|
|
568
|
+
|
|
569
|
+
```json
|
|
570
|
+
{
|
|
571
|
+
"name": "Ops RCA (weighted)",
|
|
572
|
+
"systemPrompt": "You evaluate an ops agent that triages a ticket. Score each metric 0-100. CRITICAL: root_cause_accuracy is the primary metric.",
|
|
573
|
+
"scoringConfig": {
|
|
574
|
+
"metrics": [
|
|
575
|
+
{ "name": "root_cause_accuracy", "weight": 0.6, "scale": 100 },
|
|
576
|
+
{ "name": "sop_selection", "weight": 0.2, "scale": 100 },
|
|
577
|
+
{ "name": "relevant_metrics", "weight": 0.1, "scale": 100 },
|
|
578
|
+
{ "name": "latency", "weight": 0.1, "scale": 100 }
|
|
579
|
+
],
|
|
580
|
+
"passThreshold": 80,
|
|
581
|
+
"scale": 100
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
The run's overall score is the weighted mean of the metrics the evaluator
|
|
587
|
+
emits, so you get one comparable number per run for progress tracking. Use the
|
|
588
|
+
deterministic `expect(...)` matchers for the hard checks (exact classification,
|
|
589
|
+
tool was called, budget cap) and the judge/evaluator + weighted `scoringConfig`
|
|
590
|
+
for the aggregate score. See the ops-RCA worked example at
|
|
591
|
+
[`examples/eval-files/ops-rca-classification.eval.js`](../examples/eval-files/ops-rca-classification.eval.js)
|
|
592
|
+
and its companion [`examples/eval-files/ops-rca-evaluator.json`](../examples/eval-files/ops-rca-evaluator.json),
|
|
593
|
+
and the evaluator reference in [docs/skills/AGENT_HEALTH.md](./skills/AGENT_HEALTH.md#custom-evaluators).
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
486
597
|
## Running the tests
|
|
487
598
|
|
|
599
|
+
> **Security note:** the ENTIRE contents of an imported `.eval.js`/`.eval.ts`
|
|
600
|
+
> file are persisted verbatim on the resulting test case (`sourceCode`) and
|
|
601
|
+
> rendered on the Test Case detail page as an IDE-style code view, so anyone
|
|
602
|
+
> who can view test cases in this deployment can read the full file --
|
|
603
|
+
> including any comments, hardcoded values, or internal URLs it contains.
|
|
604
|
+
> Treat eval files like any other source file that lands in your repo and
|
|
605
|
+
> gets deployed alongside the app: don't hardcode secrets, tokens, or
|
|
606
|
+
> customer data in them -- pull those from environment variables /
|
|
607
|
+
> `agent-health.config.ts` instead, same as you already would for the app's
|
|
608
|
+
> own credentials.
|
|
609
|
+
|
|
488
610
|
### Via the UI
|
|
489
611
|
|
|
490
612
|
`/evaluations/runs/new` → pick "Code import" → select your `.eval.js` files.
|
|
@@ -492,7 +614,7 @@ enforced regardless of configuration.
|
|
|
492
614
|
### Via the CLI
|
|
493
615
|
|
|
494
616
|
```bash
|
|
495
|
-
npx @opensearch-project/agent-health benchmark -f ./
|
|
617
|
+
npx @opensearch-project/agent-health benchmark -f ./examples/eval-files/demo.eval.js -a observio
|
|
496
618
|
```
|
|
497
619
|
|
|
498
620
|
### Via the HTTP API
|
|
@@ -504,7 +626,7 @@ curl -sN -X POST http://localhost:4001/api/storage/evaluation-runs \
|
|
|
504
626
|
"name": "Demo",
|
|
505
627
|
"sources": [{
|
|
506
628
|
"type": "code-import",
|
|
507
|
-
"filenames": ["
|
|
629
|
+
"filenames": ["examples/eval-files/demo.eval.js"],
|
|
508
630
|
"testCaseIds": []
|
|
509
631
|
}],
|
|
510
632
|
"agentKey": "observio",
|
|
@@ -526,6 +648,57 @@ The `.eval.js`, `.eval.ts`, and `.eval.mjs` loaders all run through a single
|
|
|
526
648
|
code-import execution path, so `benchmark -f <file>` executes the SDK body
|
|
527
649
|
directly.
|
|
528
650
|
|
|
651
|
+
> **`.eval.js` and `.eval.ts` are both executed as synthetic CJS — only
|
|
652
|
+
> `.eval.mjs` needs the package to be really resolvable.** `.eval.js` and
|
|
653
|
+
> `.eval.ts` files are both executed in a synthetic context where the
|
|
654
|
+
> `require(...)` call for `@opensearch-project/agent-health` is intercepted
|
|
655
|
+
> directly, so they work from anywhere on disk regardless of whether the
|
|
656
|
+
> package is actually installed at that location. (`.eval.ts` is
|
|
657
|
+
> transpiled to CommonJS with `esbuild` — a required runtime dependency of
|
|
658
|
+
> this package — before running through that exact same synthetic-CJS
|
|
659
|
+
> path; it deliberately does NOT use a native `import()`, both to avoid two
|
|
660
|
+
> different Node versions giving the same file two different
|
|
661
|
+
> module-execution semantics, and because `import()` caches by URL, which
|
|
662
|
+
> would make a `.eval.ts` file unloadable a second time in one process
|
|
663
|
+
> without a restart. One consequence: an `.eval.ts` fixture can't use
|
|
664
|
+
> `import.meta` or a top-level `await` — real-ESM-only features with no
|
|
665
|
+
> CommonJS equivalent.) `.eval.mjs` files use a plain native `import()`, so
|
|
666
|
+
> `import { test } from '@opensearch-project/agent-health'` in an `.mjs`
|
|
667
|
+
> file must resolve through Node's normal module resolution — the package
|
|
668
|
+
> needs to be a real dependency reachable from the file's location
|
|
669
|
+
> (installed in `node_modules`, a workspace link, or a `node_modules`
|
|
670
|
+
> symlink to a local checkout). Test registrations are shared process-wide
|
|
671
|
+
> (keyed off `globalThis`) regardless of loader, so it doesn't matter
|
|
672
|
+
> *which* physical copy of the package a given import resolves to — dist
|
|
673
|
+
> vs source, symlinked vs installed all register into the same registry
|
|
674
|
+
> the loader reads from.
|
|
675
|
+
|
|
676
|
+
> **`.eval.ts` can `import` sibling `.ts` helper files, but not ESM-only
|
|
677
|
+
> packages.** A relative/absolute `import` inside an `.eval.ts` file that
|
|
678
|
+
> resolves to another `.ts` file (e.g. `import { helper } from
|
|
679
|
+
> './helper.ts'`) is transpiled and executed through the same synthetic-CJS
|
|
680
|
+
> mechanism recursively — multi-file `.eval.ts` fixtures work, and a helper
|
|
681
|
+
> required more than once within one load executes exactly once (cached
|
|
682
|
+
> for the duration of that load only, never across separate loads/reloads).
|
|
683
|
+
> An `import` of a package that ships **no CommonJS entry point** (ESM-only,
|
|
684
|
+
> e.g. modern `chalk`) fails with an actionable error — `.eval.ts` executes
|
|
685
|
+
> as synthetic CJS, so under require()'s own rules that package can't load
|
|
686
|
+
> there — unless the running Node version has native `require(esm)`
|
|
687
|
+
> interop (stable since Node 22.12; check `process.features.require_module`),
|
|
688
|
+
> in which case it works transparently via Node's own mechanism. If you hit
|
|
689
|
+
> the error on an older Node, switch the fixture to `.eval.mjs` (real ESM,
|
|
690
|
+
> can import ESM-only packages directly) or pre-compile to `.eval.js`.
|
|
691
|
+
|
|
692
|
+
> **`.eval.mjs` reloads are cache-busted, not cached.** Every `import()` of
|
|
693
|
+
> an `.eval.mjs` file is given a unique query string
|
|
694
|
+
> (`?ah-reload=<timestamp>-<random>`) so re-loading the same file a second
|
|
695
|
+
> time in one process (e.g. re-running the CLI against an edited fixture)
|
|
696
|
+
> re-executes its top-level code instead of silently returning Node's
|
|
697
|
+
> already-cached module for that URL. Accepted cost: each reload leaves one
|
|
698
|
+
> module instance cached under a never-reused query string for the
|
|
699
|
+
> lifetime of the process — negligible for CLI/server process lifetimes
|
|
700
|
+
> (a handful to a few hundred loads, not an unbounded long-running loop).
|
|
701
|
+
|
|
529
702
|
---
|
|
530
703
|
|
|
531
704
|
## Dev tips
|
|
@@ -574,4 +747,4 @@ keeping it user-supplied lets you opt in without breaking anyone else.
|
|
|
574
747
|
- [x] Non-throwing run-scoped `judge` with `gate` / `observe` roles + `skip` + `orThrow()`
|
|
575
748
|
- [x] `defineEvaluator()` / `evaluate()` for mechanical / external verification ([#244](https://github.com/opensearch-project/agent-health/issues/244))
|
|
576
749
|
- [x] Single code-import execution path (`benchmark -f *.eval.js` runs the SDK body) + unified `.js` / `.ts` / `.mjs` loaders + `agent-health migrate sdk-v2` codemod
|
|
577
|
-
- [
|
|
750
|
+
- [x] `expect.soft()` to collect-all-failures instead of bail-on-first
|