@skill-harness/core 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/provider-failure.js +12 -1
- package/dist/run.js +10 -6
- package/package.json +2 -2
package/dist/provider-failure.js
CHANGED
|
@@ -37,7 +37,18 @@ export function withExecutionFailure(transcript, failure) {
|
|
|
37
37
|
return failure ? `${EXECUTION_FAILURE_MARKER} ${failure}\n\n${transcript}` : transcript;
|
|
38
38
|
}
|
|
39
39
|
export function executionFailureFromTranscript(transcript) {
|
|
40
|
-
|
|
40
|
+
const failure = failureFromPreamble(transcript, EXECUTION_FAILURE_MARKER);
|
|
41
|
+
if (failure !== null)
|
|
42
|
+
return failure;
|
|
43
|
+
// Before 0.25.1, the runner saved thrown errors as this entire transcript,
|
|
44
|
+
// without a turn header. Match only its exact opening preamble: a model quote
|
|
45
|
+
// after a USER header (or elsewhere in the transcript) carries no authority.
|
|
46
|
+
const legacyPrefix = "[adapter failure] ";
|
|
47
|
+
if (transcript.startsWith(legacyPrefix)) {
|
|
48
|
+
const detail = transcript.slice(legacyPrefix.length).split("\n", 1)[0].trim();
|
|
49
|
+
return `adapter failure — ${detail || "adapter threw without a message"}`;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
43
54
|
* The adapters' shared turn-header prefix (`>>> USER:` / `>>> USER (turn n/m):`),
|
package/dist/run.js
CHANGED
|
@@ -16,7 +16,7 @@ import { judgeOneRep } from "./regrade.js";
|
|
|
16
16
|
import { runDeliveryCanary, canaryFailure } from "./canary.js";
|
|
17
17
|
import { boundaryCells, stabilityNote } from "./stability.js";
|
|
18
18
|
import { aggregateMetrics } from "./metrics.js";
|
|
19
|
-
import { providerFailureFromTranscript, executionFailureFromTranscript } from "./provider-failure.js";
|
|
19
|
+
import { providerFailureFromTranscript, executionFailureFromTranscript, withExecutionFailure } from "./provider-failure.js";
|
|
20
20
|
/**
|
|
21
21
|
* The pi-daddy arm's delivery proof, by convention: `arms.yaml` points
|
|
22
22
|
* `PI_GRANTS_LEDGER` at exactly this filename inside the run directory. A fixed
|
|
@@ -372,7 +372,7 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
372
372
|
// Configuration errors do not arrive here: the two that used to be
|
|
373
373
|
// thrown from inside this block (adapter cannot produce traces for a
|
|
374
374
|
// gated scenario) are decided above, before the loop.
|
|
375
|
-
adapterFailure = e instanceof Error ? e.message : String(e);
|
|
375
|
+
adapterFailure = (e instanceof Error ? e.message : String(e)) || "adapter threw without a message";
|
|
376
376
|
transcript = `[adapter failure] ${adapterFailure}`;
|
|
377
377
|
// Nothing partial from the failed attempt may be read as evidence: an
|
|
378
378
|
// empty trace list satisfies a `forbid_calls` gate, and a stale diff
|
|
@@ -384,7 +384,7 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
384
384
|
// A provider outage is infrastructure, never a model verdict. Checked on
|
|
385
385
|
// every path: the text path carries the marker in the transcript, the
|
|
386
386
|
// structured path sets `providerFailure` above and exits 0 while doing it.
|
|
387
|
-
if (!infrastructureFailure) {
|
|
387
|
+
if (!infrastructureFailure && adapterFailure === null) {
|
|
388
388
|
const provider = providerFailureFromTranscript(transcript);
|
|
389
389
|
if (provider)
|
|
390
390
|
infrastructureFailure = `provider failure — ${provider}`;
|
|
@@ -394,7 +394,8 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
394
394
|
infrastructureFailure = `execution failure — ${execution}`;
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
|
-
|
|
397
|
+
// Thrown errors keep their one fresh retry, unlike a returned unavailable final.
|
|
398
|
+
executionUnavailable ||= adapterFailure === null && executionFailureFromTranscript(transcript) !== null;
|
|
398
399
|
// Qualified complete traces prove delivered bytes even if those bytes
|
|
399
400
|
// are only whitespace; the legacy text-only timeout heuristic cannot erase them.
|
|
400
401
|
const deliveredText = traces.length > 0 && traces.every((trace) => trace.final_status === "complete" && trace.final_text.length > 0 && !trace.capture_errors?.length);
|
|
@@ -405,8 +406,11 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
405
406
|
}
|
|
406
407
|
// Survived the retry: ERROR for this rep, inside a run that still completes
|
|
407
408
|
// and still writes every other scenario's verdict.
|
|
408
|
-
if (adapterFailure
|
|
409
|
-
infrastructureFailure
|
|
409
|
+
if (adapterFailure) {
|
|
410
|
+
infrastructureFailure ??= `adapter failure — ${adapterFailure}`;
|
|
411
|
+
// Persist only the exhausted failure. A recovered retry must retain its
|
|
412
|
+
// clean transcript; later grade/regate must never judge an unavailable run.
|
|
413
|
+
transcript = withExecutionFailure(transcript, `adapter failure — ${adapterFailure}`);
|
|
410
414
|
}
|
|
411
415
|
}
|
|
412
416
|
const repSuffix = repCount > 1 ? rep : undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skill-harness/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "skill-harness engine
|
|
3
|
+
"version": "0.26.0",
|
|
4
|
+
"description": "skill-harness engine \u2014 spec, discover, run, LLM-judge grade, score, results (internal API)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/index.js",
|