@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
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
TurnUsageSchema,
|
|
20
20
|
totalTokens,
|
|
21
21
|
} from "../../core/index.js";
|
|
22
|
+
import { hasAgentUsage } from "../../core/usage.js";
|
|
22
23
|
import type { CaseObserver, SuiteCaseResult, SuiteObserver } from "../../orchestrator/index.js";
|
|
23
24
|
import {
|
|
24
25
|
formatAlignedUsage,
|
|
@@ -80,6 +81,7 @@ type CaseView = {
|
|
|
80
81
|
assertions: GradingAssertionEntry[];
|
|
81
82
|
skippedAssertions: NonNullable<GradingReport["skippedAssertions"]>;
|
|
82
83
|
usage: RunUsage;
|
|
84
|
+
agentUsageReported: boolean;
|
|
83
85
|
/** Per-API-call agent usage, in order — the split behind `usage.agent`. */
|
|
84
86
|
agentTurns: TurnUsage[];
|
|
85
87
|
phaseDurationMs: PhaseDurations | null;
|
|
@@ -159,6 +161,7 @@ const cell = (text: string, width: number): string => {
|
|
|
159
161
|
*/
|
|
160
162
|
const scoreNote = (entry: GradingAssertionEntry): string => {
|
|
161
163
|
if (entry.errorMessage) return entry.errorMessage;
|
|
164
|
+
if (entry.abstention) return "insufficient evidence";
|
|
162
165
|
if (entry.passed) return "";
|
|
163
166
|
const score = formatScore(entry.score);
|
|
164
167
|
if (entry.threshold > 0) return `score ${score} < ${formatScore(entry.threshold)}`;
|
|
@@ -240,6 +243,7 @@ export class ConsoleReporter implements SuiteObserver {
|
|
|
240
243
|
assertions: [],
|
|
241
244
|
skippedAssertions: [],
|
|
242
245
|
usage: emptyRunUsage(),
|
|
246
|
+
agentUsageReported: false,
|
|
243
247
|
agentTurns: [],
|
|
244
248
|
phaseDurationMs: null,
|
|
245
249
|
runDir: null,
|
|
@@ -273,6 +277,13 @@ export class ConsoleReporter implements SuiteObserver {
|
|
|
273
277
|
view.toolUnresolved = tools.unresolved;
|
|
274
278
|
view.toolUnmatchedResults = tools.unmatchedResults;
|
|
275
279
|
view.toolMalformed = tools.malformedEvents;
|
|
280
|
+
} else if (event.type === "progress" && event.kind === "driver-stderr" && this.verbose) {
|
|
281
|
+
const payload = event.payload as { text?: unknown; truncated?: unknown } | null;
|
|
282
|
+
if (payload && typeof payload.text === "string") {
|
|
283
|
+
this.region.staticLine(this.theme.dim(` [${view.caseId}] ${payload.text}`));
|
|
284
|
+
if (payload.truncated === true)
|
|
285
|
+
this.region.staticLine(this.theme.dim(" [driver stderr truncated]"));
|
|
286
|
+
}
|
|
276
287
|
} else if (event.type === "progress" && event.kind === "usage") {
|
|
277
288
|
const turn = TurnUsageSchema.safeParse(event.payload).data;
|
|
278
289
|
if (turn) view.agentTurns.push(turn);
|
|
@@ -307,6 +318,7 @@ export class ConsoleReporter implements SuiteObserver {
|
|
|
307
318
|
view.assertions = result.result.grading.assertions;
|
|
308
319
|
view.skippedAssertions = result.result.grading.skippedAssertions ?? [];
|
|
309
320
|
view.usage = result.result.metadata.usage;
|
|
321
|
+
view.agentUsageReported = hasAgentUsage(result.result.metadata);
|
|
310
322
|
view.phaseDurationMs = result.result.metadata.phaseDurationMs ?? null;
|
|
311
323
|
const tools = result.result.metadata.toolOutcomes;
|
|
312
324
|
if (tools) {
|
|
@@ -451,10 +463,13 @@ export class ConsoleReporter implements SuiteObserver {
|
|
|
451
463
|
Math.max(NAME_MIN, ...view.assertions.map((a) => a.name.length)),
|
|
452
464
|
);
|
|
453
465
|
for (const entry of view.assertions) {
|
|
454
|
-
|
|
466
|
+
let mark = entry.passed ? this.theme.green("✓") : this.theme.red("✗");
|
|
467
|
+
if (entry.abstention) mark = this.theme.dim("?");
|
|
455
468
|
const duration = formatDuration(entry.durationMs).padStart(DURATION_COL);
|
|
456
469
|
const note = scoreNote(entry);
|
|
457
|
-
const suffix = note
|
|
470
|
+
const suffix = note
|
|
471
|
+
? ` ${entry.abstention ? this.theme.dim(note) : this.theme.red(note)}`
|
|
472
|
+
: "";
|
|
458
473
|
nodes.push({
|
|
459
474
|
head: `${mark} ${cell(entry.name, width)} ${this.theme.dim(duration)}${suffix}`,
|
|
460
475
|
body: !entry.passed && entry.comment ? [this.theme.dim(entry.comment)] : [],
|
|
@@ -630,11 +645,17 @@ export class ConsoleReporter implements SuiteObserver {
|
|
|
630
645
|
emptyRunUsage(),
|
|
631
646
|
);
|
|
632
647
|
const harness = harnessUsage(total);
|
|
633
|
-
|
|
648
|
+
const reported = this.cases.filter(
|
|
649
|
+
(view) => view.agentUsageReported || !isUsageEmpty(this.agentUsageOf(view)),
|
|
650
|
+
).length;
|
|
651
|
+
if (reported === 0 && isUsageEmpty(total.agent) && isUsageEmpty(harness)) {
|
|
634
652
|
return live ? [] : [` ${label("Tokens")}${this.theme.dim("none reported")}`];
|
|
635
653
|
}
|
|
636
654
|
|
|
637
|
-
const
|
|
655
|
+
const missing = this.cases.length - reported;
|
|
656
|
+
const partial = missing > 0 ? ` (${missing} run${missing === 1 ? "" : "s"} unavailable)` : "";
|
|
657
|
+
const agentText =
|
|
658
|
+
reported === 0 ? this.theme.dim("unavailable") : `${formatUsage(total.agent)}${partial}`;
|
|
638
659
|
if (live) {
|
|
639
660
|
const harnessText = isUsageEmpty(harness)
|
|
640
661
|
? ""
|
package/src/cli/run-options.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Errors } from "incur";
|
|
2
2
|
|
|
3
3
|
export type RunOptions = {
|
|
4
4
|
tag?: string[];
|
|
@@ -15,22 +15,14 @@ export type RunOptions = {
|
|
|
15
15
|
verbose?: boolean;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export const collectTags = (value: string, previous: string[]): string[] =>
|
|
19
|
-
previous.concat(
|
|
20
|
-
value
|
|
21
|
-
.split(",")
|
|
22
|
-
.map((tag) => tag.trim())
|
|
23
|
-
.filter(Boolean),
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
export const collectValues = (value: string, previous: string[]): string[] => [...previous, value];
|
|
27
|
-
|
|
28
18
|
export const parseAnnotationAssertion = (
|
|
29
19
|
value: string,
|
|
30
20
|
): [string, { passed: boolean; score?: number }] => {
|
|
31
21
|
const match = /^(.+)=(pass|fail)(?::(.+))?$/.exec(value);
|
|
32
|
-
if (!match?.[1] || !match[2]) {
|
|
33
|
-
throw new
|
|
22
|
+
if (!match?.[1]?.trim() || !match[2]) {
|
|
23
|
+
throw new Errors.ParseError({
|
|
24
|
+
message: "--assertion must use id=pass, id=fail, or id=pass:0.8 syntax",
|
|
25
|
+
});
|
|
34
26
|
}
|
|
35
27
|
const score = match[3] === undefined ? undefined : parseUnitInterval(match[3]);
|
|
36
28
|
return [
|
|
@@ -42,19 +34,10 @@ export const parseAnnotationAssertion = (
|
|
|
42
34
|
];
|
|
43
35
|
};
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
// Number() rejects partial numbers such as "3abc" and "2.9".
|
|
47
|
-
const parsed = Number(value);
|
|
48
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
49
|
-
throw new InvalidArgumentError("must be a positive integer");
|
|
50
|
-
}
|
|
51
|
-
return parsed;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export const parseUnitInterval = (value: string): number => {
|
|
37
|
+
const parseUnitInterval = (value: string): number => {
|
|
55
38
|
const parsed = Number(value);
|
|
56
|
-
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
|
|
57
|
-
throw new
|
|
39
|
+
if (!value.trim() || !Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
|
|
40
|
+
throw new Errors.ParseError({ message: "must be a number between 0 and 1" });
|
|
58
41
|
}
|
|
59
42
|
return parsed;
|
|
60
43
|
};
|
package/src/cli/view.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { Errors } from "incur";
|
|
3
3
|
import {
|
|
4
|
-
formatExperimentRun,
|
|
5
|
-
formatExperimentRunsTable,
|
|
6
|
-
formatRunDetail,
|
|
7
|
-
formatRunsTable,
|
|
8
|
-
formatSuiteRunDetail,
|
|
9
|
-
formatSuiteRunsTable,
|
|
10
4
|
isExperimentRun,
|
|
11
5
|
listExperimentRuns,
|
|
12
6
|
listRuns,
|
|
@@ -29,7 +23,6 @@ export type ViewOptions = {
|
|
|
29
23
|
web?: boolean;
|
|
30
24
|
port?: number;
|
|
31
25
|
open?: boolean;
|
|
32
|
-
json?: boolean;
|
|
33
26
|
};
|
|
34
27
|
|
|
35
28
|
const previewKind = async (
|
|
@@ -46,19 +39,16 @@ const previewKind = async (
|
|
|
46
39
|
return undefined;
|
|
47
40
|
};
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
export async function* webViewCommand(
|
|
50
43
|
runDirArg: string | undefined,
|
|
51
44
|
options: ViewOptions,
|
|
52
|
-
):
|
|
53
|
-
if (options.json) {
|
|
54
|
-
throw new InvalidArgumentError("--web and --json are mutually exclusive");
|
|
55
|
-
}
|
|
45
|
+
): AsyncGenerator<{ url: string; runsRoot: string }, void> {
|
|
56
46
|
const config = await loadLuxConfig(process.cwd());
|
|
57
47
|
const runsRoot = options.runsRoot ? resolve(options.runsRoot) : config.runsRoot;
|
|
58
48
|
const initialPath = runDirArg ? resolve(runDirArg) : undefined;
|
|
59
|
-
if (initialPath && !(await ensureDirectory("view", initialPath)))
|
|
49
|
+
if (initialPath && !(await ensureDirectory("view", initialPath))) failWith(1);
|
|
60
50
|
const initialKind = await previewKind(initialPath, options);
|
|
61
|
-
|
|
51
|
+
using controller = abortOnInterrupt();
|
|
62
52
|
const preview = await startPreviewServer({
|
|
63
53
|
runsRoot,
|
|
64
54
|
...(options.port !== undefined ? { port: options.port } : {}),
|
|
@@ -67,30 +57,29 @@ const webViewCommand = async (
|
|
|
67
57
|
...(options.case ? { caseFilter: options.case } : {}),
|
|
68
58
|
signal: controller.signal,
|
|
69
59
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
try {
|
|
61
|
+
yield { url: preview.url, runsRoot };
|
|
62
|
+
if (options.open !== false) {
|
|
63
|
+
try {
|
|
64
|
+
await openPreviewUrl(preview.url);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
process.stderr.write(`lux view: could not open a browser (${message})\n`);
|
|
68
|
+
}
|
|
78
69
|
}
|
|
70
|
+
await preview.closed;
|
|
71
|
+
if (controller.signal.aborted) failWith(SIGINT_EXIT_CODE);
|
|
72
|
+
} finally {
|
|
73
|
+
await preview.close();
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
if (controller.signal.aborted) failWith(SIGINT_EXIT_CODE);
|
|
82
|
-
};
|
|
75
|
+
}
|
|
83
76
|
|
|
84
77
|
export const viewCommand = async (
|
|
85
78
|
runDirArg: string | undefined,
|
|
86
79
|
options: ViewOptions,
|
|
87
|
-
): Promise<
|
|
88
|
-
if (options.web) {
|
|
89
|
-
await webViewCommand(runDirArg, options);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
80
|
+
): Promise<unknown> => {
|
|
92
81
|
if (options.port !== undefined || options.open === false) {
|
|
93
|
-
throw new
|
|
82
|
+
throw new Errors.ParseError({ message: "--port and --no-open require --web" });
|
|
94
83
|
}
|
|
95
84
|
if (runDirArg) {
|
|
96
85
|
const path = resolve(runDirArg);
|
|
@@ -98,71 +87,43 @@ export const viewCommand = async (
|
|
|
98
87
|
if (isExperimentRun(path)) {
|
|
99
88
|
const experiment = await loadExperimentRun(path);
|
|
100
89
|
if (!experiment) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
throw new Errors.IncurError({
|
|
91
|
+
code: "INVALID_EXPERIMENT",
|
|
92
|
+
message: `Invalid experiment at ${path}.`,
|
|
93
|
+
exitCode: 1,
|
|
94
|
+
});
|
|
104
95
|
}
|
|
105
|
-
|
|
106
|
-
options.json
|
|
107
|
-
? `${JSON.stringify({ experimentDir: path, ...experiment }, null, 2)}\n`
|
|
108
|
-
: `${formatExperimentRun(path, experiment)}\n`,
|
|
109
|
-
);
|
|
110
|
-
return;
|
|
96
|
+
return { experimentDir: path, ...experiment };
|
|
111
97
|
}
|
|
112
98
|
const suite = await loadSuiteRun(path);
|
|
113
99
|
if (suite) {
|
|
114
|
-
|
|
115
|
-
options.json
|
|
116
|
-
? `${JSON.stringify({ suiteDir: path, ...suite }, null, 2)}\n`
|
|
117
|
-
: `${formatSuiteRunDetail(path, suite)}\n`,
|
|
118
|
-
);
|
|
119
|
-
return;
|
|
100
|
+
return { suiteDir: path, ...suite };
|
|
120
101
|
}
|
|
121
|
-
|
|
122
|
-
process.stdout.write(
|
|
123
|
-
options.json ? `${JSON.stringify(detail, null, 2)}\n` : `${formatRunDetail(detail)}\n`,
|
|
124
|
-
);
|
|
125
|
-
return;
|
|
102
|
+
return loadRunDetail(path);
|
|
126
103
|
}
|
|
127
104
|
|
|
128
105
|
const config = await loadLuxConfig(process.cwd());
|
|
129
106
|
const runsRoot = options.runsRoot ? resolve(options.runsRoot) : config.runsRoot;
|
|
107
|
+
const readErrors: Array<{ path: string; message: string }> = [];
|
|
108
|
+
const listingOptions = {
|
|
109
|
+
onReadError: (path: string, error: unknown) => {
|
|
110
|
+
readErrors.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
111
|
+
},
|
|
112
|
+
};
|
|
130
113
|
if (options.experiments) {
|
|
131
|
-
const experiments = await listExperimentRuns(runsRoot);
|
|
132
|
-
|
|
133
|
-
options.json
|
|
134
|
-
? `${JSON.stringify(experiments, null, 2)}\n`
|
|
135
|
-
: `experiments at ${runsRoot}:\n${formatExperimentRunsTable(experiments)}\n`,
|
|
136
|
-
);
|
|
137
|
-
return;
|
|
114
|
+
const experiments = await listExperimentRuns(runsRoot, listingOptions);
|
|
115
|
+
return { runsRoot, experiments, ...(readErrors.length ? { readErrors } : {}) };
|
|
138
116
|
}
|
|
139
117
|
if (options.suites) {
|
|
140
|
-
const suites = await listSuiteRuns(runsRoot);
|
|
141
|
-
|
|
142
|
-
options.json
|
|
143
|
-
? `${JSON.stringify(suites, null, 2)}\n`
|
|
144
|
-
: `suite runs at ${runsRoot}:\n${formatSuiteRunsTable(suites)}\n`,
|
|
145
|
-
);
|
|
146
|
-
return;
|
|
118
|
+
const suites = await listSuiteRuns(runsRoot, listingOptions);
|
|
119
|
+
return { runsRoot, suites, ...(readErrors.length ? { readErrors } : {}) };
|
|
147
120
|
}
|
|
148
121
|
const all = await listRuns(runsRoot);
|
|
149
122
|
const runs = options.case ? all.filter((run) => run.caseSlug === options.case) : all;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
process.stdout.write(`${formatSuiteRunsTable(await listSuiteRuns(runsRoot))}\n\n`);
|
|
157
|
-
}
|
|
158
|
-
process.stdout.write(`runs at ${runsRoot}:\n`);
|
|
159
|
-
process.stdout.write(`${formatRunsTable(runs)}\n`);
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export const parsePreviewPort = (value: string): number => {
|
|
163
|
-
const port = Number(value);
|
|
164
|
-
if (!Number.isInteger(port) || port < 0 || port > 65_535) {
|
|
165
|
-
throw new InvalidArgumentError("port must be an integer from 0 to 65535");
|
|
166
|
-
}
|
|
167
|
-
return port;
|
|
123
|
+
return {
|
|
124
|
+
runsRoot,
|
|
125
|
+
...(!options.case ? { suites: await listSuiteRuns(runsRoot, listingOptions) } : {}),
|
|
126
|
+
runs,
|
|
127
|
+
...(readErrors.length ? { readErrors } : {}),
|
|
128
|
+
};
|
|
168
129
|
};
|
package/src/core/annotation.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { EvaluationMetadataSchema } from "./evaluation-clusters.js";
|
|
2
3
|
import { formatZodIssues } from "./zod.js";
|
|
3
4
|
|
|
4
5
|
const ExpectedBooleanLabelSchema = z
|
|
@@ -35,7 +36,7 @@ export const GraderAnnotationSchema = z
|
|
|
35
36
|
reviewer: z.string().min(1).optional(),
|
|
36
37
|
/** Human rationale supplied to the rubric optimizer on mismatches. */
|
|
37
38
|
feedback: z.string().optional(),
|
|
38
|
-
meta:
|
|
39
|
+
meta: EvaluationMetadataSchema.optional(),
|
|
39
40
|
})
|
|
40
41
|
.strict()
|
|
41
42
|
.superRefine((annotation, ctx) => {
|
package/src/core/answerer.ts
CHANGED
|
@@ -73,3 +73,12 @@ export type AnswererPlugin<Cfg = unknown> = {
|
|
|
73
73
|
configSchema?: z.ZodTypeAny;
|
|
74
74
|
create: (config: Cfg) => Answerer | Promise<Answerer>;
|
|
75
75
|
};
|
|
76
|
+
|
|
77
|
+
export const defineAnswerer = <S extends z.ZodType>(
|
|
78
|
+
definition: Omit<AnswererPlugin<z.output<S>>, "configSchema"> & { configSchema: S },
|
|
79
|
+
): Omit<AnswererPlugin<z.output<S>>, "configSchema"> & { configSchema: S } => definition;
|
|
80
|
+
|
|
81
|
+
export const answererRef = <S extends z.ZodType>(
|
|
82
|
+
plugin: { name: string; configSchema: S },
|
|
83
|
+
config: z.input<S>,
|
|
84
|
+
): { name: string; config: z.input<S> } => ({ name: plugin.name, config });
|
package/src/core/assertion.ts
CHANGED
|
@@ -9,6 +9,8 @@ export const AssertionResultSchema = z.object({
|
|
|
9
9
|
score: z.number().min(0).max(1),
|
|
10
10
|
comment: z.exactOptional(z.string()),
|
|
11
11
|
detail: z.unknown().optional(),
|
|
12
|
+
/** The evaluator ran successfully but lacked evidence for a verdict. */
|
|
13
|
+
abstention: z.exactOptional(z.literal("insufficient-evidence")),
|
|
12
14
|
/** Present when the assertion could not produce a judgment. */
|
|
13
15
|
errorMessage: z.exactOptional(z.string()),
|
|
14
16
|
/** Tokens the assertion itself spent. Only LLM-judge assertions report this. */
|
|
@@ -143,6 +145,7 @@ export const GradingAssertionEntrySchema = z.object({
|
|
|
143
145
|
durationMs: z.number(),
|
|
144
146
|
comment: z.exactOptional(z.string()),
|
|
145
147
|
detail: z.unknown().optional(),
|
|
148
|
+
abstention: z.exactOptional(z.literal("insufficient-evidence")),
|
|
146
149
|
errorMessage: z.exactOptional(z.string()),
|
|
147
150
|
usage: z.exactOptional(TokenUsageSchema),
|
|
148
151
|
});
|
package/src/core/case.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { AssertionPlugin } from "./assertion.js";
|
|
3
3
|
import { collectEvalCaseExtensions, setEvalCaseExtensions } from "./case-authoring-state.js";
|
|
4
|
+
import { DriverCapabilitySchema } from "./driver.js";
|
|
5
|
+
import { EvaluationMetadataSchema } from "./evaluation-clusters.js";
|
|
4
6
|
import type { LifecycleHooksPlugin } from "./lifecycle-hooks.js";
|
|
5
7
|
import { formatZodIssues } from "./zod.js";
|
|
6
8
|
|
|
@@ -46,8 +48,9 @@ const EvalCaseObjectSchema = z.object({
|
|
|
46
48
|
driver: DriverRefSchema.optional(),
|
|
47
49
|
answerer: AnswererRefSchema.optional(),
|
|
48
50
|
fixtures: FixturesRefSchema.optional(),
|
|
51
|
+
requires: z.array(DriverCapabilitySchema).optional(),
|
|
49
52
|
assertions: z.array(AssertionSchema).optional(),
|
|
50
|
-
meta:
|
|
53
|
+
meta: EvaluationMetadataSchema.optional(),
|
|
51
54
|
});
|
|
52
55
|
|
|
53
56
|
export const EvalCaseSchema = EvalCaseObjectSchema
|
|
@@ -149,6 +152,7 @@ export const PersistedCaseSchema = EvalCaseObjectSchema.partial()
|
|
|
149
152
|
driver: PersistedDriverRefSchema.optional(),
|
|
150
153
|
answerer: PersistedAnswererRefSchema.optional(),
|
|
151
154
|
fixtures: PersistedFixturesRefSchema.optional(),
|
|
155
|
+
meta: z.record(z.string(), z.unknown()).optional(),
|
|
152
156
|
})
|
|
153
157
|
.loose();
|
|
154
158
|
export type PersistedCase = z.infer<typeof PersistedCaseSchema>;
|
package/src/core/driver.ts
CHANGED
|
@@ -71,8 +71,46 @@ export interface AgentDriver {
|
|
|
71
71
|
close(): Promise<void>;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
export const DriverCapabilitySchema = z.enum([
|
|
75
|
+
"interactive",
|
|
76
|
+
"tool-calls",
|
|
77
|
+
"tool-results",
|
|
78
|
+
"artifacts",
|
|
79
|
+
"subagents",
|
|
80
|
+
"usage",
|
|
81
|
+
"cost",
|
|
82
|
+
"isolation",
|
|
83
|
+
]);
|
|
84
|
+
export type DriverCapability = z.infer<typeof DriverCapabilitySchema>;
|
|
85
|
+
|
|
86
|
+
export type DriverRuntime = {
|
|
87
|
+
commands: string[];
|
|
88
|
+
/** Missing keys mean unknown; false means the adapter cannot provide the capability. */
|
|
89
|
+
capabilities: Partial<Record<DriverCapability, boolean>>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type DriverExperiment<Cfg> = {
|
|
93
|
+
version: string;
|
|
94
|
+
/** Bind a configuration to the experiment environment; Lux validates the returned config. */
|
|
95
|
+
prepare(config: Cfg): unknown;
|
|
96
|
+
/** Fingerprint behavior outside authored cases/configuration and the reported executables. */
|
|
97
|
+
identity(config: Cfg, context: { rootDir: string }): unknown | Promise<unknown>;
|
|
98
|
+
};
|
|
99
|
+
|
|
74
100
|
export type DriverPlugin<Cfg = unknown> = {
|
|
75
101
|
name: string;
|
|
76
102
|
configSchema?: z.ZodTypeAny;
|
|
77
103
|
create: (config: Cfg) => AgentDriver | Promise<AgentDriver>;
|
|
104
|
+
runtime?: (config: Cfg) => DriverRuntime;
|
|
105
|
+
/** Ordinary runs remain supported when controlled experiments are not declared. */
|
|
106
|
+
experiment?: DriverExperiment<Cfg>;
|
|
78
107
|
};
|
|
108
|
+
|
|
109
|
+
export const defineDriver = <S extends z.ZodType>(
|
|
110
|
+
definition: Omit<DriverPlugin<z.output<S>>, "configSchema"> & { configSchema: S },
|
|
111
|
+
): Omit<DriverPlugin<z.output<S>>, "configSchema"> & { configSchema: S } => definition;
|
|
112
|
+
|
|
113
|
+
export const driverRef = <S extends z.ZodType>(
|
|
114
|
+
plugin: { name: string; configSchema: S },
|
|
115
|
+
config: z.input<S>,
|
|
116
|
+
): { name: string; config: z.input<S> } => ({ name: plugin.name, config });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Author-declared execution conditions. Lux records these without enforcing or verifying them. */
|
|
4
|
+
export const EnvironmentDeclarationSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
imageDigest: z
|
|
7
|
+
.string()
|
|
8
|
+
.regex(/^(?:[^@\s]+@)?sha256:[a-f0-9]{64}$/)
|
|
9
|
+
.optional(),
|
|
10
|
+
toolchains: z.record(z.string().min(1), z.string().min(1)).optional(),
|
|
11
|
+
network: z.enum(["disabled", "restricted", "unrestricted"]).optional(),
|
|
12
|
+
resources: z
|
|
13
|
+
.object({
|
|
14
|
+
cpuCount: z.number().positive().optional(),
|
|
15
|
+
memoryBytes: z.number().int().positive().optional(),
|
|
16
|
+
diskBytes: z.number().int().positive().optional(),
|
|
17
|
+
})
|
|
18
|
+
.strict()
|
|
19
|
+
.optional(),
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
export type EnvironmentDeclaration = z.infer<typeof EnvironmentDeclarationSchema>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const EvaluationMetadataSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
/** Related perturbations share one independent statistical unit. */
|
|
6
|
+
cluster: z.string().trim().min(1).optional(),
|
|
7
|
+
})
|
|
8
|
+
.catchall(z.unknown());
|
|
9
|
+
|
|
10
|
+
export const clusterKey = (id: string, cluster: string | undefined): string =>
|
|
11
|
+
JSON.stringify(cluster === undefined ? ["case", id] : ["cluster", cluster]);
|
|
12
|
+
|
|
13
|
+
export const independentClusterCount = (
|
|
14
|
+
items: readonly { id: string; cluster?: string | undefined }[],
|
|
15
|
+
): number => new Set(items.map((item) => clusterKey(item.id, item.cluster))).size;
|
|
16
|
+
|
|
17
|
+
export const assertDisjointClusters = (
|
|
18
|
+
splits: readonly {
|
|
19
|
+
name: string;
|
|
20
|
+
items: readonly { id: string; cluster?: string | undefined }[];
|
|
21
|
+
}[],
|
|
22
|
+
): void => {
|
|
23
|
+
const owners = new Map<string, string>();
|
|
24
|
+
for (const split of splits) {
|
|
25
|
+
for (const item of split.items) {
|
|
26
|
+
if (item.cluster === undefined) continue;
|
|
27
|
+
const owner = owners.get(item.cluster);
|
|
28
|
+
if (owner !== undefined && owner !== split.name)
|
|
29
|
+
throw new Error(
|
|
30
|
+
`evaluation cluster '${item.cluster}' appears in both ${owner} and ${split.name} splits`,
|
|
31
|
+
);
|
|
32
|
+
owners.set(item.cluster, split.name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
package/src/core/experiment.ts
CHANGED
|
@@ -84,6 +84,8 @@ export type ExperimentVariant = z.output<typeof ExperimentVariantSchema>;
|
|
|
84
84
|
export const ExperimentConstraintSchema = z
|
|
85
85
|
.object({
|
|
86
86
|
metric: z.string().min(1),
|
|
87
|
+
/** Bounds apply to paired candidate-minus-baseline case means. */
|
|
88
|
+
relativeTo: z.exactOptional(z.literal("baseline")),
|
|
87
89
|
min: z.number().optional(),
|
|
88
90
|
max: z.number().optional(),
|
|
89
91
|
profile: z.string().optional(),
|
|
@@ -151,6 +153,47 @@ const DEFAULT_EXPERIMENT_BUDGET: ExperimentBudget = {
|
|
|
151
153
|
maxWallTimeMs: 3_600_000,
|
|
152
154
|
};
|
|
153
155
|
|
|
156
|
+
const validateConstraintConfidence = (
|
|
157
|
+
constraint: ExperimentConstraint,
|
|
158
|
+
index: number,
|
|
159
|
+
boundedMetrics: Set<string>,
|
|
160
|
+
ctx: z.RefinementCtx,
|
|
161
|
+
): void => {
|
|
162
|
+
const issue = (message: string): void =>
|
|
163
|
+
ctx.addIssue({ code: "custom", path: ["objectives", "constraints", index], message });
|
|
164
|
+
if (constraint.relativeTo && constraint.confidence?.method === "wilson") {
|
|
165
|
+
issue(
|
|
166
|
+
"paired differences are not Bernoulli observations; use Hoeffding or percentile-bootstrap confidence",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
constraint.relativeTo &&
|
|
171
|
+
["subject.bytes", "quality.caseRegressions"].includes(constraint.metric)
|
|
172
|
+
) {
|
|
173
|
+
issue("relative constraints require a metric with per-case observations");
|
|
174
|
+
}
|
|
175
|
+
const bounded = boundedMetrics.has(constraint.metric);
|
|
176
|
+
if (
|
|
177
|
+
(constraint.confidence?.method === "hoeffding" || constraint.confidence?.method === "wilson") &&
|
|
178
|
+
!bounded
|
|
179
|
+
) {
|
|
180
|
+
issue(
|
|
181
|
+
`metric '${constraint.metric}' has no declared [0,1] support; use percentile-bootstrap confidence`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const minimum = constraint.relativeTo ? -1 : 0;
|
|
185
|
+
if (
|
|
186
|
+
bounded &&
|
|
187
|
+
[constraint.min, constraint.max].some(
|
|
188
|
+
(bound) => bound !== undefined && (bound < minimum || bound > 1),
|
|
189
|
+
)
|
|
190
|
+
) {
|
|
191
|
+
issue(
|
|
192
|
+
`metric '${constraint.metric}' has support [${minimum},1]; constraint bounds must stay in that interval`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
154
197
|
export const ExperimentCampaignSchema = z
|
|
155
198
|
.object({
|
|
156
199
|
id: z.string().min(1),
|
|
@@ -215,6 +258,8 @@ export const ExperimentCampaignSchema = z
|
|
|
215
258
|
"grader.macroFalseNegativeRate",
|
|
216
259
|
"grader.coverage",
|
|
217
260
|
"grader.abstentionRate",
|
|
261
|
+
"grader.insufficientEvidenceRate",
|
|
262
|
+
"grader.operationalErrorRate",
|
|
218
263
|
"grader.scoreMae",
|
|
219
264
|
]);
|
|
220
265
|
const unitIntervalMetrics = graderConfidenceMetrics;
|
|
@@ -289,29 +334,7 @@ export const ExperimentCampaignSchema = z
|
|
|
289
334
|
profiles.add(profile);
|
|
290
335
|
}
|
|
291
336
|
for (const [index, constraint] of campaign.objectives.constraints.entries()) {
|
|
292
|
-
|
|
293
|
-
(constraint.confidence?.method === "hoeffding" ||
|
|
294
|
-
constraint.confidence?.method === "wilson") &&
|
|
295
|
-
!unitIntervalMetrics.has(constraint.metric)
|
|
296
|
-
) {
|
|
297
|
-
ctx.addIssue({
|
|
298
|
-
code: "custom",
|
|
299
|
-
path: ["objectives", "constraints", index, "confidence", "method"],
|
|
300
|
-
message: `metric '${constraint.metric}' has no declared [0,1] support; use percentile-bootstrap confidence`,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
if (
|
|
304
|
-
unitIntervalMetrics.has(constraint.metric) &&
|
|
305
|
-
[constraint.min, constraint.max].some(
|
|
306
|
-
(bound) => bound !== undefined && (bound < 0 || bound > 1),
|
|
307
|
-
)
|
|
308
|
-
) {
|
|
309
|
-
ctx.addIssue({
|
|
310
|
-
code: "custom",
|
|
311
|
-
path: ["objectives", "constraints", index],
|
|
312
|
-
message: `metric '${constraint.metric}' has support [0,1]; constraint bounds must stay in that interval`,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
337
|
+
validateConstraintConfidence(constraint, index, unitIntervalMetrics, ctx);
|
|
315
338
|
if (constraint.profile && !profiles.has(constraint.profile)) {
|
|
316
339
|
ctx.addIssue({
|
|
317
340
|
code: "custom",
|
|
@@ -424,6 +447,20 @@ export const CandidateEvaluationSchema = z.object({
|
|
|
424
447
|
costUsd: z.exactOptional(z.number().nonnegative()),
|
|
425
448
|
/** Raw observations used by confidence-aware constraints. */
|
|
426
449
|
samples: z.exactOptional(z.record(z.string(), z.array(z.number()))),
|
|
450
|
+
/** Independent cases with repetition coverage retained for paired comparisons. */
|
|
451
|
+
caseSamples: z.exactOptional(
|
|
452
|
+
z.record(
|
|
453
|
+
z.string(),
|
|
454
|
+
z.object({
|
|
455
|
+
identityHash: z.string().nullable(),
|
|
456
|
+
clusterId: z.exactOptional(z.string().min(1)),
|
|
457
|
+
metrics: z.record(
|
|
458
|
+
z.string(),
|
|
459
|
+
z.object({ mean: z.number().finite(), n: z.number().int().positive() }),
|
|
460
|
+
),
|
|
461
|
+
}),
|
|
462
|
+
),
|
|
463
|
+
),
|
|
427
464
|
failedCases: z.array(
|
|
428
465
|
z.object({
|
|
429
466
|
caseId: z.string(),
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { join, relative, resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Walk authored trees without silently following or omitting a symlink that
|
|
7
|
+
* can change outside the experiment identity. Exact ignored paths support
|
|
8
|
+
* generated state directories whose names are project-configurable.
|
|
9
|
+
*/
|
|
10
|
+
export const walkAuthoredFiles = async (
|
|
11
|
+
root: string,
|
|
12
|
+
ignoredDirectoryNames: ReadonlySet<string> = new Set(),
|
|
13
|
+
ignoredPaths: ReadonlySet<string> = new Set(),
|
|
14
|
+
): Promise<string[]> => {
|
|
15
|
+
if (!existsSync(root)) return [];
|
|
16
|
+
const out: string[] = [];
|
|
17
|
+
const stack = [root];
|
|
18
|
+
while (stack.length > 0) {
|
|
19
|
+
const dir = stack.pop();
|
|
20
|
+
if (!dir) continue;
|
|
21
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
22
|
+
const full = join(dir, entry.name);
|
|
23
|
+
if (ignoredPaths.has(resolve(full))) continue;
|
|
24
|
+
if (entry.isDirectory() && ignoredDirectoryNames.has(entry.name)) continue;
|
|
25
|
+
if (entry.isSymbolicLink()) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`authored evaluation trees cannot contain symlinks: ${relative(root, full)}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (entry.isDirectory()) stack.push(full);
|
|
31
|
+
else if (entry.isFile()) out.push(relative(root, full));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return out.sort();
|
|
35
|
+
};
|
package/src/core/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./conversation-turn.js";
|
|
|
11
11
|
export * from "./driver.js";
|
|
12
12
|
export * from "./driver-metrics.js";
|
|
13
13
|
export * from "./emitter.js";
|
|
14
|
+
export * from "./environment.js";
|
|
14
15
|
export * from "./events.js";
|
|
15
16
|
export * from "./exec.js";
|
|
16
17
|
export * from "./experiment.js";
|