@prismatic-io/lux 0.0.2-preview.4 → 0.0.2-preview.40
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 +15 -3
- package/lib/answerers/claude-code/index.d.ts.map +1 -1
- package/lib/answerers/claude-code/index.js +6 -5
- package/lib/answerers/claude-code/index.js.map +1 -1
- package/lib/answerers/persona/index.d.ts +44 -5
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/answerers/persona/index.js +60 -34
- package/lib/answerers/persona/index.js.map +1 -1
- package/lib/answerers/terminal/index.d.ts +9 -3
- package/lib/answerers/terminal/index.d.ts.map +1 -1
- package/lib/answerers/terminal/index.js +6 -4
- package/lib/answerers/terminal/index.js.map +1 -1
- package/lib/assertions/authoring.d.ts +2 -2
- package/lib/assertions/authoring.d.ts.map +1 -1
- package/lib/assertions/authoring.js.map +1 -1
- package/lib/assertions/core/command-exits-zero.d.ts +5 -2
- package/lib/assertions/core/command-exits-zero.d.ts.map +1 -1
- package/lib/assertions/core/command-exits-zero.js +59 -14
- package/lib/assertions/core/command-exits-zero.js.map +1 -1
- package/lib/assertions/core/event-checks.d.ts +3 -0
- package/lib/assertions/core/event-checks.d.ts.map +1 -1
- package/lib/assertions/core/event-checks.js +49 -3
- package/lib/assertions/core/event-checks.js.map +1 -1
- package/lib/assertions/core/file-checks.d.ts +6 -0
- package/lib/assertions/core/file-checks.d.ts.map +1 -1
- package/lib/assertions/core/index.d.ts.map +1 -1
- package/lib/assertions/core/index.js +2 -1
- package/lib/assertions/core/index.js.map +1 -1
- package/lib/assertions/core/json-pointer-equals.d.ts +1 -0
- package/lib/assertions/core/json-pointer-equals.d.ts.map +1 -1
- package/lib/assertions/core/output-checks.d.ts +3 -0
- package/lib/assertions/core/output-checks.d.ts.map +1 -1
- package/lib/assertions/core/predicate.d.ts +1 -0
- package/lib/assertions/core/predicate.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.d.ts +25 -0
- package/lib/assertions/core/resource-checks.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.js +35 -5
- package/lib/assertions/core/resource-checks.js.map +1 -1
- package/lib/assertions/core/safe-regex.d.ts.map +1 -1
- package/lib/assertions/core/safe-regex.js +7 -1
- package/lib/assertions/core/safe-regex.js.map +1 -1
- package/lib/assertions/rubric/evidence.d.ts +88 -0
- package/lib/assertions/rubric/evidence.d.ts.map +1 -0
- package/lib/assertions/rubric/evidence.js +75 -0
- package/lib/assertions/rubric/evidence.js.map +1 -0
- package/lib/assertions/rubric/index.d.ts +88 -34
- package/lib/assertions/rubric/index.d.ts.map +1 -1
- package/lib/assertions/rubric/index.js +59 -43
- package/lib/assertions/rubric/index.js.map +1 -1
- package/lib/assertions/rubric/internal.d.ts +30 -5
- package/lib/assertions/rubric/internal.d.ts.map +1 -1
- package/lib/assertions/rubric/internal.js +203 -41
- package/lib/assertions/rubric/internal.js.map +1 -1
- package/lib/authoring.d.ts +37 -2
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +36 -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/doctor.d.ts +55 -0
- package/lib/cli/doctor.d.ts.map +1 -0
- package/lib/cli/doctor.js +14 -0
- package/lib/cli/doctor.js.map +1 -0
- package/lib/cli/harness-plan.d.ts +27 -0
- package/lib/cli/harness-plan.d.ts.map +1 -0
- package/lib/cli/harness-plan.js +75 -0
- package/lib/cli/harness-plan.js.map +1 -0
- 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 +215 -0
- package/lib/cli/init-templates.js.map +1 -0
- package/lib/cli/init.d.ts +10 -0
- package/lib/cli/init.d.ts.map +1 -1
- package/lib/cli/init.js +34 -173
- package/lib/cli/init.js.map +1 -1
- package/lib/cli/output-schemas.d.ts +1412 -0
- package/lib/cli/output-schemas.d.ts.map +1 -0
- package/lib/cli/output-schemas.js +94 -0
- package/lib/cli/output-schemas.js.map +1 -0
- package/lib/cli/program.d.ts +140 -12
- package/lib/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +783 -650
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/render/format.d.ts.map +1 -1
- package/lib/cli/render/format.js +1 -1
- package/lib/cli/render/format.js.map +1 -1
- package/lib/cli/render/reporter.d.ts.map +1 -1
- package/lib/cli/render/reporter.js +35 -8
- package/lib/cli/render/reporter.js.map +1 -1
- package/lib/cli/run-options.d.ts +19 -4
- package/lib/cli/run-options.d.ts.map +1 -1
- package/lib/cli/run-options.js +100 -19
- package/lib/cli/run-options.js.map +1 -1
- package/lib/cli/view.d.ts +15 -0
- package/lib/cli/view.d.ts.map +1 -0
- package/lib/cli/view.js +168 -0
- package/lib/cli/view.js.map +1 -0
- package/lib/container/driver.d.ts +27 -0
- package/lib/container/driver.d.ts.map +1 -0
- package/lib/container/driver.js +199 -0
- package/lib/container/driver.js.map +1 -0
- package/lib/container/paths.d.ts +3 -0
- package/lib/container/paths.d.ts.map +1 -0
- package/lib/container/paths.js +27 -0
- package/lib/container/paths.js.map +1 -0
- package/lib/container/protocol.d.ts +12 -0
- package/lib/container/protocol.d.ts.map +1 -0
- package/lib/container/protocol.js +38 -0
- package/lib/container/protocol.js.map +1 -0
- package/lib/container/resources.d.ts +14 -0
- package/lib/container/resources.d.ts.map +1 -0
- package/lib/container/resources.js +141 -0
- package/lib/container/resources.js.map +1 -0
- package/lib/container/worker.d.ts +2 -0
- package/lib/container/worker.d.ts.map +1 -0
- package/lib/container/worker.js +85 -0
- package/lib/container/worker.js.map +1 -0
- package/lib/core/action-events.d.ts +29 -0
- package/lib/core/action-events.d.ts.map +1 -0
- package/lib/core/action-events.js +51 -0
- package/lib/core/action-events.js.map +1 -0
- package/lib/core/agent-activity.d.ts +49 -0
- package/lib/core/agent-activity.d.ts.map +1 -0
- package/lib/core/agent-activity.js +206 -0
- package/lib/core/agent-activity.js.map +1 -0
- package/lib/core/agent-environment.d.ts +5 -0
- package/lib/core/agent-environment.d.ts.map +1 -0
- package/lib/core/agent-environment.js +47 -0
- package/lib/core/agent-environment.js.map +1 -0
- package/lib/core/agent-events.d.ts +26 -0
- package/lib/core/agent-events.d.ts.map +1 -0
- package/lib/core/agent-events.js +13 -0
- package/lib/core/agent-events.js.map +1 -0
- 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/artifact-evidence.d.ts +6 -1
- package/lib/core/artifact-evidence.d.ts.map +1 -1
- package/lib/core/artifact-evidence.js +27 -6
- package/lib/core/artifact-evidence.js.map +1 -1
- package/lib/core/assertion.d.ts +10 -4
- package/lib/core/assertion.d.ts.map +1 -1
- package/lib/core/assertion.js +8 -2
- package/lib/core/assertion.js.map +1 -1
- package/lib/core/case-defaults.d.ts +16 -0
- package/lib/core/case-defaults.d.ts.map +1 -0
- package/lib/core/case-defaults.js +42 -0
- package/lib/core/case-defaults.js.map +1 -0
- package/lib/core/case.d.ts +120 -6
- package/lib/core/case.d.ts.map +1 -1
- package/lib/core/case.js +51 -5
- package/lib/core/case.js.map +1 -1
- package/lib/core/command-verification.d.ts +10 -0
- package/lib/core/command-verification.d.ts.map +1 -0
- package/lib/core/command-verification.js +66 -0
- package/lib/core/command-verification.js.map +1 -0
- package/lib/core/driver-execution.d.ts +83 -0
- package/lib/core/driver-execution.d.ts.map +1 -0
- package/lib/core/driver-execution.js +72 -0
- package/lib/core/driver-execution.js.map +1 -0
- package/lib/core/driver-metrics.d.ts +98 -0
- package/lib/core/driver-metrics.d.ts.map +1 -0
- package/lib/core/driver-metrics.js +36 -0
- package/lib/core/driver-metrics.js.map +1 -0
- package/lib/core/driver.d.ts +64 -0
- package/lib/core/driver.d.ts.map +1 -1
- package/lib/core/driver.js +28 -0
- package/lib/core/driver.js.map +1 -1
- package/lib/core/duration.d.ts +5 -0
- package/lib/core/duration.d.ts.map +1 -0
- package/lib/core/duration.js +49 -0
- package/lib/core/duration.js.map +1 -0
- 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/events.d.ts +34 -0
- package/lib/core/events.d.ts.map +1 -1
- package/lib/core/events.js +11 -2
- package/lib/core/events.js.map +1 -1
- package/lib/core/experiment.d.ts +33 -2
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +40 -19
- 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/global-id.d.ts +6 -0
- package/lib/core/global-id.d.ts.map +1 -0
- package/lib/core/global-id.js +17 -0
- package/lib/core/global-id.js.map +1 -0
- package/lib/core/harness-catalog.d.ts +35 -0
- package/lib/core/harness-catalog.d.ts.map +1 -0
- package/lib/core/harness-catalog.js +45 -0
- package/lib/core/harness-catalog.js.map +1 -0
- package/lib/core/harness-catalogs/antigravity.d.ts +11 -0
- package/lib/core/harness-catalogs/antigravity.d.ts.map +1 -0
- package/lib/core/harness-catalogs/antigravity.js +23 -0
- package/lib/core/harness-catalogs/antigravity.js.map +1 -0
- package/lib/core/harness-catalogs/claude-code.d.ts +12 -0
- package/lib/core/harness-catalogs/claude-code.d.ts.map +1 -0
- package/lib/core/harness-catalogs/claude-code.js +24 -0
- package/lib/core/harness-catalogs/claude-code.js.map +1 -0
- package/lib/core/harness-catalogs/codex.d.ts +11 -0
- package/lib/core/harness-catalogs/codex.d.ts.map +1 -0
- package/lib/core/harness-catalogs/codex.js +26 -0
- package/lib/core/harness-catalogs/codex.js.map +1 -0
- package/lib/core/harness-catalogs/copilot.d.ts +29 -0
- package/lib/core/harness-catalogs/copilot.d.ts.map +1 -0
- package/lib/core/harness-catalogs/copilot.js +45 -0
- package/lib/core/harness-catalogs/copilot.js.map +1 -0
- package/lib/core/harness-catalogs/cursor.d.ts +2 -0
- package/lib/core/harness-catalogs/cursor.d.ts.map +1 -0
- package/lib/core/harness-catalogs/cursor.js +13 -0
- package/lib/core/harness-catalogs/cursor.js.map +1 -0
- package/lib/core/harness-catalogs/grok-build.d.ts +7 -0
- package/lib/core/harness-catalogs/grok-build.d.ts.map +1 -0
- package/lib/core/harness-catalogs/grok-build.js +18 -0
- package/lib/core/harness-catalogs/grok-build.js.map +1 -0
- package/lib/core/harness-catalogs/index.d.ts +73 -0
- package/lib/core/harness-catalogs/index.d.ts.map +1 -0
- package/lib/core/harness-catalogs/index.js +17 -0
- package/lib/core/harness-catalogs/index.js.map +1 -0
- package/lib/core/harness-matrix.d.ts +154 -0
- package/lib/core/harness-matrix.d.ts.map +1 -0
- package/lib/core/harness-matrix.js +204 -0
- package/lib/core/harness-matrix.js.map +1 -0
- package/lib/core/index.d.ts +12 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +12 -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/model-cli.d.ts +9 -11
- package/lib/core/model-cli.d.ts.map +1 -1
- package/lib/core/model-cli.js +39 -22
- package/lib/core/model-cli.js.map +1 -1
- package/lib/core/platform-process.d.ts +7 -2
- package/lib/core/platform-process.d.ts.map +1 -1
- package/lib/core/platform-process.js +54 -16
- package/lib/core/platform-process.js.map +1 -1
- package/lib/core/reference-defaults.d.ts +5 -0
- package/lib/core/reference-defaults.d.ts.map +1 -0
- package/lib/core/reference-defaults.js +42 -0
- package/lib/core/reference-defaults.js.map +1 -0
- package/lib/core/rubric.d.ts +1 -0
- package/lib/core/rubric.d.ts.map +1 -1
- package/lib/core/run.d.ts +292 -1
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +27 -0
- package/lib/core/run.js.map +1 -1
- package/lib/core/secure-copy.d.ts.map +1 -0
- package/lib/core/secure-copy.js.map +1 -0
- 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/core/wire.d.ts +11 -0
- package/lib/core/wire.d.ts.map +1 -1
- package/lib/drivers/antigravity/artifacts.d.ts +2 -0
- package/lib/drivers/antigravity/artifacts.d.ts.map +1 -0
- package/lib/drivers/antigravity/artifacts.js +33 -0
- package/lib/drivers/antigravity/artifacts.js.map +1 -0
- package/lib/drivers/antigravity/config.d.ts +88 -0
- package/lib/drivers/antigravity/config.d.ts.map +1 -0
- package/lib/drivers/antigravity/config.js +87 -0
- package/lib/drivers/antigravity/config.js.map +1 -0
- package/lib/drivers/antigravity/events.d.ts +4 -0
- package/lib/drivers/antigravity/events.d.ts.map +1 -0
- package/lib/drivers/antigravity/events.js +241 -0
- package/lib/drivers/antigravity/events.js.map +1 -0
- package/lib/drivers/antigravity/index.d.ts +114 -0
- package/lib/drivers/antigravity/index.d.ts.map +1 -0
- package/lib/drivers/antigravity/index.js +113 -0
- package/lib/drivers/antigravity/index.js.map +1 -0
- package/lib/drivers/antigravity/runtime.d.ts +7 -0
- package/lib/drivers/antigravity/runtime.d.ts.map +1 -0
- package/lib/drivers/antigravity/runtime.js +83 -0
- package/lib/drivers/antigravity/runtime.js.map +1 -0
- package/lib/drivers/claude-code/deferred-process.d.ts +17 -0
- package/lib/drivers/claude-code/deferred-process.d.ts.map +1 -0
- package/lib/drivers/claude-code/deferred-process.js +108 -0
- package/lib/drivers/claude-code/deferred-process.js.map +1 -0
- package/lib/drivers/claude-code/index.d.ts +187 -10
- package/lib/drivers/claude-code/index.d.ts.map +1 -1
- package/lib/drivers/claude-code/index.js +197 -252
- package/lib/drivers/claude-code/index.js.map +1 -1
- package/lib/drivers/claude-code/isolation.d.ts +8 -0
- package/lib/drivers/claude-code/isolation.d.ts.map +1 -0
- package/lib/drivers/claude-code/isolation.js +199 -0
- package/lib/drivers/claude-code/isolation.js.map +1 -0
- package/lib/drivers/claude-code/parse-events.d.ts.map +1 -1
- package/lib/drivers/claude-code/parse-events.js +73 -8
- package/lib/drivers/claude-code/parse-events.js.map +1 -1
- package/lib/drivers/codex/app-events.d.ts +9 -2
- package/lib/drivers/codex/app-events.d.ts.map +1 -1
- package/lib/drivers/codex/app-events.js +104 -12
- package/lib/drivers/codex/app-events.js.map +1 -1
- package/lib/drivers/codex/config.d.ts +69 -5
- package/lib/drivers/codex/config.d.ts.map +1 -1
- package/lib/drivers/codex/config.js +48 -8
- package/lib/drivers/codex/config.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 +142 -4
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +114 -276
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/drivers/copilot/config.d.ts +84 -0
- package/lib/drivers/copilot/config.d.ts.map +1 -0
- package/lib/drivers/copilot/config.js +57 -0
- package/lib/drivers/copilot/config.js.map +1 -0
- package/lib/drivers/copilot/index.d.ts +107 -0
- package/lib/drivers/copilot/index.d.ts.map +1 -0
- package/lib/drivers/copilot/index.js +268 -0
- package/lib/drivers/copilot/index.js.map +1 -0
- package/lib/drivers/cursor/config.d.ts +28 -0
- package/lib/drivers/cursor/config.d.ts.map +1 -0
- package/lib/drivers/cursor/config.js +28 -0
- package/lib/drivers/cursor/config.js.map +1 -0
- package/lib/drivers/cursor/index.d.ts +44 -0
- package/lib/drivers/cursor/index.d.ts.map +1 -0
- package/lib/drivers/cursor/index.js +286 -0
- package/lib/drivers/cursor/index.js.map +1 -0
- package/lib/drivers/cursor/interaction.d.ts +6 -0
- package/lib/drivers/cursor/interaction.d.ts.map +1 -0
- package/lib/drivers/cursor/interaction.js +55 -0
- package/lib/drivers/cursor/interaction.js.map +1 -0
- package/lib/drivers/grok-build/config.d.ts +68 -0
- package/lib/drivers/grok-build/config.d.ts.map +1 -0
- package/lib/drivers/grok-build/config.js +40 -0
- package/lib/drivers/grok-build/config.js.map +1 -0
- package/lib/drivers/grok-build/index.d.ts +87 -0
- package/lib/drivers/grok-build/index.d.ts.map +1 -0
- package/lib/drivers/grok-build/index.js +293 -0
- package/lib/drivers/grok-build/index.js.map +1 -0
- package/lib/drivers/mcp/index.d.ts +37 -4
- package/lib/drivers/mcp/index.d.ts.map +1 -1
- package/lib/drivers/mcp/index.js +25 -11
- package/lib/drivers/mcp/index.js.map +1 -1
- package/lib/drivers/shared/acp-events.d.ts +15 -0
- package/lib/drivers/shared/acp-events.d.ts.map +1 -0
- package/lib/drivers/shared/acp-events.js +83 -0
- package/lib/drivers/shared/acp-events.js.map +1 -0
- package/lib/drivers/shared/acp-interaction.d.ts +7 -0
- package/lib/drivers/shared/acp-interaction.d.ts.map +1 -0
- package/lib/drivers/shared/acp-interaction.js +20 -0
- package/lib/drivers/shared/acp-interaction.js.map +1 -0
- package/lib/drivers/shared/acp-session.d.ts +4 -0
- package/lib/drivers/shared/acp-session.d.ts.map +1 -0
- package/lib/drivers/shared/acp-session.js +23 -0
- package/lib/drivers/shared/acp-session.js.map +1 -0
- package/lib/drivers/shared/acp-transport.d.ts +40 -0
- package/lib/drivers/shared/acp-transport.d.ts.map +1 -0
- package/lib/drivers/shared/acp-transport.js +152 -0
- package/lib/drivers/shared/acp-transport.js.map +1 -0
- package/lib/drivers/shared/artifacts.d.ts.map +1 -1
- package/lib/drivers/shared/artifacts.js +3 -2
- package/lib/drivers/shared/artifacts.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 +200 -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 +144 -0
- package/lib/drivers/shared/experiment-policy.js.map +1 -0
- package/lib/drivers/shared/skills.d.ts +3 -0
- package/lib/drivers/shared/skills.d.ts.map +1 -0
- package/lib/drivers/shared/skills.js +73 -0
- package/lib/drivers/shared/skills.js.map +1 -0
- package/lib/drivers/subprocess/index.d.ts +39 -4
- package/lib/drivers/subprocess/index.d.ts.map +1 -1
- package/lib/drivers/subprocess/index.js +56 -12
- package/lib/drivers/subprocess/index.js.map +1 -1
- package/lib/index.d.ts +24 -8
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +20 -5
- package/lib/index.js.map +1 -1
- package/lib/optimization/reflective.d.ts +3 -1
- package/lib/optimization/reflective.d.ts.map +1 -1
- package/lib/optimization/reflective.js +3 -2
- package/lib/optimization/reflective.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/artifact-origin.d.ts +3 -0
- package/lib/orchestrator/artifact-origin.d.ts.map +1 -0
- package/lib/orchestrator/artifact-origin.js +40 -0
- package/lib/orchestrator/artifact-origin.js.map +1 -0
- 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/candidates.d.ts.map +1 -1
- package/lib/orchestrator/candidates.js +3 -2
- package/lib/orchestrator/candidates.js.map +1 -1
- package/lib/orchestrator/compare.d.ts +23 -6
- package/lib/orchestrator/compare.d.ts.map +1 -1
- package/lib/orchestrator/compare.js +45 -12
- package/lib/orchestrator/compare.js.map +1 -1
- package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
- package/lib/orchestrator/comparison-identity.js +2 -0
- package/lib/orchestrator/comparison-identity.js.map +1 -1
- package/lib/orchestrator/config.d.ts +180 -1
- package/lib/orchestrator/config.d.ts.map +1 -1
- package/lib/orchestrator/config.js +21 -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 +1 -0
- package/lib/orchestrator/doctor.d.ts.map +1 -1
- package/lib/orchestrator/doctor.js +134 -69
- package/lib/orchestrator/doctor.js.map +1 -1
- package/lib/orchestrator/driver-capabilities.d.ts +4 -0
- package/lib/orchestrator/driver-capabilities.d.ts.map +1 -0
- package/lib/orchestrator/driver-capabilities.js +13 -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 +51 -0
- package/lib/orchestrator/driver-identity.js.map +1 -0
- package/lib/orchestrator/experiment-context.d.ts +51 -3
- package/lib/orchestrator/experiment-context.d.ts.map +1 -1
- package/lib/orchestrator/experiment-context.js +2 -1
- package/lib/orchestrator/experiment-context.js.map +1 -1
- package/lib/orchestrator/experiment-contracts.d.ts +31 -1
- package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.d.ts +2 -1
- package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.js +28 -22
- 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 +46 -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 +70 -5
- package/lib/orchestrator/experiment-evaluation.js.map +1 -1
- package/lib/orchestrator/experiment-identity.d.ts +3 -0
- package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-identity.js +22 -33
- package/lib/orchestrator/experiment-identity.js.map +1 -1
- package/lib/orchestrator/experiment-loader.d.ts +8 -3
- package/lib/orchestrator/experiment-loader.d.ts.map +1 -1
- package/lib/orchestrator/experiment-preflight.d.ts +3 -0
- package/lib/orchestrator/experiment-preflight.d.ts.map +1 -0
- package/lib/orchestrator/experiment-preflight.js +21 -0
- package/lib/orchestrator/experiment-preflight.js.map +1 -0
- package/lib/orchestrator/experiment-report.d.ts +91 -1
- 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 +63 -2
- 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 +151 -43
- package/lib/orchestrator/experiment-selection.js.map +1 -1
- package/lib/orchestrator/experiment.d.ts +3 -3
- package/lib/orchestrator/experiment.d.ts.map +1 -1
- package/lib/orchestrator/experiment.js +26 -21
- package/lib/orchestrator/experiment.js.map +1 -1
- package/lib/orchestrator/fixture-identity.d.ts +1 -0
- package/lib/orchestrator/fixture-identity.d.ts.map +1 -1
- package/lib/orchestrator/fixture-identity.js +6 -1
- package/lib/orchestrator/fixture-identity.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 +23 -5
- package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
- package/lib/orchestrator/grader-experiment.js +54 -9
- package/lib/orchestrator/grader-experiment.js.map +1 -1
- package/lib/orchestrator/grading.d.ts +1 -0
- package/lib/orchestrator/grading.d.ts.map +1 -1
- package/lib/orchestrator/grading.js +41 -33
- package/lib/orchestrator/grading.js.map +1 -1
- package/lib/orchestrator/harness-catalog-drift.d.ts +22 -0
- package/lib/orchestrator/harness-catalog-drift.d.ts.map +1 -0
- package/lib/orchestrator/harness-catalog-drift.js +90 -0
- package/lib/orchestrator/harness-catalog-drift.js.map +1 -0
- package/lib/orchestrator/harness-registry.d.ts +4 -0
- package/lib/orchestrator/harness-registry.d.ts.map +1 -0
- package/lib/orchestrator/harness-registry.js +5 -0
- package/lib/orchestrator/harness-registry.js.map +1 -0
- package/lib/orchestrator/index.d.ts +1 -1
- package/lib/orchestrator/index.d.ts.map +1 -1
- package/lib/orchestrator/index.js +1 -1
- package/lib/orchestrator/index.js.map +1 -1
- package/lib/orchestrator/list-runs.d.ts +8 -1
- package/lib/orchestrator/list-runs.d.ts.map +1 -1
- package/lib/orchestrator/list-runs.js +83 -18
- 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 +17 -2
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +55 -177
- 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 +164 -0
- package/lib/orchestrator/paired-effects.js.map +1 -0
- package/lib/orchestrator/profile.d.ts +4 -0
- package/lib/orchestrator/profile.d.ts.map +1 -0
- package/lib/orchestrator/profile.js +10 -0
- package/lib/orchestrator/profile.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 +4 -2
- package/lib/orchestrator/regrade.d.ts.map +1 -1
- package/lib/orchestrator/rubric-loader.js +1 -1
- package/lib/orchestrator/rubric-loader.js.map +1 -1
- package/lib/orchestrator/run-dir.d.ts +2 -0
- package/lib/orchestrator/run-dir.d.ts.map +1 -1
- package/lib/orchestrator/run-dir.js +1 -1
- package/lib/orchestrator/run-dir.js.map +1 -1
- package/lib/orchestrator/run-execution.d.ts +3 -0
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +64 -10
- 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/run-workspace.d.ts +10 -0
- package/lib/orchestrator/run-workspace.d.ts.map +1 -0
- package/lib/orchestrator/run-workspace.js +58 -0
- package/lib/orchestrator/run-workspace.js.map +1 -0
- package/lib/orchestrator/suite-compare.d.ts +37 -0
- package/lib/orchestrator/suite-compare.d.ts.map +1 -1
- package/lib/orchestrator/suite-compare.js +44 -22
- package/lib/orchestrator/suite-compare.js.map +1 -1
- package/lib/orchestrator/suite-runs.d.ts +6 -1
- package/lib/orchestrator/suite-runs.d.ts.map +1 -1
- package/lib/orchestrator/suite-runs.js +14 -2
- package/lib/orchestrator/suite-runs.js.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts +79 -0
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +67 -14
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/lib/previewer/artifacts.d.ts +25 -0
- package/lib/previewer/artifacts.d.ts.map +1 -0
- package/lib/previewer/artifacts.js +180 -0
- package/lib/previewer/artifacts.js.map +1 -0
- package/lib/previewer/server.d.ts +111 -0
- package/lib/previewer/server.d.ts.map +1 -0
- package/lib/previewer/server.js +559 -0
- package/lib/previewer/server.js.map +1 -0
- package/package.json +4 -2
- package/skills/lux-answerer/SKILL.md +2 -2
- package/src/answerers/claude-code/index.ts +6 -5
- package/src/answerers/persona/index.ts +75 -34
- package/src/answerers/terminal/index.ts +7 -11
- package/src/assertions/authoring.ts +2 -0
- package/src/assertions/core/command-exits-zero.ts +82 -17
- package/src/assertions/core/event-checks.ts +64 -3
- package/src/assertions/core/index.ts +2 -0
- package/src/assertions/core/resource-checks.ts +38 -5
- package/src/assertions/core/safe-regex.ts +16 -6
- package/src/assertions/rubric/evidence.ts +85 -0
- package/src/assertions/rubric/index.ts +63 -46
- package/src/assertions/rubric/internal.ts +256 -41
- package/src/authoring.ts +346 -2
- package/src/cli/bin.ts +2 -12
- package/src/cli/command-runtime.ts +20 -9
- package/src/cli/doctor.ts +17 -0
- package/src/cli/harness-plan.ts +123 -0
- package/src/cli/init-templates.ts +238 -0
- package/src/cli/init.ts +53 -184
- package/src/cli/output-schemas.ts +109 -0
- package/src/cli/program.ts +697 -730
- package/src/cli/render/format.ts +1 -1
- package/src/cli/render/reporter.ts +36 -8
- package/src/cli/run-options.ts +131 -25
- package/src/cli/view.ts +129 -0
- package/src/container/driver.ts +213 -0
- package/src/container/paths.ts +29 -0
- package/src/container/protocol.ts +48 -0
- package/src/container/resources.ts +165 -0
- package/src/container/worker.ts +107 -0
- package/src/core/action-events.ts +59 -0
- package/src/core/agent-activity.ts +236 -0
- package/src/core/agent-environment.ts +64 -0
- package/src/core/agent-events.ts +17 -0
- package/src/core/annotation.ts +2 -1
- package/src/core/answerer.ts +9 -0
- package/src/core/artifact-evidence.ts +40 -5
- package/src/core/assertion.ts +8 -2
- package/src/core/case-defaults.ts +65 -0
- package/src/core/case.ts +79 -8
- package/src/core/command-verification.ts +74 -0
- package/src/core/driver-execution.ts +84 -0
- package/src/core/driver-metrics.ts +51 -0
- package/src/core/driver.ts +76 -0
- package/src/core/duration.ts +52 -0
- package/src/core/environment.ts +22 -0
- package/src/core/evaluation-clusters.ts +35 -0
- package/src/core/events.ts +13 -2
- package/src/core/experiment.ts +71 -25
- package/src/core/file-tree.ts +35 -0
- package/src/core/global-id.ts +26 -0
- package/src/core/harness-catalog.ts +100 -0
- package/src/core/harness-catalogs/antigravity.ts +24 -0
- package/src/core/harness-catalogs/claude-code.ts +25 -0
- package/src/core/harness-catalogs/codex.ts +27 -0
- package/src/core/harness-catalogs/copilot.ts +46 -0
- package/src/core/harness-catalogs/cursor.ts +13 -0
- package/src/core/harness-catalogs/grok-build.ts +19 -0
- package/src/core/harness-catalogs/index.ts +31 -0
- package/src/core/harness-matrix.ts +316 -0
- package/src/core/index.ts +12 -0
- package/src/core/lifecycle-fixtures.ts +4 -2
- package/src/core/model-cli.ts +60 -26
- package/src/core/platform-process.ts +65 -16
- package/src/core/reference-defaults.ts +47 -0
- package/src/core/run.ts +31 -0
- package/src/core/usage.ts +8 -0
- package/src/drivers/antigravity/README.md +171 -0
- package/src/drivers/antigravity/artifacts.ts +37 -0
- package/src/drivers/antigravity/config.ts +92 -0
- package/src/drivers/antigravity/events.ts +290 -0
- package/src/drivers/antigravity/index.ts +132 -0
- package/src/drivers/antigravity/runtime.ts +94 -0
- package/src/drivers/claude-code/deferred-process.ts +118 -0
- package/src/drivers/claude-code/index.ts +227 -281
- package/src/drivers/claude-code/isolation.ts +228 -0
- package/src/drivers/claude-code/parse-events.ts +98 -12
- package/src/drivers/codex/app-events.ts +126 -16
- package/src/drivers/codex/config.ts +59 -8
- package/src/drivers/codex/exec-events.ts +258 -0
- package/src/drivers/codex/index.ts +146 -312
- package/src/drivers/copilot/README.md +60 -0
- package/src/drivers/copilot/config.ts +61 -0
- package/src/drivers/copilot/index.ts +310 -0
- package/src/drivers/cursor/README.md +66 -0
- package/src/drivers/cursor/config.ts +31 -0
- package/src/drivers/cursor/index.ts +326 -0
- package/src/drivers/cursor/interaction.ts +74 -0
- package/src/drivers/grok-build/README.md +112 -0
- package/src/drivers/grok-build/config.ts +43 -0
- package/src/drivers/grok-build/index.ts +331 -0
- package/src/drivers/mcp/index.ts +25 -11
- package/src/drivers/shared/acp-events.ts +94 -0
- package/src/drivers/shared/acp-interaction.ts +27 -0
- package/src/drivers/shared/acp-session.ts +40 -0
- package/src/drivers/shared/acp-transport.ts +187 -0
- package/src/drivers/shared/artifacts.ts +6 -2
- package/src/drivers/shared/experiment-behavior.ts +217 -0
- package/src/drivers/shared/experiment-policy.ts +196 -0
- package/src/drivers/shared/skills.ts +74 -0
- package/src/drivers/subprocess/index.ts +55 -12
- package/src/index.ts +117 -6
- package/src/optimization/reflective.ts +3 -2
- package/src/orchestrator/annotation-loader.ts +10 -0
- package/src/orchestrator/annotation-store.ts +24 -3
- package/src/orchestrator/artifact-origin.ts +50 -0
- package/src/orchestrator/campaign-lifecycle.ts +5 -1
- package/src/orchestrator/candidates.ts +3 -2
- package/src/orchestrator/compare.ts +62 -13
- package/src/orchestrator/comparison-identity.ts +2 -0
- package/src/orchestrator/config.ts +23 -1
- package/src/orchestrator/discover.ts +8 -1
- package/src/orchestrator/doctor.ts +211 -65
- package/src/orchestrator/driver-capabilities.ts +21 -0
- package/src/orchestrator/driver-identity.ts +69 -0
- package/src/orchestrator/experiment-context.ts +5 -3
- package/src/orchestrator/experiment-corpus.ts +58 -37
- package/src/orchestrator/experiment-effects.ts +63 -0
- package/src/orchestrator/experiment-evaluation.ts +88 -10
- package/src/orchestrator/experiment-identity.ts +57 -48
- package/src/orchestrator/experiment-preflight.ts +27 -0
- 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 +178 -18
- package/src/orchestrator/experiment.ts +29 -29
- package/src/orchestrator/fixture-identity.ts +9 -1
- package/src/orchestrator/fs-read.ts +2 -32
- package/src/orchestrator/grader-experiment.ts +74 -13
- package/src/orchestrator/grading.ts +60 -31
- package/src/orchestrator/harness-catalog-drift.ts +132 -0
- package/src/orchestrator/harness-registry.ts +12 -0
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/list-runs.ts +102 -18
- package/src/orchestrator/optimization-lifecycle.ts +4 -1
- package/src/orchestrator/orchestrator.ts +105 -224
- package/src/orchestrator/paired-effects.ts +214 -0
- package/src/orchestrator/profile.ts +24 -0
- package/src/orchestrator/provenance.ts +23 -3
- package/src/orchestrator/rubric-loader.ts +1 -1
- package/src/orchestrator/run-dir.ts +8 -1
- package/src/orchestrator/run-execution.ts +75 -10
- package/src/orchestrator/run-lifecycle.ts +4 -2
- package/src/orchestrator/run-workspace.ts +62 -0
- package/src/orchestrator/suite-compare.ts +47 -23
- package/src/orchestrator/suite-runs.ts +13 -2
- package/src/orchestrator/suite-summary.ts +129 -15
- package/src/previewer/artifacts.ts +221 -0
- package/src/previewer/server.ts +789 -0
- package/viewer/app.css +2111 -0
- package/viewer/app.js +627 -0
- package/viewer/artifact-view.js +48 -0
- package/viewer/charts.js +98 -0
- package/viewer/detail.js +1266 -0
- package/viewer/format.js +129 -0
- package/viewer/index.html +151 -0
- 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/lib/drivers/shared/secure-copy.d.ts.map +0 -1
- package/lib/drivers/shared/secure-copy.js.map +0 -1
- package/skills/lux/SKILL.md +0 -89
- package/skills/lux/references/cli.md +0 -125
- package/skills/lux/references/eval-authoring.md +0 -257
- package/src/cli/skills.ts +0 -129
- /package/lib/{drivers/shared → core}/secure-copy.d.ts +0 -0
- /package/lib/{drivers/shared → core}/secure-copy.js +0 -0
- /package/src/{drivers/shared → core}/secure-copy.ts +0 -0
package/lib/cli/program.js
CHANGED
|
@@ -1,119 +1,377 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import { randomUUID } from "node:crypto";
|
|
2
54
|
import { existsSync } from "node:fs";
|
|
3
55
|
import { lstat, mkdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
56
|
import { createRequire } from "node:module";
|
|
5
|
-
import { basename, dirname,
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { GraderAnnotationSchema, matchesAnyGlob, valueHash, } from "../core/index.js";
|
|
9
|
-
import {
|
|
57
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
58
|
+
import { fileURLToPath } from "node:url";
|
|
59
|
+
import { Cli, Errors, z } from "incur";
|
|
60
|
+
import { GraderAnnotationSchema, matchesAnyGlob, ReasoningEffortSchema, valueHash, } from "../core/index.js";
|
|
61
|
+
import { registryWithHarness } from "../orchestrator/harness-registry.js";
|
|
62
|
+
import { applyCandidate, buildRegistryFromConfig, compareRuns, compareSuiteRuns, DoctorReportSchema, discoverCases, ExperimentCampaignPlanSchema, ExperimentDecisionReportSchema, ExperimentRunSchema, experimentCampaignMode, formatComparisonText, formatExperimentDecisionMarkdown, formatSuiteComparisonText, GradingSnapshotSchema, hasRegression, hasSuiteRegression, loadEvalCase, loadExperimentCampaign, loadExperimentRun, loadFrozenRegradableRun, loadLuxConfig, loadRubricDefinition, loadSuiteRun, loadVerifiedExperimentDecisionSnapshot, Orchestrator, optimizeExperimentCampaign, planExperimentCampaign, regradeRun, releaseFrozenRegradableRun, resolveExperimentCampaignPath, runExperimentCampaign, upsertGraderAnnotation, verifyExperimentDecisionIntegrity, verifyExperimentRunIntegrity, } from "../orchestrator/index.js";
|
|
63
|
+
import { resolveProfile } from "../orchestrator/profile.js";
|
|
10
64
|
import { abortOnInterrupt, ensureDirectory, failWith, relativePathWithin, } from "./command-runtime.js";
|
|
11
|
-
import {
|
|
65
|
+
import { doctorCommand } from "./doctor.js";
|
|
66
|
+
import { initProject } from "./init.js";
|
|
67
|
+
import { CompareOutputSchema, ViewOutputSchema } from "./output-schemas.js";
|
|
12
68
|
import { ConsoleReporter } from "./render/reporter.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
69
|
+
import { expandLoop, parseAnnotationAssertion, partitionFilters, planSuiteItems, resolveAnswererOverride, resolveConcurrency, selectSuite, suiteTitle, } from "./run-options.js";
|
|
70
|
+
import { viewCommand, webViewCommand } from "./view.js";
|
|
15
71
|
const requireFromHere = createRequire(import.meta.url);
|
|
16
72
|
// From lib/cli/program.js (or src/cli/program.ts under test) to the package root.
|
|
17
73
|
const PKG_VERSION = requireFromHere("../../package.json").version;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
74
|
+
const nonEmptyString = z.string().min(1);
|
|
75
|
+
const positiveInteger = z.coerce.number().int().positive();
|
|
76
|
+
const unitInterval = z.coerce.number().min(0).max(1);
|
|
77
|
+
const tagList = z
|
|
78
|
+
.array(nonEmptyString)
|
|
79
|
+
.default([])
|
|
80
|
+
.describe("Repeatable list; each value may be comma-separated");
|
|
81
|
+
const normalizeTags = (values) => values.flatMap((value) => value.split(",").map((tag) => tag.trim())).filter(Boolean);
|
|
82
|
+
const initOutput = z.object({
|
|
83
|
+
created: z.array(z.string()),
|
|
84
|
+
updated: z.array(z.string()),
|
|
85
|
+
skipped: z.array(z.object({ path: z.string(), reason: z.string() })),
|
|
86
|
+
experiment: z.boolean(),
|
|
87
|
+
});
|
|
88
|
+
const runOutput = z.union([
|
|
89
|
+
z.object({
|
|
90
|
+
caseCount: z.number().int(),
|
|
91
|
+
cases: z.array(z.object({
|
|
92
|
+
path: z.string(),
|
|
93
|
+
id: z.string(),
|
|
94
|
+
caseId: z.string().optional(),
|
|
95
|
+
permutationId: z.string().optional(),
|
|
96
|
+
})),
|
|
97
|
+
}),
|
|
98
|
+
z.object({
|
|
99
|
+
caseCount: z.number().int(),
|
|
100
|
+
tags: z.array(z.object({ tag: z.string(), count: z.number().int() })),
|
|
101
|
+
}),
|
|
102
|
+
z.object({
|
|
103
|
+
suiteDir: z.string(),
|
|
104
|
+
results: z.array(z.object({
|
|
105
|
+
caseId: z.string(),
|
|
106
|
+
runDir: z.string().nullable(),
|
|
107
|
+
exitReason: z.string(),
|
|
108
|
+
casePassed: z.boolean().nullable(),
|
|
109
|
+
caseScore: z.number().nullable(),
|
|
110
|
+
assertionsPassed: z.number().int(),
|
|
111
|
+
assertionsTotal: z.number().int(),
|
|
112
|
+
})),
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
const experimentOutput = z.union([
|
|
116
|
+
ExperimentCampaignPlanSchema,
|
|
117
|
+
ExperimentRunSchema.extend({ experimentDir: z.string() }),
|
|
118
|
+
]);
|
|
119
|
+
const gradingOutput = GradingSnapshotSchema.extend({ gradingPath: z.string() });
|
|
120
|
+
const reportOutput = z.union([
|
|
121
|
+
ExperimentDecisionReportSchema,
|
|
122
|
+
z.object({ path: z.string(), report: ExperimentDecisionReportSchema }),
|
|
123
|
+
]);
|
|
124
|
+
const campaignArgs = z.object({ campaign: nonEmptyString.describe("Experiment campaign path") });
|
|
125
|
+
const campaignOptions = z.object({
|
|
126
|
+
runsRoot: nonEmptyString.optional().describe("Run directory root overriding lux.config.ts"),
|
|
127
|
+
resume: nonEmptyString.optional().describe("Interrupted experiment directory to resume"),
|
|
128
|
+
plan: z.boolean().optional().describe("Validate and estimate without calling models"),
|
|
129
|
+
});
|
|
130
|
+
export const buildCli = () => Cli.create("lux", {
|
|
131
|
+
description: "Coding-agent evaluation and improvement with deterministic assertions and human-in-the-loop runs",
|
|
132
|
+
version: PKG_VERSION,
|
|
133
|
+
sync: {
|
|
134
|
+
include: [fileURLToPath(new URL("../../skills/*", import.meta.url))],
|
|
135
|
+
depth: 2,
|
|
136
|
+
suggestions: [
|
|
137
|
+
"Use Lux to inspect the eval cases in this project",
|
|
138
|
+
"Use Lux to diagnose the latest failed eval run",
|
|
139
|
+
"Use Lux to plan an evidence-based improvement experiment",
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
mcp: {
|
|
143
|
+
title: "Lux coding-agent evaluation",
|
|
144
|
+
instructions: "Inspect and plan before running model-spending commands. Apply only a verified promoted experiment champion.",
|
|
145
|
+
},
|
|
146
|
+
})
|
|
147
|
+
.command("init", {
|
|
148
|
+
description: "Scaffold a new eval project in the current directory",
|
|
149
|
+
options: z.object({
|
|
150
|
+
force: z.boolean().optional().describe("Overwrite existing files"),
|
|
151
|
+
experiment: z.boolean().optional().describe("Also scaffold an optimization campaign"),
|
|
152
|
+
driver: z
|
|
153
|
+
.enum([
|
|
154
|
+
"subprocess",
|
|
155
|
+
"claude-code",
|
|
156
|
+
"codex",
|
|
157
|
+
"cursor",
|
|
158
|
+
"copilot",
|
|
159
|
+
"antigravity",
|
|
160
|
+
"grok-build",
|
|
161
|
+
])
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Starter driver (default: deterministic subprocess)"),
|
|
164
|
+
model: nonEmptyString.optional().describe("Explicit subject model; required for Cursor"),
|
|
165
|
+
}),
|
|
166
|
+
output: initOutput,
|
|
167
|
+
destructive: true,
|
|
168
|
+
mcp: { annotations: { destructiveHint: true, openWorldHint: false } },
|
|
169
|
+
examples: [
|
|
170
|
+
{ description: "Scaffold a basic eval project" },
|
|
171
|
+
{ options: { experiment: true }, description: "Scaffold an optimization project" },
|
|
172
|
+
],
|
|
173
|
+
run: async (c) => c.ok(await initCommand(c.options), { cta: { commands: ["doctor", "run --list"] } }),
|
|
174
|
+
})
|
|
175
|
+
.command("doctor", {
|
|
176
|
+
description: "Preflight project configuration, CLIs, cases, campaigns, and budgets",
|
|
177
|
+
args: z.object({ campaigns: z.array(z.string()).default([]).describe("Campaign paths") }),
|
|
178
|
+
options: z.object({
|
|
179
|
+
static: z
|
|
180
|
+
.boolean()
|
|
181
|
+
.optional()
|
|
182
|
+
.describe("Validate project files without checking installed executables (for CI)"),
|
|
183
|
+
}),
|
|
184
|
+
output: DoctorReportSchema,
|
|
185
|
+
mcp: {
|
|
186
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
187
|
+
},
|
|
188
|
+
run: async (c) => c.ok(await doctorCommand(c.args.campaigns, c.options), {
|
|
189
|
+
cta: { commands: ["run --list"] },
|
|
190
|
+
}),
|
|
191
|
+
})
|
|
192
|
+
.command("run", {
|
|
193
|
+
description: "Discover and run cases, filtered by name, path, or tag",
|
|
194
|
+
args: z.object({ filters: z.array(z.string()).default([]).describe("Case names or paths") }),
|
|
195
|
+
options: z.object({
|
|
196
|
+
tag: tagList.describe("Required case tag; repeatable and comma-separated"),
|
|
197
|
+
loop: positiveInteger.optional().describe("Run each matched eval this many times"),
|
|
198
|
+
concurrency: positiveInteger.optional().describe("Maximum cases running concurrently"),
|
|
199
|
+
list: z.boolean().optional().describe("List matching cases without running"),
|
|
200
|
+
listTags: z.boolean().optional().describe("List tags across matching cases"),
|
|
201
|
+
interactive: z.boolean().optional().describe("Answer agent questions in this terminal"),
|
|
202
|
+
claudeAnswerer: z.boolean().optional().describe("Use Claude Code as the answerer"),
|
|
203
|
+
skipJudge: z.boolean().optional().describe("Skip rubric LLM judge assertions"),
|
|
204
|
+
runsRoot: nonEmptyString.optional().describe("Run directory root"),
|
|
205
|
+
profile: nonEmptyString.optional().describe("Named driver and answerer profile"),
|
|
206
|
+
driver: nonEmptyString
|
|
207
|
+
.optional()
|
|
208
|
+
.describe("Focus on one harness driver, replacing the matrix"),
|
|
209
|
+
model: nonEmptyString.optional().describe("Focus on one subject model"),
|
|
210
|
+
effort: ReasoningEffortSchema.optional().describe("Focus on one reasoning effort"),
|
|
211
|
+
subjectRoot: nonEmptyString.optional().describe("Root bound into subjectPath values"),
|
|
212
|
+
verbose: z.boolean().optional().describe("Include phase timings and run directories"),
|
|
213
|
+
}),
|
|
214
|
+
alias: { tag: "t", concurrency: "j", interactive: "i", verbose: "v" },
|
|
215
|
+
output: runOutput,
|
|
216
|
+
outputPolicy: "agent-only",
|
|
217
|
+
mcp: { annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true } },
|
|
218
|
+
examples: [
|
|
219
|
+
{ options: { list: true, tag: [] }, description: "List all discovered cases" },
|
|
220
|
+
{
|
|
221
|
+
args: { filters: ["smoke"] },
|
|
222
|
+
options: { tag: [] },
|
|
223
|
+
description: "Run matching cases",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
args: { filters: ["smoke"] },
|
|
227
|
+
options: { tag: [], driver: "codex", model: "gpt-5.6-luna", effort: "low" },
|
|
228
|
+
description: "Focus on one harness permutation",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
run: async (c) => {
|
|
232
|
+
const result = await runCommand(c.args.filters, c.options, !c.agent);
|
|
233
|
+
const suiteDir = "suiteDir" in result ? result.suiteDir : undefined;
|
|
234
|
+
return c.ok(result, {
|
|
235
|
+
cta: {
|
|
236
|
+
description: "Inspect saved results:",
|
|
237
|
+
commands: suiteDir ? [{ command: "view", args: { runDir: suiteDir } }] : [],
|
|
238
|
+
},
|
|
30
239
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
.
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
.command("experiment", {
|
|
243
|
+
description: "Evaluate controlled source variants across cases and model profiles",
|
|
244
|
+
args: campaignArgs,
|
|
245
|
+
options: campaignOptions,
|
|
246
|
+
output: experimentOutput,
|
|
247
|
+
mcp: { annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true } },
|
|
248
|
+
run: async (c) => experimentCommand("experiment", c.args.campaign, c.options),
|
|
249
|
+
})
|
|
250
|
+
.command("optimize", {
|
|
251
|
+
description: "Evolve candidates from grading feedback and promote on held-out cases",
|
|
252
|
+
args: campaignArgs,
|
|
253
|
+
options: campaignOptions,
|
|
254
|
+
output: experimentOutput,
|
|
255
|
+
destructive: true,
|
|
256
|
+
mcp: { annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true } },
|
|
257
|
+
run: async (c) => experimentCommand("optimize", c.args.campaign, c.options),
|
|
258
|
+
})
|
|
259
|
+
.command("grade", {
|
|
260
|
+
description: "Re-run current graders against a persisted run",
|
|
261
|
+
args: z.object({ runDir: nonEmptyString.describe("Persisted run directory") }),
|
|
262
|
+
options: z.object({ case: nonEmptyString.optional().describe("Current case definition") }),
|
|
263
|
+
output: gradingOutput,
|
|
264
|
+
mcp: { annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true } },
|
|
265
|
+
run: async (c) => gradeCommand(c.args.runDir, c.options),
|
|
266
|
+
})
|
|
267
|
+
.command("annotate", {
|
|
268
|
+
description: "Add or update a human grader-alignment label",
|
|
269
|
+
args: z.object({ runDir: nonEmptyString.describe("Persisted run directory") }),
|
|
270
|
+
options: z.object({
|
|
271
|
+
out: nonEmptyString.describe("JSON annotation dataset to create or update"),
|
|
272
|
+
rubric: nonEmptyString.describe("Candidate-relative declarative rubric path"),
|
|
273
|
+
id: nonEmptyString.optional().describe("Stable annotation ID"),
|
|
274
|
+
label: z.enum(["pass", "fail"]).optional().describe("Expected overall verdict"),
|
|
275
|
+
score: unitInterval.optional().describe("Expected score from zero to one"),
|
|
276
|
+
assertion: z
|
|
277
|
+
.array(nonEmptyString)
|
|
278
|
+
.default([])
|
|
279
|
+
.describe("Assertion label id=pass|fail[:score]"),
|
|
280
|
+
split: z.enum(["train", "validation", "test"]).optional().describe("Dataset split"),
|
|
281
|
+
tag: tagList.describe("Selection tag; repeatable and comma-separated"),
|
|
282
|
+
feedback: nonEmptyString.optional().describe("Human rationale for mismatches"),
|
|
283
|
+
annotator: nonEmptyString.optional().describe("Label author"),
|
|
284
|
+
reviewer: nonEmptyString.optional().describe("Independent reviewer"),
|
|
285
|
+
source: z.enum(["human", "synthetic"]).default("human").describe("Label provenance"),
|
|
286
|
+
}),
|
|
287
|
+
output: z.object({ id: z.string(), path: z.string(), labels: z.number().int() }),
|
|
288
|
+
destructive: true,
|
|
289
|
+
mcp: {
|
|
290
|
+
annotations: {
|
|
291
|
+
readOnlyHint: false,
|
|
292
|
+
destructiveHint: false,
|
|
293
|
+
idempotentHint: true,
|
|
294
|
+
openWorldHint: false,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
run: async (c) => annotateCommand(c.args.runDir, c.options),
|
|
298
|
+
})
|
|
299
|
+
.command("apply", {
|
|
300
|
+
description: "Apply the verified promoted experiment champion",
|
|
301
|
+
args: z.object({
|
|
302
|
+
experimentDir: nonEmptyString.describe("Experiment directory"),
|
|
303
|
+
candidateId: nonEmptyString.optional().describe("Expected promoted candidate ID"),
|
|
304
|
+
}),
|
|
305
|
+
output: z.object({
|
|
306
|
+
candidateId: z.string(),
|
|
307
|
+
subjectRoot: z.string(),
|
|
308
|
+
sourceBytes: z.number(),
|
|
309
|
+
}),
|
|
310
|
+
destructive: true,
|
|
311
|
+
mcp: {
|
|
312
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
313
|
+
},
|
|
314
|
+
run: async (c) => applyCommand(c.args.experimentDir, c.args.candidateId),
|
|
315
|
+
})
|
|
316
|
+
.command("report", {
|
|
317
|
+
description: "Render a PR-ready report from a verified experiment record",
|
|
318
|
+
args: z.object({ experimentDir: nonEmptyString.describe("Experiment directory") }),
|
|
319
|
+
options: z.object({
|
|
320
|
+
out: nonEmptyString.optional().describe("Write Markdown to this path"),
|
|
321
|
+
force: z.boolean().optional().describe("Overwrite an existing output file"),
|
|
322
|
+
}),
|
|
323
|
+
output: reportOutput,
|
|
324
|
+
mcp: { annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false } },
|
|
325
|
+
run: async (c) => reportCommand(c.args.experimentDir, c.options),
|
|
326
|
+
})
|
|
327
|
+
.command("view", {
|
|
328
|
+
description: "List past runs or show one run in detail",
|
|
329
|
+
args: z.object({ runDir: nonEmptyString.optional().describe("Run or suite directory") }),
|
|
330
|
+
options: z.object({
|
|
331
|
+
runsRoot: nonEmptyString.optional().describe("Run directory root"),
|
|
332
|
+
case: nonEmptyString.optional().describe("Filter lists to one case slug"),
|
|
333
|
+
suites: z.boolean().optional().describe("List logical suite runs"),
|
|
334
|
+
experiments: z.boolean().optional().describe("List experiment runs"),
|
|
335
|
+
web: z.boolean().optional().describe("Open the Lux Review web app"),
|
|
336
|
+
port: z.coerce.number().int().min(0).max(65535).optional().describe("Review server port"),
|
|
337
|
+
open: z.boolean().default(true).describe("Open a browser in web mode"),
|
|
338
|
+
}),
|
|
339
|
+
output: ViewOutputSchema,
|
|
340
|
+
mcp: { annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } },
|
|
341
|
+
run: (c) => {
|
|
342
|
+
if (c.options.web) {
|
|
343
|
+
if (c.formatExplicit && c.format !== "jsonl") {
|
|
344
|
+
throw new Errors.ParseError({
|
|
345
|
+
message: "--web requires streaming output; omit --format or use --format jsonl",
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return webViewCommand(c.args.runDir, c.options);
|
|
349
|
+
}
|
|
350
|
+
return viewCommand(c.args.runDir, c.options).then((result) => ViewOutputSchema.parse(result));
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
.command("compare", {
|
|
354
|
+
description: "Diff two run or suite directories and fail on regression",
|
|
355
|
+
args: z.object({
|
|
356
|
+
runA: nonEmptyString.describe("Baseline run"),
|
|
357
|
+
runB: nonEmptyString.describe("Current run"),
|
|
358
|
+
}),
|
|
359
|
+
options: z.object({
|
|
360
|
+
passRateTolerance: unitInterval.optional().describe("Allowed per-case pass-rate decrease"),
|
|
361
|
+
}),
|
|
362
|
+
output: CompareOutputSchema,
|
|
363
|
+
mcp: { annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } },
|
|
364
|
+
run: async (c) => CompareOutputSchema.parse(await compareCommand(c.args.runA, c.args.runB, c.options)),
|
|
365
|
+
});
|
|
366
|
+
const initCommand = async (options) => initProject({ cwd: process.cwd(), ...options });
|
|
367
|
+
const gradeCommand = async (runDir, options) => {
|
|
368
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
369
|
+
try {
|
|
112
370
|
const directory = resolve(runDir);
|
|
113
371
|
if (!(await ensureDirectory("grade", directory)))
|
|
114
|
-
|
|
372
|
+
failWith(1);
|
|
115
373
|
const config = await loadLuxConfig(process.cwd());
|
|
116
|
-
const controller = abortOnInterrupt();
|
|
374
|
+
const controller = __addDisposableResource(env_1, abortOnInterrupt(), false);
|
|
117
375
|
const result = await regradeRun({
|
|
118
376
|
runDir: directory,
|
|
119
377
|
registry: buildRegistryFromConfig(config),
|
|
@@ -121,335 +379,217 @@ const registerCandidateCommands = (program) => {
|
|
|
121
379
|
...(options.case ? { case: await loadEvalCase(resolve(options.case)) } : {}),
|
|
122
380
|
abortSignal: controller.signal,
|
|
123
381
|
});
|
|
124
|
-
const report = result.snapshot.report;
|
|
125
|
-
let gradingStatus = "ungraded";
|
|
126
|
-
if (report.casePassed === true)
|
|
127
|
-
gradingStatus = "PASS";
|
|
128
|
-
if (report.casePassed === false)
|
|
129
|
-
gradingStatus = "FAIL";
|
|
130
|
-
let output;
|
|
131
|
-
if (options.json) {
|
|
132
|
-
output = `${JSON.stringify({ gradingPath: result.path, ...result.snapshot }, null, 2)}\n`;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
const score = report.caseScore === null ? "—" : `${(report.caseScore * 100).toFixed(0)}%`;
|
|
136
|
-
output = [
|
|
137
|
-
`grading: ${gradingStatus}`,
|
|
138
|
-
`score: ${score}`,
|
|
139
|
-
`checks: ${report.assertions.filter((assertion) => assertion.passed).length}/${report.assertions.length}`,
|
|
140
|
-
`saved: ${result.path}`,
|
|
141
|
-
"",
|
|
142
|
-
].join("\n");
|
|
143
|
-
}
|
|
144
|
-
process.stdout.write(output);
|
|
145
382
|
if (controller.signal.aborted)
|
|
146
383
|
failWith(SIGINT_EXIT_CODE);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
.description("add or update a human grader-alignment label for a persisted run")
|
|
153
|
-
.requiredOption("--out <path>", "JSON annotation dataset to create or update")
|
|
154
|
-
.requiredOption("--rubric <path>", "candidate-relative declarative .json rubric path")
|
|
155
|
-
.option("--id <id>", "stable annotation id")
|
|
156
|
-
.option("--label <pass|fail>", "expected overall case verdict")
|
|
157
|
-
.option("--score <n>", "expected overall case score from 0 to 1", parseUnitInterval)
|
|
158
|
-
.option("--assertion <id=pass|fail[:score]>", "expected stable assertion verdict and optional score (repeatable)", collectValues, [])
|
|
159
|
-
.option("--split <train|validation|test>", "dataset split, stored as a tag")
|
|
160
|
-
.option("--tag <tag>", "extra selection tag (repeatable, comma-separated)", collectTags, [])
|
|
161
|
-
.option("--feedback <text>", "human rationale shown to the rubric optimizer on mismatch")
|
|
162
|
-
.option("--annotator <id>", "person or system that authored the label")
|
|
163
|
-
.option("--reviewer <id>", "independent reviewer of the label")
|
|
164
|
-
.option("--source <human|synthetic>", "label provenance", "human")
|
|
165
|
-
.action(async (runDir, options) => {
|
|
166
|
-
const directory = resolve(runDir);
|
|
167
|
-
if (!(await ensureDirectory("annotate", directory)))
|
|
168
|
-
return;
|
|
169
|
-
if (options.label && options.label !== "pass" && options.label !== "fail") {
|
|
170
|
-
throw new InvalidArgumentError("--label must be pass or fail");
|
|
171
|
-
}
|
|
172
|
-
if (options.split && !["train", "validation", "test"].includes(options.split)) {
|
|
173
|
-
throw new InvalidArgumentError("--split must be train, validation, or test");
|
|
174
|
-
}
|
|
175
|
-
if (options.source !== "human" && options.source !== "synthetic") {
|
|
176
|
-
throw new InvalidArgumentError("--source must be human or synthetic");
|
|
177
|
-
}
|
|
178
|
-
const parsedAssertions = options.assertion.map(parseAnnotationAssertion);
|
|
179
|
-
const assertionIds = parsedAssertions.map(([id]) => id);
|
|
180
|
-
if (new Set(assertionIds).size !== assertionIds.length) {
|
|
181
|
-
throw new InvalidArgumentError("--assertion ids must not be repeated");
|
|
182
|
-
}
|
|
183
|
-
const assertions = Object.fromEntries(parsedAssertions);
|
|
184
|
-
if (options.label === undefined &&
|
|
185
|
-
options.score === undefined &&
|
|
186
|
-
Object.keys(assertions).length === 0) {
|
|
187
|
-
throw new Error("lux annotate: provide --label, --score, or at least one --assertion");
|
|
188
|
-
}
|
|
189
|
-
const out = resolve(options.out);
|
|
190
|
-
const run = await loadFrozenRegradableRun(directory);
|
|
191
|
-
try {
|
|
192
|
-
const rubric = await loadRubricDefinition(await resolveAuthoredFile(options.rubric, "rubric"));
|
|
193
|
-
if (rubric.id !== run.case.id) {
|
|
194
|
-
throw new Error(`lux annotate: rubric id '${rubric.id}' does not match persisted case '${run.case.id}'`);
|
|
195
|
-
}
|
|
196
|
-
const rubricIds = new Set(rubric.assertions.map((assertion) => assertion.id));
|
|
197
|
-
const unknownIds = assertionIds.filter((assertionId) => !rubricIds.has(assertionId));
|
|
198
|
-
if (unknownIds.length > 0) {
|
|
199
|
-
throw new Error(`lux annotate: unknown rubric assertion ids: ${unknownIds.join(", ")}`);
|
|
200
|
-
}
|
|
201
|
-
const tags = [...(options.split ? [options.split] : []), ...options.tag].filter((tag, index, all) => all.indexOf(tag) === index);
|
|
202
|
-
const caseId = run.case.id;
|
|
203
|
-
const portableRunDir = (relative(dirname(out), directory) || ".").split(sep).join("/");
|
|
204
|
-
const id = options.id ??
|
|
205
|
-
`${caseId}-${basename(directory)}-${valueHash(portableRunDir).slice(0, 10)}`.replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
206
|
-
const annotation = GraderAnnotationSchema.parse({
|
|
207
|
-
id,
|
|
208
|
-
runDir: portableRunDir,
|
|
209
|
-
rubricPath: options.rubric,
|
|
210
|
-
tags,
|
|
211
|
-
source: options.source,
|
|
212
|
-
...(options.annotator ? { annotator: options.annotator } : {}),
|
|
213
|
-
labeledAt: new Date().toISOString(),
|
|
214
|
-
...(options.reviewer ? { reviewer: options.reviewer } : {}),
|
|
215
|
-
expected: {
|
|
216
|
-
...(options.label ? { casePassed: options.label === "pass" } : {}),
|
|
217
|
-
...(options.score !== undefined ? { caseScore: options.score } : {}),
|
|
218
|
-
assertions,
|
|
219
|
-
},
|
|
220
|
-
...(options.feedback ? { feedback: options.feedback } : {}),
|
|
384
|
+
if (result.snapshot.report.casePassed !== true) {
|
|
385
|
+
throw new Errors.IncurError({
|
|
386
|
+
code: "GRADING_FAILED",
|
|
387
|
+
message: `The persisted run did not pass regrading. Grading saved to ${result.path}.`,
|
|
388
|
+
exitCode: 1,
|
|
221
389
|
});
|
|
222
|
-
const dataset = await upsertGraderAnnotation(out, annotation);
|
|
223
|
-
process.stdout.write(`annotated ${annotation.id} in ${out} (${dataset.annotations.length} labels)\n`);
|
|
224
|
-
}
|
|
225
|
-
finally {
|
|
226
|
-
await releaseFrozenRegradableRun(run);
|
|
227
390
|
}
|
|
228
|
-
|
|
391
|
+
return { gradingPath: result.path, ...result.snapshot };
|
|
392
|
+
}
|
|
393
|
+
catch (e_1) {
|
|
394
|
+
env_1.error = e_1;
|
|
395
|
+
env_1.hasError = true;
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
__disposeResources(env_1);
|
|
399
|
+
}
|
|
229
400
|
};
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
.
|
|
243
|
-
.
|
|
244
|
-
|
|
245
|
-
const directory = resolve(experimentDir);
|
|
246
|
-
const experiment = await loadExperimentRun(directory);
|
|
247
|
-
if (!experiment) {
|
|
248
|
-
process.stderr.write(`lux apply: experiment manifest not found at ${directory}\n`);
|
|
249
|
-
failWith(1);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (experiment.status !== "passed") {
|
|
253
|
-
process.stderr.write(`lux apply: experiment status is '${experiment.status}', not 'passed'\n`);
|
|
254
|
-
failWith(1);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const championId = experiment.championCandidateId;
|
|
258
|
-
if (!championId) {
|
|
259
|
-
process.stderr.write("lux apply: passed experiment has no promoted champion\n");
|
|
260
|
-
failWith(1);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
if (candidateId && candidateId !== championId) {
|
|
264
|
-
process.stderr.write(`lux apply: candidate '${candidateId}' is not the promoted champion '${championId}'\n`);
|
|
265
|
-
failWith(1);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
let candidate;
|
|
269
|
-
try {
|
|
270
|
-
const candidates = await verifyExperimentRunIntegrity(directory, experiment);
|
|
271
|
-
verifyExperimentDecisionIntegrity(experiment);
|
|
272
|
-
candidate = candidates.find((entry) => entry.id === championId);
|
|
273
|
-
if (!candidate) {
|
|
274
|
-
throw new Error(`champion candidate '${championId}' not found`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch (error) {
|
|
278
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
279
|
-
process.stderr.write(`lux apply: experiment integrity check failed: ${message}\n`);
|
|
280
|
-
failWith(1);
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
await applyCandidate(experiment.subjectRoot, experiment.campaignSnapshot.subject, candidate);
|
|
284
|
-
process.stdout.write(`applied candidate ${candidate.id} to ${experiment.subjectRoot} (${candidate.sourceBytes} bytes)\n`);
|
|
285
|
-
});
|
|
286
|
-
program
|
|
287
|
-
.command("report <experimentDir>")
|
|
288
|
-
.description("render a PR-ready promotion report from a verified experiment record")
|
|
289
|
-
.option("--json", "emit the structured decision report as JSON")
|
|
290
|
-
.option("--out <path>", "write the report to a file instead of stdout")
|
|
291
|
-
.option("--force", "overwrite an existing output file")
|
|
292
|
-
.action(async (experimentDir, options) => {
|
|
293
|
-
const directory = resolve(experimentDir);
|
|
294
|
-
if (!(await ensureDirectory("report", directory)))
|
|
295
|
-
return;
|
|
296
|
-
const snapshot = await loadVerifiedExperimentDecisionSnapshot(directory);
|
|
297
|
-
const { experiment, report } = snapshot;
|
|
298
|
-
const rendered = options.json
|
|
299
|
-
? `${JSON.stringify(report, null, 2)}\n`
|
|
300
|
-
: `${formatExperimentDecisionMarkdown(report)}\n`;
|
|
301
|
-
if (!options.out) {
|
|
302
|
-
process.stdout.write(rendered);
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
const out = resolve(options.out);
|
|
306
|
-
const outputStats = await lstat(out).catch((error) => {
|
|
307
|
-
if (error.code === "ENOENT")
|
|
308
|
-
return null;
|
|
309
|
-
throw error;
|
|
401
|
+
const annotateCommand = async (runDir, options) => {
|
|
402
|
+
const directory = resolve(runDir);
|
|
403
|
+
if (!(await ensureDirectory("annotate", directory)))
|
|
404
|
+
failWith(1);
|
|
405
|
+
const parsedAssertions = options.assertion.map(parseAnnotationAssertion);
|
|
406
|
+
const assertionIds = parsedAssertions.map(([id]) => id);
|
|
407
|
+
if (new Set(assertionIds).size !== assertionIds.length) {
|
|
408
|
+
throw new Errors.ParseError({ message: "--assertion IDs must not be repeated" });
|
|
409
|
+
}
|
|
410
|
+
const assertions = Object.fromEntries(parsedAssertions);
|
|
411
|
+
if (options.label === undefined &&
|
|
412
|
+
options.score === undefined &&
|
|
413
|
+
Object.keys(assertions).length === 0) {
|
|
414
|
+
throw new Errors.ParseError({
|
|
415
|
+
message: "Provide --label, --score, or at least one --assertion",
|
|
310
416
|
});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const physicalParent = await realpath(dirname(out));
|
|
325
|
-
const finalPhysicalOut = resolve(physicalParent, basename(out));
|
|
326
|
-
assertReportDestination(finalPhysicalOut, physicalDirectory, physicalSubjectRoot, experiment.campaignSnapshot.subject.exclude);
|
|
327
|
-
await writeReportSafely(finalPhysicalOut, rendered, options.force ?? false);
|
|
328
|
-
process.stdout.write(`wrote experiment report to ${out}\n`);
|
|
329
|
-
});
|
|
330
|
-
program
|
|
331
|
-
.command("view [runDir]")
|
|
332
|
-
.description("list past runs, or show one run's detail when given a runDir")
|
|
333
|
-
.option("--runs-root <path>", "run-dir root (defaults to lux.config.ts)")
|
|
334
|
-
.option("--case <id>", "filter the run list to one case slug (list mode only)")
|
|
335
|
-
.option("--suites", "list logical suite runs instead of individual runs")
|
|
336
|
-
.option("--experiments", "list experiment runs instead of individual runs")
|
|
337
|
-
.option("--json", "emit machine-readable JSON")
|
|
338
|
-
.action(async (runDirArg, options) => {
|
|
339
|
-
if (runDirArg) {
|
|
340
|
-
const path = resolve(runDirArg);
|
|
341
|
-
if (!(await ensureDirectory("view", path)))
|
|
342
|
-
return;
|
|
343
|
-
if (isExperimentRun(path)) {
|
|
344
|
-
const experiment = await loadExperimentRun(path);
|
|
345
|
-
if (!experiment) {
|
|
346
|
-
process.stderr.write(`lux view: invalid experiment at ${path}\n`);
|
|
347
|
-
failWith(1);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
process.stdout.write(options.json
|
|
351
|
-
? `${JSON.stringify({ experimentDir: path, ...experiment }, null, 2)}\n`
|
|
352
|
-
: `${formatExperimentRun(path, experiment)}\n`);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const suite = await loadSuiteRun(path);
|
|
356
|
-
if (suite) {
|
|
357
|
-
process.stdout.write(options.json
|
|
358
|
-
? `${JSON.stringify({ suiteDir: path, ...suite }, null, 2)}\n`
|
|
359
|
-
: `${formatSuiteRunDetail(path, suite)}\n`);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const detail = await loadRunDetail(path);
|
|
363
|
-
if (options.json) {
|
|
364
|
-
process.stdout.write(`${JSON.stringify(detail, null, 2)}\n`);
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
process.stdout.write(`${formatRunDetail(detail)}\n`);
|
|
368
|
-
}
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const cwd = process.cwd();
|
|
372
|
-
const config = await loadLuxConfig(cwd);
|
|
373
|
-
const runsRoot = options.runsRoot ? resolve(options.runsRoot) : config.runsRoot;
|
|
374
|
-
if (options.experiments) {
|
|
375
|
-
const experiments = await listExperimentRuns(runsRoot);
|
|
376
|
-
process.stdout.write(options.json
|
|
377
|
-
? `${JSON.stringify(experiments, null, 2)}\n`
|
|
378
|
-
: `experiments at ${runsRoot}:\n${formatExperimentRunsTable(experiments)}\n`);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (options.suites) {
|
|
382
|
-
const suites = await listSuiteRuns(runsRoot);
|
|
383
|
-
process.stdout.write(options.json
|
|
384
|
-
? `${JSON.stringify(suites, null, 2)}\n`
|
|
385
|
-
: `suite runs at ${runsRoot}:\n${formatSuiteRunsTable(suites)}\n`);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
const all = await listRuns(runsRoot);
|
|
389
|
-
const runs = options.case ? all.filter((r) => r.caseSlug === options.case) : all;
|
|
390
|
-
if (options.json) {
|
|
391
|
-
process.stdout.write(`${JSON.stringify(runs, null, 2)}\n`);
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
if (!options.case) {
|
|
395
|
-
process.stdout.write(`suite runs at ${runsRoot}:\n`);
|
|
396
|
-
process.stdout.write(`${formatSuiteRunsTable(await listSuiteRuns(runsRoot))}\n\n`);
|
|
397
|
-
}
|
|
398
|
-
process.stdout.write(`runs at ${runsRoot}:\n`);
|
|
399
|
-
process.stdout.write(`${formatRunsTable(runs)}\n`);
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
program
|
|
403
|
-
.command("compare <runA> <runB>")
|
|
404
|
-
.description("diff two run or suite directories")
|
|
405
|
-
.option("--pass-rate-tolerance <fraction>", "allowed per-case pass-rate decrease for suite comparison", parseUnitInterval)
|
|
406
|
-
.action(async (runA, runB, options) => {
|
|
407
|
-
const pathA = resolve(runA);
|
|
408
|
-
const pathB = resolve(runB);
|
|
409
|
-
// Validate both up front so one typo'd path in CI cannot read as
|
|
410
|
-
// "0 regressed"; report every bad path, not just the first.
|
|
411
|
-
const okA = await ensureDirectory("compare", pathA);
|
|
412
|
-
const okB = await ensureDirectory("compare", pathB);
|
|
413
|
-
if (!okA || !okB)
|
|
414
|
-
return;
|
|
415
|
-
const [suiteA, suiteB] = await Promise.all([loadSuiteRun(pathA), loadSuiteRun(pathB)]);
|
|
416
|
-
if (Boolean(suiteA) !== Boolean(suiteB)) {
|
|
417
|
-
process.stderr.write("lux compare: both paths must be runs or both must be suites\n");
|
|
418
|
-
failWith(1);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
if (suiteA && suiteB) {
|
|
422
|
-
const comparison = await compareSuiteRuns(pathA, pathB, {
|
|
423
|
-
...(options.passRateTolerance !== undefined
|
|
424
|
-
? { passRateTolerance: options.passRateTolerance }
|
|
425
|
-
: {}),
|
|
417
|
+
}
|
|
418
|
+
const out = resolve(options.out);
|
|
419
|
+
const run = await loadFrozenRegradableRun(directory);
|
|
420
|
+
try {
|
|
421
|
+
const rubric = await loadRubricDefinition(await resolveAuthoredFile(options.rubric, "rubric"));
|
|
422
|
+
if (rubric.id !== run.case.id) {
|
|
423
|
+
throw new Error(`Rubric ID '${rubric.id}' does not match persisted case '${run.case.id}'.`);
|
|
424
|
+
}
|
|
425
|
+
const rubricIds = new Set(rubric.assertions.map((assertion) => assertion.id));
|
|
426
|
+
const unknownIds = assertionIds.filter((assertionId) => !rubricIds.has(assertionId));
|
|
427
|
+
if (unknownIds.length > 0) {
|
|
428
|
+
throw new Errors.ParseError({
|
|
429
|
+
message: `Unknown rubric assertion IDs: ${unknownIds.join(", ")}`,
|
|
426
430
|
});
|
|
427
|
-
process.stdout.write(`${formatSuiteComparisonText(comparison)}\n`);
|
|
428
|
-
if (hasSuiteRegression(comparison))
|
|
429
|
-
failWith(1);
|
|
430
|
-
return;
|
|
431
431
|
}
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
432
|
+
const annotationTags = [
|
|
433
|
+
...(options.split ? [options.split] : []),
|
|
434
|
+
...normalizeTags(options.tag),
|
|
435
|
+
].filter((tag, index, all) => all.indexOf(tag) === index);
|
|
436
|
+
const portableRunDir = (relative(dirname(out), directory) || ".").split(sep).join("/");
|
|
437
|
+
const id = options.id ??
|
|
438
|
+
`${run.case.id}-${basename(directory)}-${valueHash(portableRunDir).slice(0, 10)}`.replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
439
|
+
const annotation = GraderAnnotationSchema.parse({
|
|
440
|
+
id,
|
|
441
|
+
runDir: portableRunDir,
|
|
442
|
+
rubricPath: options.rubric,
|
|
443
|
+
tags: annotationTags,
|
|
444
|
+
source: options.source,
|
|
445
|
+
...(options.annotator ? { annotator: options.annotator } : {}),
|
|
446
|
+
labeledAt: new Date().toISOString(),
|
|
447
|
+
...(options.reviewer ? { reviewer: options.reviewer } : {}),
|
|
448
|
+
expected: {
|
|
449
|
+
...(options.label ? { casePassed: options.label === "pass" } : {}),
|
|
450
|
+
...(options.score !== undefined ? { caseScore: options.score } : {}),
|
|
451
|
+
assertions,
|
|
452
|
+
},
|
|
453
|
+
...(options.feedback ? { feedback: options.feedback } : {}),
|
|
454
|
+
});
|
|
455
|
+
const dataset = await upsertGraderAnnotation(out, annotation);
|
|
456
|
+
return { id: annotation.id, path: out, labels: dataset.annotations.length };
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
await releaseFrozenRegradableRun(run);
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const applyCommand = async (experimentDir, candidateId) => {
|
|
463
|
+
const directory = resolve(experimentDir);
|
|
464
|
+
const experiment = await loadExperimentRun(directory);
|
|
465
|
+
if (!experiment) {
|
|
466
|
+
throw new Errors.IncurError({
|
|
467
|
+
code: "EXPERIMENT_NOT_FOUND",
|
|
468
|
+
message: `Experiment manifest not found at ${directory}.`,
|
|
469
|
+
exitCode: 1,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (experiment.status !== "passed") {
|
|
473
|
+
throw new Errors.IncurError({
|
|
474
|
+
code: "EXPERIMENT_NOT_PROMOTED",
|
|
475
|
+
message: `Experiment status is '${experiment.status}', not 'passed'.`,
|
|
476
|
+
exitCode: 1,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
const championId = experiment.championCandidateId;
|
|
480
|
+
if (!championId) {
|
|
481
|
+
throw new Errors.IncurError({
|
|
482
|
+
code: "CHAMPION_NOT_FOUND",
|
|
483
|
+
message: "The passed experiment has no promoted champion.",
|
|
484
|
+
exitCode: 1,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
if (candidateId && candidateId !== championId) {
|
|
488
|
+
throw new Errors.IncurError({
|
|
489
|
+
code: "CANDIDATE_NOT_PROMOTED",
|
|
490
|
+
message: `Candidate '${candidateId}' is not the promoted champion '${championId}'.`,
|
|
491
|
+
exitCode: 1,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
let candidate;
|
|
495
|
+
try {
|
|
496
|
+
const candidates = await verifyExperimentRunIntegrity(directory, experiment);
|
|
497
|
+
verifyExperimentDecisionIntegrity(experiment);
|
|
498
|
+
candidate = candidates.find((entry) => entry.id === championId);
|
|
499
|
+
if (!candidate)
|
|
500
|
+
throw new Error(`Champion candidate '${championId}' not found.`);
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
throw new Errors.IncurError({
|
|
504
|
+
code: "EXPERIMENT_INTEGRITY_FAILED",
|
|
505
|
+
message: error instanceof Error ? error.message : String(error),
|
|
506
|
+
exitCode: 1,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
await applyCandidate(experiment.subjectRoot, experiment.campaignSnapshot.subject, candidate);
|
|
510
|
+
return {
|
|
511
|
+
candidateId: candidate.id,
|
|
512
|
+
subjectRoot: experiment.subjectRoot,
|
|
513
|
+
sourceBytes: candidate.sourceBytes,
|
|
514
|
+
};
|
|
515
|
+
};
|
|
516
|
+
const reportCommand = async (experimentDir, options) => {
|
|
517
|
+
const directory = resolve(experimentDir);
|
|
518
|
+
if (!(await ensureDirectory("report", directory)))
|
|
519
|
+
failWith(1);
|
|
520
|
+
const snapshot = await loadVerifiedExperimentDecisionSnapshot(directory);
|
|
521
|
+
const { experiment, report } = snapshot;
|
|
522
|
+
if (!options.out)
|
|
523
|
+
return report;
|
|
524
|
+
const out = resolve(options.out);
|
|
525
|
+
const outputStats = await lstat(out).catch((error) => {
|
|
526
|
+
if (error.code === "ENOENT")
|
|
527
|
+
return null;
|
|
528
|
+
throw error;
|
|
436
529
|
});
|
|
530
|
+
if (outputStats && (outputStats.isSymbolicLink() || !outputStats.isFile())) {
|
|
531
|
+
throw new Error("An existing --out must be a regular, non-symlink file.");
|
|
532
|
+
}
|
|
533
|
+
const [physicalDirectory, physicalSubjectRoot, physicalOut] = await Promise.all([
|
|
534
|
+
realpath(directory),
|
|
535
|
+
realpath(experiment.subjectRoot),
|
|
536
|
+
physicalDestinationPath(out),
|
|
537
|
+
]);
|
|
538
|
+
assertReportDestination(physicalOut, physicalDirectory, physicalSubjectRoot, experiment.campaignSnapshot.subject.exclude);
|
|
539
|
+
if (outputStats && !options.force) {
|
|
540
|
+
throw new Error(`Output already exists (use --force): ${out}`);
|
|
541
|
+
}
|
|
542
|
+
await mkdir(dirname(out), { recursive: true });
|
|
543
|
+
const physicalParent = await realpath(dirname(out));
|
|
544
|
+
const finalPhysicalOut = resolve(physicalParent, basename(out));
|
|
545
|
+
assertReportDestination(finalPhysicalOut, physicalDirectory, physicalSubjectRoot, experiment.campaignSnapshot.subject.exclude);
|
|
546
|
+
await writeReportSafely(finalPhysicalOut, `${formatExperimentDecisionMarkdown(report)}\n`, options.force ?? false);
|
|
547
|
+
return { path: out, report };
|
|
437
548
|
};
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
549
|
+
const compareCommand = async (runA, runB, options) => {
|
|
550
|
+
const pathA = resolve(runA);
|
|
551
|
+
const pathB = resolve(runB);
|
|
552
|
+
const invalid = (await Promise.all([pathA, pathB].map(async (path) => existsSync(path) && (await stat(path)).isDirectory() ? undefined : path))).filter((path) => path !== undefined);
|
|
553
|
+
if (invalid.length > 0) {
|
|
554
|
+
throw new Errors.IncurError({
|
|
555
|
+
code: "RUN_DIRECTORY_NOT_FOUND",
|
|
556
|
+
message: invalid.map((path) => `${path} is not a directory`).join("; "),
|
|
557
|
+
exitCode: 1,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
const [suiteA, suiteB] = await Promise.all([loadSuiteRun(pathA), loadSuiteRun(pathB)]);
|
|
561
|
+
if (Boolean(suiteA) !== Boolean(suiteB)) {
|
|
562
|
+
throw new Errors.IncurError({
|
|
563
|
+
code: "INCOMPATIBLE_RUN_TYPES",
|
|
564
|
+
message: "Both paths must be runs or both must be suites.",
|
|
565
|
+
exitCode: 1,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
if (suiteA && suiteB) {
|
|
569
|
+
const comparison = await compareSuiteRuns(pathA, pathB, {
|
|
570
|
+
...(options.passRateTolerance !== undefined
|
|
571
|
+
? { passRateTolerance: options.passRateTolerance }
|
|
572
|
+
: {}),
|
|
573
|
+
});
|
|
574
|
+
if (hasSuiteRegression(comparison)) {
|
|
575
|
+
throw new Errors.IncurError({
|
|
576
|
+
code: "REGRESSION_DETECTED",
|
|
577
|
+
message: formatSuiteComparisonText(comparison),
|
|
578
|
+
exitCode: 1,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return comparison;
|
|
582
|
+
}
|
|
583
|
+
const comparison = await compareRuns({ pathA, pathB });
|
|
584
|
+
if (hasRegression(comparison)) {
|
|
585
|
+
throw new Errors.IncurError({
|
|
586
|
+
code: "REGRESSION_DETECTED",
|
|
587
|
+
message: formatComparisonText(comparison),
|
|
588
|
+
exitCode: 1,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return comparison;
|
|
451
592
|
};
|
|
452
|
-
const caseIdOf = (casePath) => basename(casePath, extname(casePath));
|
|
453
593
|
const SIGINT_EXIT_CODE = 130;
|
|
454
594
|
const physicalDestinationPath = async (path) => {
|
|
455
595
|
const suffix = [];
|
|
@@ -512,250 +652,243 @@ const resolveAuthoredFile = async (value, label) => {
|
|
|
512
652
|
*/
|
|
513
653
|
const isFailure = (outcome) => outcome.exitReason !== "done" || outcome.casePassed !== true;
|
|
514
654
|
const firstLine = (message) => (message.split("\n")[0] ?? message).trim();
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
655
|
+
const experimentCommand = async (mode, campaignPath, options) => {
|
|
656
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
657
|
+
try {
|
|
658
|
+
const cwd = process.cwd();
|
|
659
|
+
const config = await loadLuxConfig(cwd);
|
|
660
|
+
const campaign = await resolveExperimentCampaignPath(config.rootDir, campaignPath);
|
|
661
|
+
const loaded = await loadExperimentCampaign(campaign);
|
|
662
|
+
experimentCampaignMode(campaign, loaded.campaign, mode);
|
|
663
|
+
const registry = buildRegistryFromConfig(config);
|
|
664
|
+
const controller = __addDisposableResource(env_2, abortOnInterrupt(), false);
|
|
665
|
+
const execution = {
|
|
666
|
+
config,
|
|
667
|
+
registry,
|
|
668
|
+
loaded,
|
|
669
|
+
...(options.runsRoot ? { runsRoot: resolve(options.runsRoot) } : {}),
|
|
670
|
+
...(options.resume ? { resumeFrom: resolve(options.resume) } : {}),
|
|
671
|
+
abortSignal: controller.signal,
|
|
672
|
+
};
|
|
673
|
+
if (options.plan) {
|
|
674
|
+
const plan = await planExperimentCampaign(execution, mode);
|
|
675
|
+
if (!plan.fitsProjectedCallBudgets) {
|
|
676
|
+
throw new Errors.IncurError({
|
|
677
|
+
code: "EXPERIMENT_BUDGET_EXCEEDED",
|
|
678
|
+
message: "The projected experiment work exceeds its configured call budgets.",
|
|
679
|
+
exitCode: 1,
|
|
680
|
+
});
|
|
536
681
|
}
|
|
537
|
-
|
|
682
|
+
return plan;
|
|
683
|
+
}
|
|
684
|
+
const result = mode === "optimize"
|
|
685
|
+
? await optimizeExperimentCampaign(execution)
|
|
686
|
+
: await runExperimentCampaign(execution);
|
|
687
|
+
if (controller.signal.aborted)
|
|
688
|
+
failWith(SIGINT_EXIT_CODE);
|
|
689
|
+
if (result.manifest.status !== "passed") {
|
|
690
|
+
throw new Errors.IncurError({
|
|
691
|
+
code: "EXPERIMENT_FAILED",
|
|
692
|
+
message: `Experiment ${result.manifest.experimentId} finished with status '${result.manifest.status}'.`,
|
|
693
|
+
exitCode: 1,
|
|
694
|
+
});
|
|
538
695
|
}
|
|
696
|
+
return { experimentDir: result.experimentDir, ...result.manifest };
|
|
697
|
+
}
|
|
698
|
+
catch (e_2) {
|
|
699
|
+
env_2.error = e_2;
|
|
700
|
+
env_2.hasError = true;
|
|
701
|
+
}
|
|
702
|
+
finally {
|
|
703
|
+
__disposeResources(env_2);
|
|
539
704
|
}
|
|
540
|
-
return { explicit, patterns };
|
|
541
705
|
};
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
byPath.set(c.path, c);
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
if (patterns.length > 0 || tags.length > 0) {
|
|
554
|
-
for (const c of selectCases(cases, { patterns, tags }))
|
|
555
|
-
byPath.set(c.path, c);
|
|
706
|
+
/** A profile pins one driver, so callers pass the project matrix only without one. */
|
|
707
|
+
const runCommand = async (filters, options, human = false) => {
|
|
708
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
709
|
+
try {
|
|
710
|
+
if (options.interactive && options.claudeAnswerer) {
|
|
711
|
+
throw new Errors.ParseError({
|
|
712
|
+
message: "--interactive and --claude-answerer are mutually exclusive",
|
|
713
|
+
});
|
|
556
714
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
715
|
+
const cwd = process.cwd();
|
|
716
|
+
const config = await loadLuxConfig(cwd);
|
|
717
|
+
const authoredProfile = options.profile ? config.profiles?.[options.profile] : undefined;
|
|
718
|
+
if (options.profile && !authoredProfile) {
|
|
719
|
+
const available = Object.keys(config.profiles ?? {}).sort();
|
|
720
|
+
throw new Errors.IncurError({
|
|
721
|
+
code: "PROFILE_NOT_FOUND",
|
|
722
|
+
message: `Unknown profile '${options.profile}'${available.length > 0 ? `; available: ${available.join(", ")}` : ""}.`,
|
|
723
|
+
exitCode: 1,
|
|
566
724
|
});
|
|
567
725
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (options.plan) {
|
|
598
|
-
const plan = await planExperimentCampaign(execution, mode);
|
|
599
|
-
let output;
|
|
600
|
-
if (options.json) {
|
|
601
|
-
output = `${JSON.stringify(plan, null, 2)}\n`;
|
|
726
|
+
const profile = resolveProfile(config, authoredProfile);
|
|
727
|
+
const harness = profile.harness;
|
|
728
|
+
const registry = registryWithHarness(buildRegistryFromConfig(config), config, harness);
|
|
729
|
+
const defaultDriver = profile?.driver ?? config.defaultDriver;
|
|
730
|
+
const orchestrator = new Orchestrator({
|
|
731
|
+
driverDefaults: config.driverDefaults,
|
|
732
|
+
environment: profile?.environment ?? config.environment,
|
|
733
|
+
registry,
|
|
734
|
+
defaultAnswerer: profile?.answerer ?? config.defaultAnswerer,
|
|
735
|
+
...(defaultDriver ? { defaultDriver } : {}),
|
|
736
|
+
...(harness ? { harness } : {}),
|
|
737
|
+
lifecycleFixtures: config.lifecycleFixtures,
|
|
738
|
+
});
|
|
739
|
+
const runsRoot = options.runsRoot ? resolve(options.runsRoot) : config.runsRoot;
|
|
740
|
+
const subjectRoot = options.subjectRoot ? resolve(options.subjectRoot) : config.subjectRoot;
|
|
741
|
+
// A config file is the user's opt-in to tree-wide discovery. Without one,
|
|
742
|
+
// stay scoped to the `cases/` scaffold: discovery imports every .ts it
|
|
743
|
+
// visits, and running module-level side effects across an arbitrary cwd
|
|
744
|
+
// must never be the default.
|
|
745
|
+
const root = config.casesRoot ?? (config.configPath ? config.rootDir : join(cwd, "cases"));
|
|
746
|
+
// A configured casesRoot is a path the user declared; its absence is a
|
|
747
|
+
// broken config, not an empty suite — never mask it behind "no cases
|
|
748
|
+
// matched" or a lucky explicit-file run.
|
|
749
|
+
if (config.casesRoot && !existsSync(config.casesRoot)) {
|
|
750
|
+
throw new Errors.IncurError({
|
|
751
|
+
code: "CASES_ROOT_NOT_FOUND",
|
|
752
|
+
message: `casesRoot does not exist: ${config.casesRoot}`,
|
|
753
|
+
exitCode: 1,
|
|
754
|
+
});
|
|
602
755
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
`
|
|
612
|
-
`
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
`proposal calls:${plan.projected.proposalCalls}/${plan.budget.maxProposalCalls}`,
|
|
616
|
-
`unpriced calls: observed at runtime (cap ${plan.budget.maxUnpricedModelCalls})`,
|
|
617
|
-
`projected work within budget: ${projectedStatus}`,
|
|
618
|
-
...plan.warnings.map((warning) => `warning: ${warning}`),
|
|
619
|
-
"",
|
|
620
|
-
].join("\n");
|
|
756
|
+
const { explicit, patterns } = partitionFilters(filters, cwd);
|
|
757
|
+
const tags = normalizeTags(options.tag ?? []);
|
|
758
|
+
const hasFilter = explicit.length > 0 || patterns.length > 0 || tags.length > 0;
|
|
759
|
+
// Explicitly named files need no discovery, so they run even without a
|
|
760
|
+
// project; anything else would need a tree walk with nowhere safe to walk.
|
|
761
|
+
if (config.configPath === null && !existsSync(root) && explicit.length === 0) {
|
|
762
|
+
throw new Errors.IncurError({
|
|
763
|
+
code: "PROJECT_NOT_INITIALIZED",
|
|
764
|
+
message: `No lux.config.ts or cases/ directory in ${cwd}.`,
|
|
765
|
+
hint: "Run `lux init` or name a case file directly.",
|
|
766
|
+
exitCode: 1,
|
|
767
|
+
});
|
|
621
768
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (options.interactive && options.claudeAnswerer) {
|
|
640
|
-
throw new Error("lux run: --interactive and --claude-answerer are mutually exclusive");
|
|
641
|
-
}
|
|
642
|
-
const cwd = process.cwd();
|
|
643
|
-
const config = await loadLuxConfig(cwd);
|
|
644
|
-
const profile = options.profile ? config.profiles?.[options.profile] : undefined;
|
|
645
|
-
if (options.profile && !profile) {
|
|
646
|
-
const available = Object.keys(config.profiles ?? {}).sort();
|
|
647
|
-
process.stderr.write(`lux: unknown profile '${options.profile}'${available.length > 0 ? `; available: ${available.join(", ")}` : ""}\n`);
|
|
648
|
-
failWith(1);
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
const harness = profile?.harness ?? config.harness;
|
|
652
|
-
const registry = buildRegistryFromConfig(config).withAssertion(createRubricAssertion(harness));
|
|
653
|
-
const defaultDriver = profile?.driver ?? config.defaultDriver;
|
|
654
|
-
const orchestrator = new Orchestrator({
|
|
655
|
-
registry,
|
|
656
|
-
defaultAnswerer: profile?.answerer ?? config.defaultAnswerer,
|
|
657
|
-
...(defaultDriver ? { defaultDriver } : {}),
|
|
658
|
-
...(harness ? { harness } : {}),
|
|
659
|
-
lifecycleFixtures: config.lifecycleFixtures,
|
|
660
|
-
});
|
|
661
|
-
const runsRoot = options.runsRoot ? resolve(options.runsRoot) : config.runsRoot;
|
|
662
|
-
const subjectRoot = options.subjectRoot ? resolve(options.subjectRoot) : config.subjectRoot;
|
|
663
|
-
// A config file is the user's opt-in to tree-wide discovery. Without one,
|
|
664
|
-
// stay scoped to the `cases/` scaffold: discovery imports every .ts it
|
|
665
|
-
// visits, and running module-level side effects across an arbitrary cwd
|
|
666
|
-
// must never be the default.
|
|
667
|
-
const root = config.casesRoot ?? (config.configPath ? config.rootDir : join(cwd, "cases"));
|
|
668
|
-
// A configured casesRoot is a path the user declared; its absence is a
|
|
669
|
-
// broken config, not an empty suite — never mask it behind "no cases
|
|
670
|
-
// matched" or a lucky explicit-file run.
|
|
671
|
-
if (config.casesRoot && !existsSync(config.casesRoot)) {
|
|
672
|
-
process.stderr.write(`lux: casesRoot does not exist: ${config.casesRoot}\n`);
|
|
673
|
-
failWith(1);
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
const { explicit, patterns } = partitionFilters(filters, cwd);
|
|
677
|
-
const tags = options.tag ?? [];
|
|
678
|
-
const hasFilter = explicit.length > 0 || patterns.length > 0 || tags.length > 0;
|
|
679
|
-
// Explicitly named files need no discovery, so they run even without a
|
|
680
|
-
// project; anything else would need a tree walk with nowhere safe to walk.
|
|
681
|
-
if (config.configPath === null && !existsSync(root) && explicit.length === 0) {
|
|
682
|
-
process.stderr.write(`lux: no lux.config.ts or cases/ directory in ${cwd} — run \`lux init\` to scaffold a project, or name a case file directly\n`);
|
|
683
|
-
failWith(1);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
const { cases, problems } = existsSync(root)
|
|
687
|
-
? await discoverCases(root)
|
|
688
|
-
: { cases: [], problems: [] };
|
|
689
|
-
for (const problem of problems) {
|
|
690
|
-
process.stderr.write(`lux: skipped ${problem.path}: ${firstLine(problem.reason)}\n`);
|
|
691
|
-
}
|
|
692
|
-
const selected = selectSuite(cases, { explicit, patterns }, tags);
|
|
693
|
-
// An empty selection fails every mode the same way — running, `--list`, and
|
|
694
|
-
// `--list-tags` all exit 1, so a typo'd filter can never read as a clean pass.
|
|
695
|
-
if (selected.length === 0) {
|
|
696
|
-
process.stderr.write(hasFilter
|
|
697
|
-
? "lux: no cases matched the given filters\n"
|
|
698
|
-
: `lux: no cases discovered under ${root}\n`);
|
|
699
|
-
failWith(1);
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
// List the tag vocabulary of the selected cases — "which --tag values exist?".
|
|
703
|
-
if (options.listTags) {
|
|
704
|
-
const counts = new Map();
|
|
705
|
-
for (const c of selected)
|
|
706
|
-
for (const t of c.tags)
|
|
707
|
-
counts.set(t, (counts.get(t) ?? 0) + 1);
|
|
708
|
-
for (const [tag, n] of [...counts].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
709
|
-
process.stdout.write(`${tag}\t${n}\n`);
|
|
769
|
+
const { cases, problems } = existsSync(root)
|
|
770
|
+
? await discoverCases(root)
|
|
771
|
+
: { cases: [], problems: [] };
|
|
772
|
+
for (const problem of problems) {
|
|
773
|
+
process.stderr.write(`lux: skipped ${problem.path}: ${firstLine(problem.reason)}\n`);
|
|
774
|
+
}
|
|
775
|
+
const selected = selectSuite(cases, { explicit, patterns }, tags);
|
|
776
|
+
// An empty selection fails every mode the same way — running, `--list`, and
|
|
777
|
+
// `--list-tags` all exit 1, so a typo'd filter can never read as a clean pass.
|
|
778
|
+
if (selected.length === 0) {
|
|
779
|
+
throw new Errors.IncurError({
|
|
780
|
+
code: "NO_CASES_MATCHED",
|
|
781
|
+
message: hasFilter
|
|
782
|
+
? "No cases matched the given filters."
|
|
783
|
+
: `No cases were discovered under ${root}.`,
|
|
784
|
+
exitCode: 1,
|
|
785
|
+
});
|
|
710
786
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
787
|
+
// List the tag vocabulary of the selected cases — "which --tag values exist?".
|
|
788
|
+
if (options.listTags) {
|
|
789
|
+
const counts = new Map();
|
|
790
|
+
for (const c of selected)
|
|
791
|
+
for (const t of c.tags)
|
|
792
|
+
counts.set(t, (counts.get(t) ?? 0) + 1);
|
|
793
|
+
const result = {
|
|
794
|
+
caseCount: selected.length,
|
|
795
|
+
tags: [...counts]
|
|
796
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
797
|
+
.map(([tag, count]) => ({ tag, count })),
|
|
798
|
+
};
|
|
799
|
+
if (human)
|
|
800
|
+
process.stdout.write(`${result.caseCount} cases\n${result.tags.map(({ tag, count }) => ` ${tag}: ${count}`).join("\n")}\n`);
|
|
801
|
+
return result;
|
|
802
|
+
}
|
|
803
|
+
const focus = {
|
|
804
|
+
driver: options.driver,
|
|
805
|
+
model: options.model,
|
|
806
|
+
effort: options.effort,
|
|
807
|
+
};
|
|
808
|
+
const base = await planSuiteItems(selected, {
|
|
809
|
+
driverDefaults: config.driverDefaults,
|
|
810
|
+
matrix: authoredProfile ? undefined : config.matrix,
|
|
811
|
+
defaultDriver,
|
|
812
|
+
registry,
|
|
813
|
+
focus,
|
|
814
|
+
});
|
|
815
|
+
// Collect-only: answer "what would run?" without spawning a single agent.
|
|
816
|
+
if (options.list) {
|
|
817
|
+
if (human)
|
|
818
|
+
process.stdout.write(`${base.length} cases\n${base.map(({ id, path }) => ` ${id} ${path}`).join("\n")}\n`);
|
|
819
|
+
return {
|
|
820
|
+
caseCount: base.length,
|
|
821
|
+
cases: base.map(({ path, id, caseId, permutationId }) => ({
|
|
822
|
+
path,
|
|
823
|
+
id,
|
|
824
|
+
...(caseId !== undefined ? { caseId } : {}),
|
|
825
|
+
...(permutationId !== undefined ? { permutationId } : {}),
|
|
826
|
+
})),
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
const loop = options.loop ?? 1;
|
|
830
|
+
const items = expandLoop(base, loop);
|
|
831
|
+
const { concurrency, note } = resolveConcurrency(options, config.concurrency);
|
|
832
|
+
if (note)
|
|
833
|
+
process.stderr.write(note);
|
|
834
|
+
const reporter = human ? new ConsoleReporter({ verbose: options.verbose ?? false }) : undefined;
|
|
835
|
+
const controller = __addDisposableResource(env_3, abortOnInterrupt(), false);
|
|
836
|
+
reporter?.begin({
|
|
837
|
+
title: suiteTitle(patterns, tags, explicit, loop, focus),
|
|
838
|
+
caseIds: items.map((i) => i.id),
|
|
738
839
|
runsRoot,
|
|
739
|
-
|
|
740
|
-
answererOverride: resolveAnswererOverride(options),
|
|
741
|
-
skipJudge: options.skipJudge,
|
|
742
|
-
observer: reporter,
|
|
743
|
-
abortSignal: controller.signal,
|
|
744
|
-
concurrency,
|
|
745
|
-
subjectRoot,
|
|
746
|
-
profileId: options.profile,
|
|
840
|
+
loop,
|
|
747
841
|
});
|
|
748
|
-
results =
|
|
749
|
-
|
|
842
|
+
let results = [];
|
|
843
|
+
try {
|
|
844
|
+
const suite = await orchestrator.runSuite(items, {
|
|
845
|
+
runsRoot,
|
|
846
|
+
fixturesRoot: config.fixturesRoot,
|
|
847
|
+
answererOverride: resolveAnswererOverride(options),
|
|
848
|
+
skipJudge: options.skipJudge,
|
|
849
|
+
...(reporter ? { observer: reporter } : {}),
|
|
850
|
+
abortSignal: controller.signal,
|
|
851
|
+
concurrency,
|
|
852
|
+
subjectRoot,
|
|
853
|
+
profileId: options.profile,
|
|
854
|
+
});
|
|
855
|
+
results = suite.results;
|
|
856
|
+
reporter?.setSuiteDir(suite.suiteDir);
|
|
857
|
+
if (controller.signal.aborted)
|
|
858
|
+
failWith(SIGINT_EXIT_CODE);
|
|
859
|
+
if (results.some(isFailure)) {
|
|
860
|
+
throw new Errors.IncurError({
|
|
861
|
+
code: "SUITE_FAILED",
|
|
862
|
+
message: `${results.filter(isFailure).length} of ${results.length} case attempts failed. Suite: ${suite.suiteDir}`,
|
|
863
|
+
exitCode: 1,
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
suiteDir: suite.suiteDir,
|
|
868
|
+
results: results.map((result) => ({
|
|
869
|
+
caseId: result.caseId,
|
|
870
|
+
runDir: result.runDir ?? null,
|
|
871
|
+
exitReason: result.exitReason,
|
|
872
|
+
casePassed: result.casePassed,
|
|
873
|
+
caseScore: result.result?.grading?.caseScore ?? null,
|
|
874
|
+
assertionsPassed: result.result?.grading?.assertions.filter((assertion) => assertion.passed).length ?? 0,
|
|
875
|
+
assertionsTotal: result.result?.grading?.assertions.filter((assertion) => assertion.passed !== null)
|
|
876
|
+
.length ?? 0,
|
|
877
|
+
})),
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
finally {
|
|
881
|
+
reporter?.finish();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
catch (e_3) {
|
|
885
|
+
env_3.error = e_3;
|
|
886
|
+
env_3.hasError = true;
|
|
750
887
|
}
|
|
751
888
|
finally {
|
|
752
|
-
|
|
889
|
+
__disposeResources(env_3);
|
|
753
890
|
}
|
|
754
|
-
if (controller.signal.aborted)
|
|
755
|
-
failWith(SIGINT_EXIT_CODE);
|
|
756
|
-
else if (results.some(isFailure))
|
|
757
|
-
failWith(1);
|
|
758
891
|
};
|
|
759
892
|
// Internal exports for testing
|
|
760
|
-
export {
|
|
893
|
+
export { isFailure, resolveConcurrency };
|
|
761
894
|
//# sourceMappingURL=program.js.map
|