@prismatic-io/lux 0.0.2-preview.14 → 0.0.2-preview.16
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/lib/answerers/claude-code/index.d.ts +14 -2
- package/lib/answerers/claude-code/index.d.ts.map +1 -1
- package/lib/answerers/claude-code/index.js +3 -3
- package/lib/answerers/claude-code/index.js.map +1 -1
- package/lib/answerers/persona/index.d.ts +28 -2
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/answerers/persona/index.js +3 -3
- package/lib/answerers/persona/index.js.map +1 -1
- package/lib/answerers/terminal/index.d.ts +8 -2
- package/lib/answerers/terminal/index.d.ts.map +1 -1
- package/lib/answerers/terminal/index.js +3 -2
- package/lib/answerers/terminal/index.js.map +1 -1
- package/lib/assertions/core/resource-checks.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.js +3 -5
- package/lib/assertions/core/resource-checks.js.map +1 -1
- package/lib/assertions/rubric/index.d.ts.map +1 -1
- package/lib/assertions/rubric/index.js +7 -1
- package/lib/assertions/rubric/index.js.map +1 -1
- package/lib/assertions/rubric/internal.d.ts +16 -5
- package/lib/assertions/rubric/internal.d.ts.map +1 -1
- package/lib/assertions/rubric/internal.js +10 -6
- package/lib/assertions/rubric/internal.js.map +1 -1
- package/lib/authoring.d.ts +32 -2
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +33 -1
- package/lib/authoring.js.map +1 -1
- package/lib/cli/bin.js +2 -13
- package/lib/cli/bin.js.map +1 -1
- package/lib/cli/command-runtime.d.ts +3 -3
- package/lib/cli/command-runtime.d.ts.map +1 -1
- package/lib/cli/command-runtime.js +17 -6
- package/lib/cli/command-runtime.js.map +1 -1
- package/lib/cli/init-templates.d.ts +11 -0
- package/lib/cli/init-templates.d.ts.map +1 -0
- package/lib/cli/init-templates.js +207 -0
- package/lib/cli/init-templates.js.map +1 -0
- package/lib/cli/init.d.ts +7 -0
- package/lib/cli/init.d.ts.map +1 -1
- package/lib/cli/init.js +23 -173
- package/lib/cli/init.js.map +1 -1
- package/lib/cli/output-schemas.d.ts +1399 -0
- package/lib/cli/output-schemas.d.ts.map +1 -0
- package/lib/cli/output-schemas.js +91 -0
- package/lib/cli/output-schemas.js.map +1 -0
- package/lib/cli/program.d.ts +135 -4
- package/lib/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +725 -520
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/render/reporter.d.ts.map +1 -1
- package/lib/cli/render/reporter.js +24 -4
- package/lib/cli/render/reporter.js.map +1 -1
- package/lib/cli/run-options.d.ts +0 -4
- package/lib/cli/run-options.d.ts.map +1 -1
- package/lib/cli/run-options.js +8 -19
- package/lib/cli/run-options.js.map +1 -1
- package/lib/cli/view.d.ts +5 -3
- package/lib/cli/view.d.ts.map +1 -1
- package/lib/cli/view.js +120 -78
- package/lib/cli/view.js.map +1 -1
- package/lib/core/annotation.d.ts +6 -2
- package/lib/core/annotation.d.ts.map +1 -1
- package/lib/core/annotation.js +2 -1
- package/lib/core/annotation.js.map +1 -1
- package/lib/core/answerer.d.ts +12 -0
- package/lib/core/answerer.d.ts.map +1 -1
- package/lib/core/answerer.js +2 -0
- package/lib/core/answerer.js.map +1 -1
- package/lib/core/assertion.d.ts +3 -0
- package/lib/core/assertion.d.ts.map +1 -1
- package/lib/core/assertion.js +3 -0
- package/lib/core/assertion.js.map +1 -1
- package/lib/core/case.d.ts +24 -2
- package/lib/core/case.d.ts.map +1 -1
- package/lib/core/case.js +5 -1
- package/lib/core/case.js.map +1 -1
- package/lib/core/driver.d.ts +40 -0
- package/lib/core/driver.d.ts.map +1 -1
- package/lib/core/driver.js +12 -0
- package/lib/core/driver.js.map +1 -1
- package/lib/core/environment.d.ts +18 -0
- package/lib/core/environment.d.ts.map +1 -0
- package/lib/core/environment.js +21 -0
- package/lib/core/environment.js.map +1 -0
- package/lib/core/evaluation-clusters.d.ts +17 -0
- package/lib/core/evaluation-clusters.d.ts.map +1 -0
- package/lib/core/evaluation-clusters.js +23 -0
- package/lib/core/evaluation-clusters.js.map +1 -0
- package/lib/core/experiment.d.ts +11 -0
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +31 -17
- package/lib/core/experiment.js.map +1 -1
- package/lib/core/file-tree.d.ts +7 -0
- package/lib/core/file-tree.d.ts.map +1 -0
- package/lib/core/file-tree.js +35 -0
- package/lib/core/file-tree.js.map +1 -0
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +1 -0
- package/lib/core/index.js.map +1 -1
- package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
- package/lib/core/lifecycle-fixtures.js +4 -2
- package/lib/core/lifecycle-fixtures.js.map +1 -1
- package/lib/core/platform-process.d.ts +5 -1
- package/lib/core/platform-process.d.ts.map +1 -1
- package/lib/core/platform-process.js +48 -10
- package/lib/core/platform-process.js.map +1 -1
- package/lib/core/run.d.ts +93 -1
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +16 -0
- package/lib/core/run.js.map +1 -1
- package/lib/core/usage.d.ts +5 -0
- package/lib/core/usage.d.ts.map +1 -1
- package/lib/core/usage.js +3 -0
- package/lib/core/usage.js.map +1 -1
- package/lib/drivers/claude-code/index.d.ts +65 -2
- package/lib/drivers/claude-code/index.d.ts.map +1 -1
- package/lib/drivers/claude-code/index.js +30 -3
- package/lib/drivers/claude-code/index.js.map +1 -1
- package/lib/drivers/codex/app-events.d.ts.map +1 -1
- package/lib/drivers/codex/app-events.js +41 -30
- package/lib/drivers/codex/app-events.js.map +1 -1
- package/lib/drivers/codex/exec-events.d.ts +4 -0
- package/lib/drivers/codex/exec-events.d.ts.map +1 -0
- package/lib/drivers/codex/exec-events.js +230 -0
- package/lib/drivers/codex/exec-events.js.map +1 -0
- package/lib/drivers/codex/index.d.ts +76 -4
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +35 -232
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/drivers/cursor/acp-transport.d.ts +38 -0
- package/lib/drivers/cursor/acp-transport.d.ts.map +1 -0
- package/lib/drivers/cursor/acp-transport.js +152 -0
- package/lib/drivers/cursor/acp-transport.js.map +1 -0
- package/lib/drivers/cursor/config.d.ts +22 -0
- package/lib/drivers/cursor/config.d.ts.map +1 -0
- package/lib/drivers/cursor/config.js +26 -0
- package/lib/drivers/cursor/config.js.map +1 -0
- package/lib/drivers/cursor/events.d.ts +13 -0
- package/lib/drivers/cursor/events.d.ts.map +1 -0
- package/lib/drivers/cursor/events.js +73 -0
- package/lib/drivers/cursor/events.js.map +1 -0
- package/lib/drivers/cursor/index.d.ts +37 -0
- package/lib/drivers/cursor/index.d.ts.map +1 -0
- package/lib/drivers/cursor/index.js +276 -0
- package/lib/drivers/cursor/index.js.map +1 -0
- package/lib/drivers/cursor/interaction.d.ts +7 -0
- package/lib/drivers/cursor/interaction.d.ts.map +1 -0
- package/lib/drivers/cursor/interaction.js +66 -0
- package/lib/drivers/cursor/interaction.js.map +1 -0
- package/lib/drivers/mcp/index.d.ts +35 -2
- package/lib/drivers/mcp/index.d.ts.map +1 -1
- package/lib/drivers/mcp/index.js +16 -3
- package/lib/drivers/mcp/index.js.map +1 -1
- package/lib/drivers/shared/experiment-behavior.d.ts +13 -0
- package/lib/drivers/shared/experiment-behavior.d.ts.map +1 -0
- package/lib/drivers/shared/experiment-behavior.js +197 -0
- package/lib/drivers/shared/experiment-behavior.js.map +1 -0
- package/lib/drivers/shared/experiment-policy.d.ts +4 -0
- package/lib/drivers/shared/experiment-policy.d.ts.map +1 -0
- package/lib/drivers/shared/experiment-policy.js +143 -0
- package/lib/drivers/shared/experiment-policy.js.map +1 -0
- package/lib/drivers/subprocess/index.d.ts +37 -2
- package/lib/drivers/subprocess/index.d.ts.map +1 -1
- package/lib/drivers/subprocess/index.js +46 -6
- package/lib/drivers/subprocess/index.js.map +1 -1
- package/lib/index.d.ts +6 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/orchestrator/annotation-loader.d.ts +6 -2
- package/lib/orchestrator/annotation-loader.d.ts.map +1 -1
- package/lib/orchestrator/annotation-loader.js +8 -0
- package/lib/orchestrator/annotation-loader.js.map +1 -1
- package/lib/orchestrator/annotation-store.d.ts.map +1 -1
- package/lib/orchestrator/annotation-store.js +16 -2
- package/lib/orchestrator/annotation-store.js.map +1 -1
- package/lib/orchestrator/campaign-lifecycle.d.ts +1 -0
- package/lib/orchestrator/campaign-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/campaign-lifecycle.js +4 -1
- package/lib/orchestrator/campaign-lifecycle.js.map +1 -1
- package/lib/orchestrator/compare.d.ts +4 -0
- package/lib/orchestrator/compare.d.ts.map +1 -1
- package/lib/orchestrator/compare.js +13 -1
- package/lib/orchestrator/compare.js.map +1 -1
- package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
- package/lib/orchestrator/comparison-identity.js +1 -0
- package/lib/orchestrator/comparison-identity.js.map +1 -1
- package/lib/orchestrator/config.d.ts +42 -0
- package/lib/orchestrator/config.d.ts.map +1 -1
- package/lib/orchestrator/config.js +6 -2
- package/lib/orchestrator/config.js.map +1 -1
- package/lib/orchestrator/discover.d.ts +1 -0
- package/lib/orchestrator/discover.d.ts.map +1 -1
- package/lib/orchestrator/discover.js +7 -1
- package/lib/orchestrator/discover.js.map +1 -1
- package/lib/orchestrator/doctor.d.ts.map +1 -1
- package/lib/orchestrator/doctor.js +34 -26
- package/lib/orchestrator/doctor.js.map +1 -1
- package/lib/orchestrator/driver-capabilities.d.ts +3 -0
- package/lib/orchestrator/driver-capabilities.d.ts.map +1 -0
- package/lib/orchestrator/driver-capabilities.js +10 -0
- package/lib/orchestrator/driver-capabilities.js.map +1 -0
- package/lib/orchestrator/driver-identity.d.ts +5 -0
- package/lib/orchestrator/driver-identity.d.ts.map +1 -0
- package/lib/orchestrator/driver-identity.js +41 -0
- package/lib/orchestrator/driver-identity.js.map +1 -0
- package/lib/orchestrator/experiment-context.d.ts +10 -0
- package/lib/orchestrator/experiment-context.d.ts.map +1 -1
- package/lib/orchestrator/experiment-contracts.d.ts +10 -0
- package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.js +17 -19
- package/lib/orchestrator/experiment-corpus.js.map +1 -1
- package/lib/orchestrator/experiment-effects.d.ts +33 -0
- package/lib/orchestrator/experiment-effects.d.ts.map +1 -0
- package/lib/orchestrator/experiment-effects.js +44 -0
- package/lib/orchestrator/experiment-effects.js.map +1 -0
- package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.js +36 -3
- package/lib/orchestrator/experiment-evaluation.js.map +1 -1
- package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-identity.js +18 -29
- package/lib/orchestrator/experiment-identity.js.map +1 -1
- package/lib/orchestrator/experiment-loader.d.ts +7 -2
- package/lib/orchestrator/experiment-loader.d.ts.map +1 -1
- package/lib/orchestrator/experiment-report.d.ts +70 -0
- package/lib/orchestrator/experiment-report.d.ts.map +1 -1
- package/lib/orchestrator/experiment-report.js +9 -1
- package/lib/orchestrator/experiment-report.js.map +1 -1
- package/lib/orchestrator/experiment-runs.d.ts +22 -1
- package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runs.js +13 -5
- package/lib/orchestrator/experiment-runs.js.map +1 -1
- package/lib/orchestrator/experiment-runtime-identity.d.ts +0 -16
- package/lib/orchestrator/experiment-runtime-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runtime-identity.js +5 -220
- package/lib/orchestrator/experiment-runtime-identity.js.map +1 -1
- package/lib/orchestrator/experiment-selection.d.ts.map +1 -1
- package/lib/orchestrator/experiment-selection.js +52 -0
- package/lib/orchestrator/experiment-selection.js.map +1 -1
- package/lib/orchestrator/experiment.d.ts +0 -2
- package/lib/orchestrator/experiment.d.ts.map +1 -1
- package/lib/orchestrator/experiment.js +23 -15
- package/lib/orchestrator/experiment.js.map +1 -1
- package/lib/orchestrator/fs-read.d.ts +1 -6
- package/lib/orchestrator/fs-read.d.ts.map +1 -1
- package/lib/orchestrator/fs-read.js +2 -32
- package/lib/orchestrator/fs-read.js.map +1 -1
- package/lib/orchestrator/grader-experiment.d.ts +17 -1
- package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
- package/lib/orchestrator/grader-experiment.js +52 -7
- package/lib/orchestrator/grader-experiment.js.map +1 -1
- package/lib/orchestrator/grading.d.ts.map +1 -1
- package/lib/orchestrator/grading.js +13 -6
- package/lib/orchestrator/grading.js.map +1 -1
- package/lib/orchestrator/list-runs.d.ts +1 -0
- package/lib/orchestrator/list-runs.d.ts.map +1 -1
- package/lib/orchestrator/list-runs.js +27 -6
- package/lib/orchestrator/list-runs.js.map +1 -1
- package/lib/orchestrator/optimization-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/optimization-lifecycle.js +3 -1
- package/lib/orchestrator/optimization-lifecycle.js.map +1 -1
- package/lib/orchestrator/orchestrator.d.ts +6 -1
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +17 -143
- package/lib/orchestrator/orchestrator.js.map +1 -1
- package/lib/orchestrator/paired-effects.d.ts +34 -0
- package/lib/orchestrator/paired-effects.d.ts.map +1 -0
- package/lib/orchestrator/paired-effects.js +158 -0
- package/lib/orchestrator/paired-effects.js.map +1 -0
- package/lib/orchestrator/provenance.d.ts +5 -1
- package/lib/orchestrator/provenance.d.ts.map +1 -1
- package/lib/orchestrator/provenance.js +16 -3
- package/lib/orchestrator/provenance.js.map +1 -1
- package/lib/orchestrator/regrade.d.ts +1 -0
- package/lib/orchestrator/regrade.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.d.ts +2 -0
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +18 -3
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/lib/orchestrator/run-lifecycle.d.ts +1 -0
- package/lib/orchestrator/run-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/run-lifecycle.js +3 -2
- package/lib/orchestrator/run-lifecycle.js.map +1 -1
- package/lib/orchestrator/suite-runs.d.ts +3 -1
- package/lib/orchestrator/suite-runs.d.ts.map +1 -1
- package/lib/orchestrator/suite-runs.js +12 -2
- package/lib/orchestrator/suite-runs.js.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +2 -1
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/lib/previewer/server.d.ts +6 -0
- package/lib/previewer/server.d.ts.map +1 -1
- package/lib/previewer/server.js +21 -5
- package/lib/previewer/server.js.map +1 -1
- package/package.json +2 -2
- package/skills/lux/references/cli.md +5 -2
- package/skills/lux/references/eval-authoring.md +0 -7
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/answerers/claude-code/index.ts +3 -3
- package/src/answerers/persona/index.ts +3 -3
- package/src/answerers/terminal/index.ts +4 -9
- package/src/assertions/core/resource-checks.ts +3 -5
- package/src/assertions/rubric/index.ts +7 -1
- package/src/assertions/rubric/internal.ts +13 -8
- package/src/authoring.ts +331 -2
- package/src/cli/bin.ts +2 -12
- package/src/cli/command-runtime.ts +20 -9
- package/src/cli/init-templates.ts +223 -0
- package/src/cli/init.ts +38 -184
- package/src/cli/output-schemas.ts +106 -0
- package/src/cli/program.ts +621 -548
- package/src/cli/render/reporter.ts +25 -4
- package/src/cli/run-options.ts +8 -25
- package/src/cli/view.ts +45 -84
- package/src/core/annotation.ts +2 -1
- package/src/core/answerer.ts +9 -0
- package/src/core/assertion.ts +3 -0
- package/src/core/case.ts +5 -1
- package/src/core/driver.ts +38 -0
- package/src/core/environment.ts +22 -0
- package/src/core/evaluation-clusters.ts +35 -0
- package/src/core/experiment.ts +60 -23
- package/src/core/file-tree.ts +35 -0
- package/src/core/index.ts +1 -0
- package/src/core/lifecycle-fixtures.ts +4 -2
- package/src/core/platform-process.ts +54 -6
- package/src/core/run.ts +20 -0
- package/src/core/usage.ts +8 -0
- package/src/drivers/claude-code/index.ts +34 -3
- package/src/drivers/codex/app-events.ts +43 -33
- package/src/drivers/codex/exec-events.ts +258 -0
- package/src/drivers/codex/index.ts +37 -262
- package/src/drivers/cursor/README.md +57 -0
- package/src/drivers/cursor/acp-transport.ts +182 -0
- package/src/drivers/cursor/config.ts +29 -0
- package/src/drivers/cursor/events.ts +83 -0
- package/src/drivers/cursor/index.ts +316 -0
- package/src/drivers/cursor/interaction.ts +90 -0
- package/src/drivers/mcp/index.ts +16 -3
- package/src/drivers/shared/experiment-behavior.ts +214 -0
- package/src/drivers/shared/experiment-policy.ts +195 -0
- package/src/drivers/subprocess/index.ts +45 -6
- package/src/index.ts +16 -5
- package/src/orchestrator/annotation-loader.ts +10 -0
- package/src/orchestrator/annotation-store.ts +24 -3
- package/src/orchestrator/campaign-lifecycle.ts +5 -1
- package/src/orchestrator/compare.ts +16 -1
- package/src/orchestrator/comparison-identity.ts +1 -0
- package/src/orchestrator/config.ts +6 -2
- package/src/orchestrator/discover.ts +8 -1
- package/src/orchestrator/doctor.ts +40 -22
- package/src/orchestrator/driver-capabilities.ts +16 -0
- package/src/orchestrator/driver-identity.ts +59 -0
- package/src/orchestrator/experiment-corpus.ts +23 -28
- package/src/orchestrator/experiment-effects.ts +61 -0
- package/src/orchestrator/experiment-evaluation.ts +45 -5
- package/src/orchestrator/experiment-identity.ts +27 -36
- package/src/orchestrator/experiment-report.ts +21 -1
- package/src/orchestrator/experiment-runs.ts +11 -4
- package/src/orchestrator/experiment-runtime-identity.ts +5 -243
- package/src/orchestrator/experiment-selection.ts +77 -0
- package/src/orchestrator/experiment.ts +26 -24
- package/src/orchestrator/fs-read.ts +2 -32
- package/src/orchestrator/grader-experiment.ts +72 -8
- package/src/orchestrator/grading.ts +15 -6
- package/src/orchestrator/list-runs.ts +29 -6
- package/src/orchestrator/optimization-lifecycle.ts +4 -1
- package/src/orchestrator/orchestrator.ts +26 -183
- package/src/orchestrator/paired-effects.ts +209 -0
- package/src/orchestrator/provenance.ts +23 -3
- package/src/orchestrator/run-execution.ts +18 -3
- package/src/orchestrator/run-lifecycle.ts +4 -2
- package/src/orchestrator/suite-runs.ts +10 -1
- package/src/orchestrator/suite-summary.ts +2 -1
- package/src/previewer/server.ts +27 -5
- package/viewer/app.js +27 -6
- package/viewer/detail.js +22 -5
- package/lib/answerers/scripted/index.d.ts +0 -18
- package/lib/answerers/scripted/index.d.ts.map +0 -1
- package/lib/answerers/scripted/index.js +0 -63
- package/lib/answerers/scripted/index.js.map +0 -1
- package/lib/cli/skills.d.ts +0 -38
- package/lib/cli/skills.d.ts.map +0 -1
- package/lib/cli/skills.js +0 -84
- package/lib/cli/skills.js.map +0 -1
- package/src/answerers/scripted/index.ts +0 -78
- package/src/cli/skills.ts +0 -129
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
stableJson,
|
|
18
18
|
totalTokens,
|
|
19
19
|
} from "../core/index.js";
|
|
20
|
+
import { hasAgentUsage } from "../core/usage.js";
|
|
20
21
|
import { stableDriverIdentity } from "./comparison-identity.js";
|
|
21
22
|
import { safeReadJson, walkFiles } from "./fs-read.js";
|
|
22
23
|
|
|
@@ -59,6 +60,7 @@ export type RunComparison = {
|
|
|
59
60
|
b: { ask: number; approve: number; custom: number };
|
|
60
61
|
};
|
|
61
62
|
usage: { a: RunUsage; b: RunUsage };
|
|
63
|
+
agentUsageAvailable?: { a: boolean; b: boolean };
|
|
62
64
|
} | null;
|
|
63
65
|
grading: {
|
|
64
66
|
casePassedA: boolean | null;
|
|
@@ -232,6 +234,11 @@ export const compareRuns = async (input: ComparisonInput): Promise<RunComparison
|
|
|
232
234
|
if (stableJson(runtimeA) !== stableJson(runtimeB)) {
|
|
233
235
|
caseMismatch.push({ field: "runtime", a: runtimeA, b: runtimeB });
|
|
234
236
|
}
|
|
237
|
+
const environmentA = a.runJson?.provenance?.environment;
|
|
238
|
+
const environmentB = b.runJson?.provenance?.environment;
|
|
239
|
+
if (stableJson(environmentA) !== stableJson(environmentB)) {
|
|
240
|
+
caseMismatch.push({ field: "environment", a: environmentA, b: environmentB });
|
|
241
|
+
}
|
|
235
242
|
const sourceA = a.runJson?.provenance?.source;
|
|
236
243
|
const sourceB = b.runJson?.provenance?.source;
|
|
237
244
|
const sourceHashUnavailable =
|
|
@@ -282,6 +289,10 @@ export const compareRuns = async (input: ComparisonInput): Promise<RunComparison
|
|
|
282
289
|
a: a.runJson?.interruptCounts ?? { ask: 0, approve: 0, custom: 0 },
|
|
283
290
|
b: b.runJson?.interruptCounts ?? { ask: 0, approve: 0, custom: 0 },
|
|
284
291
|
},
|
|
292
|
+
agentUsageAvailable: {
|
|
293
|
+
a: a.runJson !== null && hasAgentUsage(a.runJson),
|
|
294
|
+
b: b.runJson !== null && hasAgentUsage(b.runJson),
|
|
295
|
+
},
|
|
285
296
|
usage: {
|
|
286
297
|
a: a.runJson?.usage ?? emptyRunUsage(),
|
|
287
298
|
b: b.runJson?.usage ?? emptyRunUsage(),
|
|
@@ -350,7 +361,11 @@ export const formatComparisonText = (cmp: RunComparison): string => {
|
|
|
350
361
|
);
|
|
351
362
|
const au = cmp.runDelta.usage.a;
|
|
352
363
|
const bu = cmp.runDelta.usage.b;
|
|
353
|
-
|
|
364
|
+
const agentA =
|
|
365
|
+
cmp.runDelta.agentUsageAvailable?.a === false ? "unavailable" : totalTokens(au.agent);
|
|
366
|
+
const agentB =
|
|
367
|
+
cmp.runDelta.agentUsageAvailable?.b === false ? "unavailable" : totalTokens(bu.agent);
|
|
368
|
+
lines.push(` agent tokens: ${agentA} → ${agentB}`);
|
|
354
369
|
lines.push(
|
|
355
370
|
` harness tokens: ${totalTokens(harnessUsage(au))} → ${totalTokens(harnessUsage(bu))}`,
|
|
356
371
|
);
|
|
@@ -31,6 +31,7 @@ export const runComparisonIdentity = (metadata: RunMetadata): string =>
|
|
|
31
31
|
valueHash({
|
|
32
32
|
source: sourceIdentity(metadata),
|
|
33
33
|
runtime: metadata.provenance?.runtime,
|
|
34
|
+
environment: metadata.provenance?.environment,
|
|
34
35
|
caseHash: metadata.provenance?.caseHash,
|
|
35
36
|
assertionsHash: metadata.provenance?.assertionsHash,
|
|
36
37
|
fixturesHash: metadata.provenance?.fixturesHash,
|
|
@@ -4,11 +4,11 @@ import { pathToFileURL } from "node:url";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { claudeCodeAnswerer } from "../answerers/claude-code/index.js";
|
|
6
6
|
import { personaAnswerer } from "../answerers/persona/index.js";
|
|
7
|
-
import { scriptedAnswerer } from "../answerers/scripted/index.js";
|
|
8
7
|
import { terminalAnswerer } from "../answerers/terminal/index.js";
|
|
9
8
|
import { allCoreAssertions } from "../assertions/core/index.js";
|
|
10
9
|
import { allMcpAssertions } from "../assertions/mcp/index.js";
|
|
11
10
|
import { createRubricAssertion } from "../assertions/rubric/index.js";
|
|
11
|
+
import { EnvironmentDeclarationSchema } from "../core/environment.js";
|
|
12
12
|
import {
|
|
13
13
|
type AnswererPlugin,
|
|
14
14
|
AnswererRefSchema,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../core/index.js";
|
|
26
26
|
import { claudeCodeDriver } from "../drivers/claude-code/index.js";
|
|
27
27
|
import { codexDriver } from "../drivers/codex/index.js";
|
|
28
|
+
import { cursorDriver } from "../drivers/cursor/index.js";
|
|
28
29
|
import { mcpProbeDriver } from "../drivers/mcp/index.js";
|
|
29
30
|
import { subprocessDriver } from "../drivers/subprocess/index.js";
|
|
30
31
|
|
|
@@ -35,6 +36,7 @@ const pluginLike = z.looseObject({
|
|
|
35
36
|
|
|
36
37
|
export const ExperimentProfileSchema = z
|
|
37
38
|
.object({
|
|
39
|
+
environment: EnvironmentDeclarationSchema.optional(),
|
|
38
40
|
driver: DriverRefSchema.optional(),
|
|
39
41
|
answerer: AnswererRefSchema.optional(),
|
|
40
42
|
harness: HarnessModelConfigSchema.optional(),
|
|
@@ -70,6 +72,7 @@ const LifecycleHooksPluginSchema = z.custom<LifecycleHooksPlugin>().superRefine(
|
|
|
70
72
|
|
|
71
73
|
const LuxConfigSchema = z
|
|
72
74
|
.object({
|
|
75
|
+
environment: EnvironmentDeclarationSchema.optional(),
|
|
73
76
|
casesRoot: z.string().optional(),
|
|
74
77
|
fixturesRoot: z.string().optional(),
|
|
75
78
|
runsRoot: z.string().default(".lux-runs"),
|
|
@@ -165,6 +168,7 @@ export const loadLuxConfig = async (cwd: string = process.cwd()): Promise<Resolv
|
|
|
165
168
|
...(validated.defaultDriver ? { defaultDriver: validated.defaultDriver } : {}),
|
|
166
169
|
...(validated.harness ? { harness: validated.harness } : {}),
|
|
167
170
|
concurrency: validated.concurrency,
|
|
171
|
+
...(validated.environment ? { environment: validated.environment } : {}),
|
|
168
172
|
...(validated.profiles ? { profiles: validated.profiles } : {}),
|
|
169
173
|
...(validated.drivers ? { drivers: validated.drivers } : {}),
|
|
170
174
|
...(validated.answerers ? { answerers: validated.answerers } : {}),
|
|
@@ -176,13 +180,13 @@ export const loadLuxConfig = async (cwd: string = process.cwd()): Promise<Resolv
|
|
|
176
180
|
const builtinDrivers: DriverPlugin<never>[] = [
|
|
177
181
|
claudeCodeDriver,
|
|
178
182
|
codexDriver,
|
|
183
|
+
cursorDriver,
|
|
179
184
|
subprocessDriver,
|
|
180
185
|
mcpProbeDriver,
|
|
181
186
|
];
|
|
182
187
|
const builtinAnswerers: AnswererPlugin<never>[] = [
|
|
183
188
|
personaAnswerer,
|
|
184
189
|
claudeCodeAnswerer,
|
|
185
|
-
scriptedAnswerer,
|
|
186
190
|
terminalAnswerer,
|
|
187
191
|
];
|
|
188
192
|
export const buildRegistryFromConfig = (config: ResolvedLuxConfig): Registry => {
|
|
@@ -13,6 +13,7 @@ export type DiscoveredCase = {
|
|
|
13
13
|
relPath: string;
|
|
14
14
|
/** `meta.tags` normalized to strings; empty when unset. */
|
|
15
15
|
tags: string[];
|
|
16
|
+
cluster?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -96,7 +97,13 @@ export const discoverCases = async (root: string): Promise<DiscoveryResult> => {
|
|
|
96
97
|
problems.push({ path, reason: formatZodIssues(parsed.error) });
|
|
97
98
|
continue;
|
|
98
99
|
}
|
|
99
|
-
cases.push({
|
|
100
|
+
cases.push({
|
|
101
|
+
path,
|
|
102
|
+
id: parsed.data.id ?? relPath,
|
|
103
|
+
relPath,
|
|
104
|
+
tags: tagsOf(parsed.data.meta),
|
|
105
|
+
...(parsed.data.meta?.cluster ? { cluster: parsed.data.meta.cluster } : {}),
|
|
106
|
+
});
|
|
100
107
|
}
|
|
101
108
|
return { cases, problems };
|
|
102
109
|
};
|
|
@@ -8,12 +8,14 @@ import type { EvalCase, Registry } from "../core/index.js";
|
|
|
8
8
|
import { registryForEvalCase } from "./case-extensions.js";
|
|
9
9
|
import { buildRegistryFromConfig, loadLuxConfig, type ResolvedLuxConfig } from "./config.js";
|
|
10
10
|
import { type DiscoveredCase, type DiscoveryResult, discoverCases } from "./discover.js";
|
|
11
|
-
import {
|
|
11
|
+
import { validateDriverCapabilities } from "./driver-capabilities.js";
|
|
12
|
+
import { ExperimentCampaignPlanSchema, planExperimentCampaign } from "./experiment.js";
|
|
12
13
|
import {
|
|
13
14
|
experimentCampaignMode,
|
|
14
15
|
loadExperimentCampaign,
|
|
15
16
|
resolveExperimentCampaignPath,
|
|
16
17
|
} from "./experiment-loader.js";
|
|
18
|
+
import { cliIdentity } from "./experiment-runtime-identity.js";
|
|
17
19
|
import { assertionConfigForSchema } from "./grading.js";
|
|
18
20
|
import { loadEvalCase } from "./loader.js";
|
|
19
21
|
import { validatePluginConfig, validatePluginConfigAsync } from "./validate-plugin-config.js";
|
|
@@ -142,17 +144,20 @@ const caseDriverReferences = (local: unknown, config: ResolvedLuxConfig): unknow
|
|
|
142
144
|
];
|
|
143
145
|
};
|
|
144
146
|
|
|
145
|
-
const
|
|
147
|
+
const driverCommands = (reference: unknown, registry: Registry): string[] => {
|
|
146
148
|
const name = refName(reference);
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
const plugin = name ? registry.getDriver(name) : undefined;
|
|
150
|
+
if (!plugin?.runtime) return [];
|
|
151
|
+
try {
|
|
152
|
+
const config = validatePluginConfig(
|
|
153
|
+
plugin.configSchema,
|
|
154
|
+
rawRefConfig(reference),
|
|
155
|
+
`driver '${name}'`,
|
|
156
|
+
);
|
|
157
|
+
return plugin.runtime(config).commands;
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
154
160
|
}
|
|
155
|
-
return undefined;
|
|
156
161
|
};
|
|
157
162
|
|
|
158
163
|
const answererCommand = (reference: unknown): string | undefined => {
|
|
@@ -179,12 +184,13 @@ const rubricCommands = (assertion: unknown, config: ResolvedLuxConfig): string[]
|
|
|
179
184
|
return [...new Set(commands)];
|
|
180
185
|
};
|
|
181
186
|
|
|
182
|
-
const configuredCliTargets = (config: ResolvedLuxConfig): CliTarget[] => {
|
|
187
|
+
const configuredCliTargets = (config: ResolvedLuxConfig, registry: Registry): CliTarget[] => {
|
|
183
188
|
const targets: CliTarget[] = [];
|
|
184
189
|
const add = (label: string, command: string | undefined): void => {
|
|
185
190
|
if (command) targets.push({ label, command });
|
|
186
191
|
};
|
|
187
|
-
|
|
192
|
+
for (const command of driverCommands(config.defaultDriver, registry))
|
|
193
|
+
add("default driver", command);
|
|
188
194
|
add("default answerer", answererCommand(config.defaultAnswerer));
|
|
189
195
|
if (config.harness) {
|
|
190
196
|
add(
|
|
@@ -195,7 +201,9 @@ const configuredCliTargets = (config: ResolvedLuxConfig): CliTarget[] => {
|
|
|
195
201
|
for (const [id, profile] of Object.entries(config.profiles ?? {}).sort(([a], [b]) =>
|
|
196
202
|
a.localeCompare(b),
|
|
197
203
|
)) {
|
|
198
|
-
|
|
204
|
+
for (const command of driverCommands(profile.driver ?? config.defaultDriver, registry)) {
|
|
205
|
+
add(`profile '${id}' driver`, command);
|
|
206
|
+
}
|
|
199
207
|
add(`profile '${id}' answerer`, answererCommand(profile.answerer ?? config.defaultAnswerer));
|
|
200
208
|
const harness = profile.harness ?? config.harness;
|
|
201
209
|
if (harness) {
|
|
@@ -418,17 +426,19 @@ const caseDriverFailure = (
|
|
|
418
426
|
caseId: string,
|
|
419
427
|
reference: unknown,
|
|
420
428
|
registry: Registry,
|
|
429
|
+
required: EvalCase["requires"],
|
|
421
430
|
): string | null => {
|
|
422
431
|
const name = refName(reference);
|
|
423
432
|
if (!name) return "driver reference has no name";
|
|
424
433
|
const plugin = registry.getDriver(name);
|
|
425
434
|
if (!plugin) return `unknown driver '${name}'`;
|
|
426
435
|
try {
|
|
427
|
-
validatePluginConfig(
|
|
436
|
+
const config = validatePluginConfig(
|
|
428
437
|
plugin.configSchema,
|
|
429
438
|
rawRefConfig(reference),
|
|
430
439
|
`case '${caseId}' driver '${name}'`,
|
|
431
440
|
);
|
|
441
|
+
validateDriverCapabilities(plugin, config, required);
|
|
432
442
|
return null;
|
|
433
443
|
} catch (error) {
|
|
434
444
|
return firstLine(error);
|
|
@@ -440,6 +450,7 @@ const validateCaseDrivers = (
|
|
|
440
450
|
drivers: readonly unknown[],
|
|
441
451
|
registry: Registry,
|
|
442
452
|
recorder: DoctorRecorder,
|
|
453
|
+
required: EvalCase["requires"],
|
|
443
454
|
): void => {
|
|
444
455
|
if (drivers.length === 0) {
|
|
445
456
|
recorder.add(
|
|
@@ -450,7 +461,7 @@ const validateCaseDrivers = (
|
|
|
450
461
|
return;
|
|
451
462
|
}
|
|
452
463
|
const failures = drivers
|
|
453
|
-
.map((driver) => caseDriverFailure(caseId, driver, registry))
|
|
464
|
+
.map((driver) => caseDriverFailure(caseId, driver, registry, required))
|
|
454
465
|
.filter((failure): failure is string => failure !== null);
|
|
455
466
|
if (failures.length === drivers.length) {
|
|
456
467
|
recorder.add(
|
|
@@ -542,13 +553,16 @@ const executionTargetsForCase = (
|
|
|
542
553
|
caseId: string,
|
|
543
554
|
drivers: readonly unknown[],
|
|
544
555
|
answerer: unknown,
|
|
556
|
+
registry: Registry,
|
|
545
557
|
): CliTarget[] => {
|
|
546
558
|
const targets = drivers.flatMap((driver): CliTarget[] => {
|
|
547
|
-
|
|
548
|
-
|
|
559
|
+
return driverCommands(driver, registry).map((command) => ({
|
|
560
|
+
label: `case '${caseId}' driver`,
|
|
561
|
+
command,
|
|
562
|
+
}));
|
|
549
563
|
});
|
|
550
|
-
const
|
|
551
|
-
if (
|
|
564
|
+
const answererCli = answererCommand(answerer);
|
|
565
|
+
if (answererCli) targets.push({ label: `case '${caseId}' answerer`, command: answererCli });
|
|
552
566
|
return targets;
|
|
553
567
|
};
|
|
554
568
|
|
|
@@ -563,11 +577,11 @@ const inspectCase = async (
|
|
|
563
577
|
const caseRegistry = registryForEvalCase(registry, authored);
|
|
564
578
|
const drivers = caseDriverReferences(authored.driver, config);
|
|
565
579
|
const answerer = authored.answerer ?? config.defaultAnswerer;
|
|
566
|
-
validateCaseDrivers(discovered.id, drivers, caseRegistry, recorder);
|
|
580
|
+
validateCaseDrivers(discovered.id, drivers, caseRegistry, recorder, authored.requires);
|
|
567
581
|
validateCaseAnswerer(discovered.id, answerer, caseRegistry, recorder);
|
|
568
582
|
return [
|
|
569
583
|
...(await validateCaseAssertions(discovered.id, authored, config, caseRegistry, recorder)),
|
|
570
|
-
...executionTargetsForCase(discovered.id, drivers, answerer),
|
|
584
|
+
...executionTargetsForCase(discovered.id, drivers, answerer, caseRegistry),
|
|
571
585
|
];
|
|
572
586
|
} catch (error) {
|
|
573
587
|
recorder.add(
|
|
@@ -735,7 +749,11 @@ export const doctorProject = async (options: DoctorOptions): Promise<DoctorRepor
|
|
|
735
749
|
validateConfiguredDrivers(config, registry, recorder);
|
|
736
750
|
validateConfiguredAnswerers(config, registry, recorder);
|
|
737
751
|
const caseCliTargets = await inspectCases(config, registry, recorder);
|
|
738
|
-
await inspectCliTargets(
|
|
752
|
+
await inspectCliTargets(
|
|
753
|
+
[...configuredCliTargets(config, registry), ...caseCliTargets],
|
|
754
|
+
config,
|
|
755
|
+
recorder,
|
|
756
|
+
);
|
|
739
757
|
await inspectCampaigns(parsedOptions.campaigns ?? [], config, registry, recorder);
|
|
740
758
|
return recorder.finish(config.rootDir);
|
|
741
759
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DriverCapability, DriverPlugin } from "../core/driver.js";
|
|
2
|
+
|
|
3
|
+
export const validateDriverCapabilities = (
|
|
4
|
+
plugin: DriverPlugin,
|
|
5
|
+
config: unknown,
|
|
6
|
+
required: readonly DriverCapability[] = [],
|
|
7
|
+
): void => {
|
|
8
|
+
if (required.length === 0) return;
|
|
9
|
+
const capabilities = plugin.runtime?.(config).capabilities;
|
|
10
|
+
const missing = required.filter((capability) => capabilities?.[capability] !== true);
|
|
11
|
+
if (missing.length > 0) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`driver '${plugin.name}' does not declare required capabilities: ${missing.join(", ")}`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DriverPlugin, DriverRuntime } from "../core/driver.js";
|
|
2
|
+
import type { Registry } from "../core/registry.js";
|
|
3
|
+
import { cliIdentity } from "./experiment-runtime-identity.js";
|
|
4
|
+
import { validatePluginConfig } from "./validate-plugin-config.js";
|
|
5
|
+
|
|
6
|
+
const resolvedDriver = (
|
|
7
|
+
reference: unknown,
|
|
8
|
+
registry: Registry,
|
|
9
|
+
): { plugin: DriverPlugin; config: unknown } | undefined => {
|
|
10
|
+
const name =
|
|
11
|
+
typeof reference === "string" ? reference : (reference as { name?: unknown } | null)?.name;
|
|
12
|
+
if (typeof name !== "string") return undefined;
|
|
13
|
+
const plugin = registry.getDriver(name);
|
|
14
|
+
if (!plugin) throw new Error(`unknown driver '${name}'`);
|
|
15
|
+
const raw =
|
|
16
|
+
typeof reference === "object" && reference !== null
|
|
17
|
+
? (reference as { config?: unknown }).config
|
|
18
|
+
: undefined;
|
|
19
|
+
return { plugin, config: validatePluginConfig(plugin.configSchema, raw, `driver '${name}'`) };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const referencedDriverRuntime = (reference: unknown, registry: Registry): DriverRuntime => {
|
|
23
|
+
const resolved = resolvedDriver(reference, registry);
|
|
24
|
+
return resolved?.plugin.runtime?.(resolved.config) ?? { commands: [], capabilities: {} };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const experimentDriverIdentity = async (
|
|
28
|
+
reference: unknown,
|
|
29
|
+
registry: Registry,
|
|
30
|
+
rootDir: string,
|
|
31
|
+
): Promise<unknown> => {
|
|
32
|
+
const resolved = resolvedDriver(reference, registry);
|
|
33
|
+
if (!resolved) return null;
|
|
34
|
+
const { plugin, config } = resolved;
|
|
35
|
+
const experiment = plugin.experiment;
|
|
36
|
+
if (!experiment)
|
|
37
|
+
throw new Error(
|
|
38
|
+
`driver '${plugin.name}' supports ordinary eval runs but does not declare controlled experiment support`,
|
|
39
|
+
);
|
|
40
|
+
if (!experiment.version.trim())
|
|
41
|
+
throw new Error(`driver '${plugin.name}' experiment version must not be empty`);
|
|
42
|
+
const prepared = validatePluginConfig(
|
|
43
|
+
plugin.configSchema,
|
|
44
|
+
experiment.prepare(config),
|
|
45
|
+
`driver '${plugin.name}' experiment configuration`,
|
|
46
|
+
);
|
|
47
|
+
const runtime = plugin.runtime?.(prepared) ?? { commands: [], capabilities: {} };
|
|
48
|
+
const behavior = await experiment.identity(config, { rootDir });
|
|
49
|
+
return {
|
|
50
|
+
driver: plugin.name,
|
|
51
|
+
version: experiment.version,
|
|
52
|
+
config: prepared,
|
|
53
|
+
capabilities: runtime.capabilities,
|
|
54
|
+
executables: await Promise.all(
|
|
55
|
+
runtime.commands.map((command) => cliIdentity(command, rootDir)),
|
|
56
|
+
),
|
|
57
|
+
behavior,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { RubricConfigSchema } from "../assertions/rubric/index.js";
|
|
4
|
+
import { assertDisjointClusters } from "../core/evaluation-clusters.js";
|
|
4
5
|
import {
|
|
5
6
|
bindSubjectRoot,
|
|
6
7
|
type Candidate,
|
|
@@ -11,16 +12,10 @@ import {
|
|
|
11
12
|
} from "../core/index.js";
|
|
12
13
|
import { assertDisjointAnnotationSplits, selectGraderAnnotations } from "./annotation-loader.js";
|
|
13
14
|
import { discoverCases, selectCases } from "./discover.js";
|
|
15
|
+
import { experimentDriverIdentity } from "./driver-identity.js";
|
|
14
16
|
import type { CampaignExecutionOptions, EvaluationCorpus } from "./experiment-context.js";
|
|
15
17
|
import { profileOrThrow } from "./experiment-context.js";
|
|
16
|
-
import {
|
|
17
|
-
claudeConfigRoot,
|
|
18
|
-
claudeUserBehaviorHash,
|
|
19
|
-
cliIdentity,
|
|
20
|
-
configuredClaudeSourceHashes,
|
|
21
|
-
referencedAnswererCli,
|
|
22
|
-
referencedCli,
|
|
23
|
-
} from "./experiment-runtime-identity.js";
|
|
18
|
+
import { cliIdentity, referencedAnswererCli } from "./experiment-runtime-identity.js";
|
|
24
19
|
import { fixtureIdentity } from "./fixture-identity.js";
|
|
25
20
|
import { loadEvalCase } from "./loader.js";
|
|
26
21
|
import { resolveAnswererReference, resolveDriverReference } from "./orchestrator.js";
|
|
@@ -123,8 +118,6 @@ export const agentCorpusHash = async (
|
|
|
123
118
|
.map((evalCase) => [evalCase.path, evalCase]),
|
|
124
119
|
).values(),
|
|
125
120
|
];
|
|
126
|
-
const caseBehaviorHashes = new Map<string, Promise<string | null>>();
|
|
127
|
-
const configuredSourceHashes = new Map<string, Promise<string>>();
|
|
128
121
|
const definitions = await Promise.all(
|
|
129
122
|
selected.map(async (evalCase) => {
|
|
130
123
|
const authored = await loadEvalCase(evalCase.path);
|
|
@@ -142,30 +135,14 @@ export const agentCorpusHash = async (
|
|
|
142
135
|
profile.answerer ?? execution.config.defaultAnswerer,
|
|
143
136
|
harness,
|
|
144
137
|
);
|
|
145
|
-
const driverCli = referencedCli(driver);
|
|
146
138
|
const answererCli = referencedAnswererCli(answerer);
|
|
147
|
-
const configuredRoot =
|
|
148
|
-
driverCli?.name === "claude-code" ? claudeConfigRoot(driver) : null;
|
|
149
|
-
let driverClaudeBehaviorHash: string | null = null;
|
|
150
|
-
if (configuredRoot) {
|
|
151
|
-
let hash = caseBehaviorHashes.get(configuredRoot);
|
|
152
|
-
if (!hash) {
|
|
153
|
-
hash = claudeUserBehaviorHash(configuredRoot);
|
|
154
|
-
caseBehaviorHashes.set(configuredRoot, hash);
|
|
155
|
-
}
|
|
156
|
-
driverClaudeBehaviorHash = await hash;
|
|
157
|
-
}
|
|
158
139
|
return {
|
|
159
140
|
profileId,
|
|
160
141
|
driver,
|
|
161
|
-
|
|
162
|
-
? await cliIdentity(driverCli.command, execution.config.rootDir)
|
|
163
|
-
: null,
|
|
164
|
-
driverClaudeBehaviorHash,
|
|
165
|
-
configuredDriverSources: await configuredClaudeSourceHashes(
|
|
142
|
+
driverIdentity: await experimentDriverIdentity(
|
|
166
143
|
driver,
|
|
144
|
+
execution.registry,
|
|
167
145
|
execution.config.rootDir,
|
|
168
|
-
configuredSourceHashes,
|
|
169
146
|
),
|
|
170
147
|
answerer,
|
|
171
148
|
answererCliIdentity: answererCli
|
|
@@ -303,6 +280,24 @@ export const prepareCorpus = async (
|
|
|
303
280
|
);
|
|
304
281
|
}
|
|
305
282
|
const owners = new Map<string, string>();
|
|
283
|
+
assertDisjointClusters(
|
|
284
|
+
splits.map((split) => ({
|
|
285
|
+
name: split.name,
|
|
286
|
+
items: selectCases(discovery.cases, split.selector),
|
|
287
|
+
})),
|
|
288
|
+
);
|
|
289
|
+
for (const constraint of campaign.objectives.constraints) {
|
|
290
|
+
if (constraint.confidence?.method !== "wilson") continue;
|
|
291
|
+
const selector = splits.find((split) => split.name === constraint.split)?.selector;
|
|
292
|
+
if (
|
|
293
|
+
selector &&
|
|
294
|
+
selectCases(discovery.cases, selector).some((evalCase) => evalCase.cluster !== undefined)
|
|
295
|
+
) {
|
|
296
|
+
throw new Error(
|
|
297
|
+
"authored case clusters produce bounded means; use Hoeffding or percentile-bootstrap confidence instead of Wilson",
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
306
301
|
const caseIds = new Map<string, string>();
|
|
307
302
|
const evidenceOwners = new Map<
|
|
308
303
|
string,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ConfidenceConfigSchema } from "../core/index.js";
|
|
3
|
+
import type { ExperimentRun } from "./experiment-runs.js";
|
|
4
|
+
import { candidateEvaluations } from "./experiment-selection.js";
|
|
5
|
+
import { PairedEffectSchema, pairedEffect } from "./paired-effects.js";
|
|
6
|
+
|
|
7
|
+
export const ExperimentEffectsSchema = z.array(
|
|
8
|
+
PairedEffectSchema.extend({
|
|
9
|
+
candidateId: z.string(),
|
|
10
|
+
profileId: z.string(),
|
|
11
|
+
split: z.enum(["train", "validation", "test"]),
|
|
12
|
+
metric: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const experimentEffects = (
|
|
17
|
+
experiment: ExperimentRun,
|
|
18
|
+
): z.infer<typeof ExperimentEffectsSchema> => {
|
|
19
|
+
const candidateIds =
|
|
20
|
+
experiment.selection?.decisionCandidateIds ??
|
|
21
|
+
experiment.candidates.map((candidate) => candidate.id);
|
|
22
|
+
const baseline = candidateEvaluations(experiment, "baseline");
|
|
23
|
+
const metrics = new Set([
|
|
24
|
+
"quality.passRate",
|
|
25
|
+
experiment.evaluationKind === "grader" ? "harness.total" : "agent.total",
|
|
26
|
+
...(experiment.evaluationKind === "agent" ? ["interrupts.ask", "toolCalls"] : []),
|
|
27
|
+
...experiment.campaignSnapshot.objectives.constraints
|
|
28
|
+
.filter((constraint) => constraint.relativeTo)
|
|
29
|
+
.map((constraint) => constraint.metric),
|
|
30
|
+
]);
|
|
31
|
+
return candidateIds
|
|
32
|
+
.filter((id) => id !== "baseline")
|
|
33
|
+
.flatMap((candidateId) =>
|
|
34
|
+
candidateEvaluations(experiment, candidateId).flatMap((after) =>
|
|
35
|
+
[...metrics].map((metric) => {
|
|
36
|
+
const before = baseline.find(
|
|
37
|
+
(entry) => entry.profileId === after.profileId && entry.split === after.split,
|
|
38
|
+
);
|
|
39
|
+
const constraint = experiment.campaignSnapshot.objectives.constraints.find(
|
|
40
|
+
(entry) =>
|
|
41
|
+
entry.relativeTo &&
|
|
42
|
+
entry.metric === metric &&
|
|
43
|
+
entry.split === after.split &&
|
|
44
|
+
(!entry.profile || entry.profile === after.profileId),
|
|
45
|
+
);
|
|
46
|
+
const confidence =
|
|
47
|
+
constraint?.confidence ??
|
|
48
|
+
ConfidenceConfigSchema.parse({
|
|
49
|
+
method: metric === "quality.passRate" ? "hoeffding" : "percentile-bootstrap",
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
candidateId,
|
|
53
|
+
profileId: after.profileId,
|
|
54
|
+
split: after.split,
|
|
55
|
+
metric,
|
|
56
|
+
...pairedEffect(before, after, metric, confidence),
|
|
57
|
+
};
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
valueHash,
|
|
18
18
|
} from "../core/index.js";
|
|
19
19
|
import { materializeCandidate } from "./candidates.js";
|
|
20
|
+
import { runComparisonIdentity } from "./comparison-identity.js";
|
|
20
21
|
import { readContentBoundJson, writeContentBoundJson } from "./content-bound-json.js";
|
|
21
22
|
import { type discoverCases, selectCases } from "./discover.js";
|
|
22
23
|
import { ensureBudget, throwIfAborted } from "./experiment-budget.js";
|
|
@@ -27,6 +28,7 @@ import { average } from "./experiment-selection.js";
|
|
|
27
28
|
import { evaluateGraderCandidate } from "./grader-experiment.js";
|
|
28
29
|
import { loadEvalCase } from "./loader.js";
|
|
29
30
|
import { Orchestrator, type SuiteCaseResult, type SuiteItem } from "./orchestrator.js";
|
|
31
|
+
import { clusterCaseSamples, groupedCaseMetricSamples } from "./paired-effects.js";
|
|
30
32
|
import { loadSuiteSummary, type SuiteSummary } from "./suite-summary.js";
|
|
31
33
|
|
|
32
34
|
type ExperimentSplit = "train" | "validation" | "test";
|
|
@@ -280,8 +282,36 @@ const agentEvaluationFromSuite = (input: {
|
|
|
280
282
|
summary: SuiteSummary;
|
|
281
283
|
results: readonly SuiteCaseResult[];
|
|
282
284
|
suiteDir: string;
|
|
285
|
+
evaluationHash: string | undefined;
|
|
286
|
+
clusterId: string | undefined;
|
|
283
287
|
}): CandidateEvaluation => {
|
|
284
288
|
const { candidate, profileId, split, summary, results, suiteDir } = input;
|
|
289
|
+
const caseSamples = clusterCaseSamples(
|
|
290
|
+
summary.samples.map((sample) => {
|
|
291
|
+
const metadata = results.find((result) => result.caseId === sample.caseId)?.result?.metadata;
|
|
292
|
+
// Validated assertion configs contain a fresh candidate workspace path.
|
|
293
|
+
// The campaign identity binds the authored evaluator without that incidental path.
|
|
294
|
+
const identityHash =
|
|
295
|
+
metadata?.provenance && input.evaluationHash
|
|
296
|
+
? runComparisonIdentity({
|
|
297
|
+
...metadata,
|
|
298
|
+
provenance: { ...metadata.provenance, evaluatorConfigHash: input.evaluationHash },
|
|
299
|
+
})
|
|
300
|
+
: null;
|
|
301
|
+
return {
|
|
302
|
+
[sample.canonicalCaseId]: {
|
|
303
|
+
identityHash,
|
|
304
|
+
...(input.clusterId ? { clusterId: input.clusterId } : {}),
|
|
305
|
+
metrics: Object.fromEntries(
|
|
306
|
+
Object.entries({
|
|
307
|
+
...sample.metrics,
|
|
308
|
+
"quality.passRate": sample.passed === true ? 1 : 0,
|
|
309
|
+
}).map(([name, value]) => [name, { mean: value, n: 1 }]),
|
|
310
|
+
),
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
285
315
|
const modelAccounting = results.map((result) =>
|
|
286
316
|
modelAccountingFromMetadata(result.result?.metadata ?? null),
|
|
287
317
|
);
|
|
@@ -296,6 +326,7 @@ const agentEvaluationFromSuite = (input: {
|
|
|
296
326
|
metrics: evaluationMetrics(summary, candidate),
|
|
297
327
|
metricDefinitions: evaluationMetricDefinitions(summary),
|
|
298
328
|
samples: evaluationSamples(summary),
|
|
329
|
+
...(caseSamples ? { caseSamples } : {}),
|
|
299
330
|
metricCalls: summary.samples.length,
|
|
300
331
|
judgeCalls: results.reduce(
|
|
301
332
|
(total, result) =>
|
|
@@ -362,7 +393,7 @@ const aggregateAgentUnits = (
|
|
|
362
393
|
caseValues.set(evaluatedCaseId, values);
|
|
363
394
|
}
|
|
364
395
|
}
|
|
365
|
-
const
|
|
396
|
+
const ungroupedSamples = Object.fromEntries(
|
|
366
397
|
[...samplesByCase].map(([name, byCase]) => [
|
|
367
398
|
name,
|
|
368
399
|
[...byCase.entries()]
|
|
@@ -374,6 +405,9 @@ const aggregateAgentUnits = (
|
|
|
374
405
|
{},
|
|
375
406
|
...units.map(({ evaluation }) => evaluation.metricDefinitions ?? {}),
|
|
376
407
|
);
|
|
408
|
+
const caseSamples = clusterCaseSamples(units.map((unit) => unit.evaluation.caseSamples));
|
|
409
|
+
const grouped = groupedCaseMetricSamples(caseSamples, repetitions);
|
|
410
|
+
const samples = grouped ?? ungroupedSamples;
|
|
377
411
|
return {
|
|
378
412
|
candidateId: candidate.id,
|
|
379
413
|
profileId,
|
|
@@ -384,13 +418,16 @@ const aggregateAgentUnits = (
|
|
|
384
418
|
[...caseValues].map(([caseId, values]) => [caseId, average(values)]),
|
|
385
419
|
),
|
|
386
420
|
metrics: Object.fromEntries(
|
|
387
|
-
[...metricValues]
|
|
388
|
-
name,
|
|
389
|
-
name
|
|
390
|
-
|
|
421
|
+
[...metricValues]
|
|
422
|
+
.filter(([name]) => !grouped || name === "subject.bytes" || Object.hasOwn(grouped, name))
|
|
423
|
+
.map(([name, values]) => [
|
|
424
|
+
name,
|
|
425
|
+
name === "subject.bytes" ? candidate.sourceBytes : average(grouped?.[name] ?? values),
|
|
426
|
+
]),
|
|
391
427
|
),
|
|
392
428
|
metricDefinitions,
|
|
393
429
|
samples,
|
|
430
|
+
...(caseSamples ? { caseSamples } : {}),
|
|
394
431
|
metricCalls: units.reduce((total, unit) => total + (unit.evaluation.metricCalls ?? 0), 0),
|
|
395
432
|
judgeCalls: units.reduce((total, unit) => total + (unit.evaluation.judgeCalls ?? 0), 0),
|
|
396
433
|
unpricedModelCalls: units.reduce(
|
|
@@ -461,6 +498,7 @@ export const evaluateAgentCandidate = async (options: {
|
|
|
461
498
|
const defaultDriver = profile.driver ?? execution.config.defaultDriver;
|
|
462
499
|
const registry = execution.registry.withAssertion(createRubricAssertion(harness));
|
|
463
500
|
const orchestrator = new Orchestrator({
|
|
501
|
+
environment: profile.environment ?? execution.config.environment,
|
|
464
502
|
registry,
|
|
465
503
|
defaultAnswerer: profile.answerer ?? execution.config.defaultAnswerer,
|
|
466
504
|
...(defaultDriver ? { defaultDriver } : {}),
|
|
@@ -629,6 +667,8 @@ export const evaluateAgentCandidate = async (options: {
|
|
|
629
667
|
summary,
|
|
630
668
|
results: suite.results,
|
|
631
669
|
suiteDir: suite.suiteDir,
|
|
670
|
+
evaluationHash: experiment.manifest.evaluationHash,
|
|
671
|
+
clusterId: evalCase.cluster,
|
|
632
672
|
});
|
|
633
673
|
} finally {
|
|
634
674
|
await rm(unitRoot, { recursive: true, force: true });
|