Vally 0.6.0
Released on 2026-06-11.
Breaking
- Removed
LlmClient.chat(),LlmChatOptions,LlmResponse,chatAndExtractJson, andextractJsonfrom the public API — all LLM judge calls now useLlmClient.judge()exclusively. PromptGraderandPairwiseGraderconstructors no longer acceptRetryOptions(unused — no caller or config surface ever set it).- Removed
withRetryfrom the public API (no longer used by either LLM grader after the tool-call refactor; the function remains internal-only). - Reporter API rebuilt around the new
PlanReporterinterface. All four built-in reporters (EvalConsoleReporter,EvalJsonlReporter,EvalMarkdownReporter,EvalJunitReporter) andCompositeEvalReporterwere rewritten in place; the legacyBufferedEvalReporter,StringWritable, andEvalReporterRegistrytypes were removed. The following public exports were also removed:EvalReporter,EvalRunStartInfo,EvalCompleteInfo,EvalReporterRegistration,RegisteredReporter,ReporterMutex, andcreateEvalReporterRegistry. - Eval pipeline rebuilt around concurrent trials:
runMatchedEvalandrunMultiTrialwere removed, as were the legacy orchestrator (packages/core/src/pipeline/orchestrate.ts) and the multi-trial module (packages/core/src/pipeline/multi-trial.ts), including their exportedcomputeMultiTrialVerdictanddecomposeMultiTrialOutcomeshelpers. The associated public typesRunEvalOptions,RunEvalResult,StimuliExecutionResult,StimulusExecutionContext,MultiTrialOptions, andTrialProgressEventwere also removed. - JSONL output:
TrialResultRecordand the per-eval entries of therun-summaryrecord now include a requiredvariant: stringfield. Plainvally evalruns use a default variant name; experiment runs carry the configured variant name. Downstream consumers that parse the JSONL stream should expect this field on every line. vally-server: theoutcomestable gainedexperiment_name,experiment_run_id,variant,eval_file,eval_hash, andconfig_hashcolumns plus two indexes (idx_outcomes_experiment,idx_outcomes_variant).SCHEMA_VERSIONbumped to2. There is no migration path — pre-existing SQLite databases must be deleted and re-ingested.- CLI: Removed
vally eval --keep-executor-session-logs; executor session logs are now preserved by default under the run output directory. - Removed the unused
onDiagnosticcallback fromEvalRunOptions(public API).runEvalno longer emitted diagnostics through it; callers that set it can drop the option. - Skills are no longer auto-discovered from the working directory. Specify skills for a run either in an eval file via
environment.skills(paths relative to the eval file), or for a wholevally evalrun via the opt-in--skill-dir <path>flag. Whenenvironment.skillsis set it fully replaces the--skill-dirbase (replace, not merge), andvally experiment runnever auto-discovers skills, so baseline-vs-skilled A/B comparisons stay hermetic. Paths that are missing, not a directory, lack aSKILL.md, or contain an unparseableSKILL.mdnow fail the run fast instead of being silently skipped.
Features
- New
vally experiment runCLI command for declarative multi-variant evaluation. Reads an*.experiment.yamlfile that declares the eval set, the varying dimensions (typically/environment/skills), and the named variants; runs every(eval × variant)combination through a single shared worker pool; emits per-variantresults.jsonl+run-summary.jsonlplus an experiment-level markdown report. vally experiment run --dry-runresolves the experiment and prints the plan (one row per(variant × eval)) without executing trials.- Console and markdown reporters now surface the variant name in headings and the progress table when a plan spans more than one variant. Single-variant runs (every plain
vally evalinvocation) are unchanged. - CLI:
vally evalnow accepts--shutdown-timeout <duration>(e.g.5s,500ms; default5000ms) to bound how long cleanup waits for executors to shut down, including on Ctrl+C. Previously this window was hardcoded to 5 seconds. (#112) tool-callsgrader: added asequencefield that asserts tools were called in a given relative order (an ordered subsequence). Other tool calls may be interleaved between the sequence entries. Entries use the sameToolMatch | stringform asrequired/disallowed, and can be combined with them. (#452)tool-callsgrader: addedat_step/before_steppositional constraints torequiredentries, asserting that a tool was invoked in a specific agent step (at_step: N) or within the first N steps (before_step: N). Steps are counted byturn_startevents; trajectories without turn boundaries collapse to a single step0.tool-callsgrader: extendedToolMatchwithmin_count(require at least N matching calls on arequiredentry),args(regex matching against arbitrary call arguments, not justcommand/path),result(regex matching against the tool’s output/observation, onrequired/disallowedentries), andfinal(assert the matching call is the final tool call). (#479)
Fixed
-
CLI:
vally evalnow handlesSIGINT/SIGTERM(Ctrl+C) gracefully — it shuts down executors, flushes partial JSONL output, and finalizes partial markdown/JUnit reports before exiting (code 130 onSIGINT) instead of being torn down by Node’s default action mid-run. A second Ctrl+C forces an immediate exit. (#112) -
LLM judges (
prompt,pairwise) now deliver verdicts via asubmit_grade/submit_pairwise_gradetool call instead of parsing JSON out of model prose, eliminating intermittent “No valid JSON object found in LLM response” failures. (#345) -
Grader validation error messages now include the grader’s
namewhen set or itstype, if it has noname. -
Eval loader now rejects blank or whitespace-only grader
namevalues with a clear error instead of accepting them. -
tool-callsgrader: a danglingtool_call(no matchingtool_result, e.g. a truncated run) now only errors when asequenceis configured —required/disallowed-only configs grade such partial runs normally instead of throwing. -
CLI:
vally evalnow utilizes all available workers to run trials. -
vally experiment run: the experiment-relative eval path recorded inresults.jsonl/run-summary.jsonl(experiment.evalFile) is now computed against the canonicalized experiment directory, so symlinked or temp-dir checkouts no longer produce../private/var/…-style paths.
Changed
-
Server ingest now emits the
VALLY_LEGACY_JSONLdeprecation warning for legacy JSONL input. -
defaults.timeoutin an eval spec is now honored as the per-stimulus timeout when--timeoutis not passed on the command line. Previously the field was accepted and validated but never applied, so the eval always fell back to the built-in default. The CLI--timeoutflag still takes precedence over the spec default. -
run-commandgraders now continue evaluating configured stdout/stderr assertions against partial output when a command times out or exceedsmaxBuffer. -
copilot-sdkexecutor now aborts in-flight backend work before disconnecting when a run fails or times out, instead of only disconnecting (which left the backend request running). Both the abort and disconnect are best-effort and individually time-bounded so a wedged connection can’t block teardown. (#112) -
vally gradecan now consumevally eval --skip-grade --output jsonloutput again.EvalJsonlReporternow always emits explicitgradeResultandtrajectorykeys (asnullwhen absent) on everytrial-resultline, so ungraded success records are validEvalOutcomeshapes and round-trip through the documented “grade later” workflow instead of failing withstdin did not match a recognized shape. (#492) -
Harbor exporter: generated Dockerfiles now install Node.js and the vally CLI by default, requiring a Debian/Ubuntu-family base image (apt-get). Pass
installRuntime=falseto skip the install block when using a base image that already provides the runtime.