skill-harness 0.23.0 → 0.24.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/README.md +4 -0
- package/dist/index.js +144 -31
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ spec it will run each scenario, LLM-judge the transcript, score it against a shi
|
|
|
6
6
|
bar, and open an interactive review UI so you can measure a `SKILL.md` edit.
|
|
7
7
|
Subject, judge, and spec-drafting model calls all run through Pi.
|
|
8
8
|
|
|
9
|
+
Fresh subject execution requires **exactly Pi 1.0.4**. Different or unavailable
|
|
10
|
+
versions produce execution ERROR before a subject call. Retained results remain
|
|
11
|
+
readable; the historical Pi 0.83.0 raw parser is not current qualification.
|
|
12
|
+
|
|
9
13
|
## Install
|
|
10
14
|
|
|
11
15
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -5836,6 +5836,11 @@ function redactValue(value, homeDir, depth) {
|
|
|
5836
5836
|
}
|
|
5837
5837
|
|
|
5838
5838
|
// packages/core/dist/execution-trace.js
|
|
5839
|
+
var QUALIFIED_PI_VERSION = "1.0.4";
|
|
5840
|
+
var LEGACY_PI_TRACE_VERSION = "0.83.0";
|
|
5841
|
+
function piQualificationFailure(version) {
|
|
5842
|
+
return version === QUALIFIED_PI_VERSION ? null : `Pi ${version ?? "(version unavailable)"} is unqualified; execution requires exact Pi ${QUALIFIED_PI_VERSION}`;
|
|
5843
|
+
}
|
|
5839
5844
|
var SKIPPED = /* @__PURE__ */ new Set(["message_update", "tool_execution_update"]);
|
|
5840
5845
|
var MAX_DETAILS_CHARS = 2e3;
|
|
5841
5846
|
function parseTrace(lines, meta) {
|
|
@@ -5846,6 +5851,13 @@ function parseTrace(lines, meta) {
|
|
|
5846
5851
|
let completionCounter = 0;
|
|
5847
5852
|
let malformedLines = 0;
|
|
5848
5853
|
let sawTerminal = false;
|
|
5854
|
+
const settledContract = meta.piVersion !== LEGACY_PI_TRACE_VERSION;
|
|
5855
|
+
const qualificationFailure = settledContract ? piQualificationFailure(meta.piVersion) : null;
|
|
5856
|
+
let sawSettled = false;
|
|
5857
|
+
let lastStopReason;
|
|
5858
|
+
let currentAssistantText = "";
|
|
5859
|
+
let eligibleFinal = false;
|
|
5860
|
+
const captureErrors = [];
|
|
5849
5861
|
let finalText = "";
|
|
5850
5862
|
let lastAssistantText = "";
|
|
5851
5863
|
let inputTokens = null;
|
|
@@ -5865,13 +5877,35 @@ function parseTrace(lines, meta) {
|
|
|
5865
5877
|
malformedLines++;
|
|
5866
5878
|
continue;
|
|
5867
5879
|
}
|
|
5880
|
+
if (!ev || typeof ev !== "object" || Array.isArray(ev)) {
|
|
5881
|
+
malformedLines++;
|
|
5882
|
+
continue;
|
|
5883
|
+
}
|
|
5868
5884
|
const type2 = ev.type;
|
|
5869
|
-
if (typeof type2 !== "string"
|
|
5885
|
+
if (typeof type2 !== "string") {
|
|
5886
|
+
malformedLines++;
|
|
5887
|
+
continue;
|
|
5888
|
+
}
|
|
5889
|
+
if (settledContract && ["agent_start", "turn_start", "message_start", "message_update", "message_end", "tool_execution_start", "tool_execution_end"].includes(type2)) {
|
|
5890
|
+
sawSettled = false;
|
|
5891
|
+
if (type2 !== "tool_execution_end") {
|
|
5892
|
+
currentAssistantText = "";
|
|
5893
|
+
lastStopReason = void 0;
|
|
5894
|
+
eligibleFinal = false;
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
if (SKIPPED.has(type2))
|
|
5870
5898
|
continue;
|
|
5871
5899
|
if (type2 === "tool_execution_start") {
|
|
5872
5900
|
const id = str2(ev.toolCallId);
|
|
5873
|
-
if (!id)
|
|
5901
|
+
if (!id) {
|
|
5902
|
+
captureErrors.push("tool start without an identity");
|
|
5903
|
+
continue;
|
|
5904
|
+
}
|
|
5905
|
+
if (calls.has(id)) {
|
|
5906
|
+
captureErrors.push("duplicate tool start identity");
|
|
5874
5907
|
continue;
|
|
5908
|
+
}
|
|
5875
5909
|
calls.set(id, {
|
|
5876
5910
|
id,
|
|
5877
5911
|
name: str2(ev.toolName) ?? "(unknown)",
|
|
@@ -5889,11 +5923,19 @@ function parseTrace(lines, meta) {
|
|
|
5889
5923
|
}
|
|
5890
5924
|
if (type2 === "tool_execution_end") {
|
|
5891
5925
|
const id = str2(ev.toolCallId);
|
|
5892
|
-
if (!id)
|
|
5926
|
+
if (!id) {
|
|
5927
|
+
captureErrors.push("tool end without an identity");
|
|
5893
5928
|
continue;
|
|
5929
|
+
}
|
|
5894
5930
|
const call = calls.get(id);
|
|
5895
|
-
if (!call)
|
|
5931
|
+
if (!call) {
|
|
5932
|
+
captureErrors.push("tool end without a matching start");
|
|
5933
|
+
continue;
|
|
5934
|
+
}
|
|
5935
|
+
if (call.completionIndex >= 0) {
|
|
5936
|
+
captureErrors.push("duplicate tool completion identity");
|
|
5896
5937
|
continue;
|
|
5938
|
+
}
|
|
5897
5939
|
if (call.completionIndex < 0)
|
|
5898
5940
|
activeCalls = Math.max(0, activeCalls - 1);
|
|
5899
5941
|
call.completionIndex = completionCounter++;
|
|
@@ -5906,7 +5948,17 @@ function parseTrace(lines, meta) {
|
|
|
5906
5948
|
if (type2 === "message_end") {
|
|
5907
5949
|
sawTerminal = true;
|
|
5908
5950
|
const msg = ev.message;
|
|
5909
|
-
|
|
5951
|
+
if (!msg || typeof msg !== "object" || Array.isArray(msg) || typeof msg.role !== "string") {
|
|
5952
|
+
captureErrors.push("malformed message_end message");
|
|
5953
|
+
continue;
|
|
5954
|
+
}
|
|
5955
|
+
if (msg.role !== "assistant" && msg.role !== "toolResult")
|
|
5956
|
+
continue;
|
|
5957
|
+
if (!Array.isArray(msg.content) || msg.content.some((block) => !block || typeof block !== "object" || Array.isArray(block) || typeof block.type !== "string" || block.type === "text" && typeof block.text !== "string")) {
|
|
5958
|
+
captureErrors.push("malformed message_end content");
|
|
5959
|
+
continue;
|
|
5960
|
+
}
|
|
5961
|
+
const at = isoTime(msg.timestamp);
|
|
5910
5962
|
if (msg?.role === "assistant" && at) {
|
|
5911
5963
|
for (const block of msg.content ?? []) {
|
|
5912
5964
|
if (block.type !== "toolCall" || typeof block.id !== "string")
|
|
@@ -5926,6 +5978,9 @@ function parseTrace(lines, meta) {
|
|
|
5926
5978
|
if (msg?.role !== "assistant")
|
|
5927
5979
|
continue;
|
|
5928
5980
|
const text = assistantText(msg);
|
|
5981
|
+
eligibleFinal = msg.content.every((block) => block.type === "text" || block.type === "thinking");
|
|
5982
|
+
currentAssistantText = text;
|
|
5983
|
+
lastStopReason = msg.stopReason;
|
|
5929
5984
|
if (text) {
|
|
5930
5985
|
lastAssistantText = text;
|
|
5931
5986
|
if (msg.stopReason === "stop")
|
|
@@ -5939,10 +5994,19 @@ function parseTrace(lines, meta) {
|
|
|
5939
5994
|
}
|
|
5940
5995
|
if (type2 === "turn_end" || type2 === "agent_end" || type2 === "agent_settled") {
|
|
5941
5996
|
sawTerminal = true;
|
|
5997
|
+
if (type2 === "agent_settled")
|
|
5998
|
+
sawSettled = true;
|
|
5942
5999
|
continue;
|
|
5943
6000
|
}
|
|
5944
6001
|
}
|
|
5945
6002
|
const toolCalls = [...calls.values()].sort((a, b) => a.issueIndex - b.issueIndex);
|
|
6003
|
+
if (malformedLines > 0)
|
|
6004
|
+
captureErrors.push(`pi JSONL contained ${malformedLines} malformed line(s); trace evidence is incomplete`);
|
|
6005
|
+
const finalStatus = qualificationFailure ? "unqualified" : !sawSettled || activeCalls > 0 ? "incomplete" : captureErrors.length > 0 ? "unavailable" : lastStopReason === "error" ? "error" : lastStopReason === "aborted" ? "aborted" : lastStopReason === "length" ? "truncated" : lastStopReason === "stop" && eligibleFinal && currentAssistantText.length > 0 ? "complete" : "unavailable";
|
|
6006
|
+
if (settledContract && finalStatus !== "complete") {
|
|
6007
|
+
const reason = qualificationFailure ?? (finalStatus === "incomplete" ? "current settlement is missing or tool calls remain outstanding" : finalStatus === "truncated" ? "settled assistant reached its output length limit" : finalStatus === "error" || finalStatus === "aborted" ? `settled assistant stop reason is ${lastStopReason}` : "current terminal assistant content is empty, ineligible, or captured evidence is malformed");
|
|
6008
|
+
captureErrors.push(`Pi ${meta.piVersion ?? "(version unavailable)"} final delivery is ${finalStatus}; ${reason}`);
|
|
6009
|
+
}
|
|
5946
6010
|
const metrics = {
|
|
5947
6011
|
input_tokens: inputTokens,
|
|
5948
6012
|
output_tokens: outputTokens,
|
|
@@ -5970,7 +6034,9 @@ function parseTrace(lines, meta) {
|
|
|
5970
6034
|
// and `smoke-real-pi.sh` asserts no `/home/` survives into a persisted trace
|
|
5971
6035
|
// — an assertion that used to pass only because the smoke model happened not
|
|
5972
6036
|
// to echo one.
|
|
5973
|
-
final_text: redactText(finalText || lastAssistantText, meta.homeDir),
|
|
6037
|
+
final_text: redactText(settledContract ? currentAssistantText : finalText || lastAssistantText, meta.homeDir),
|
|
6038
|
+
...settledContract ? { final_status: finalStatus } : {},
|
|
6039
|
+
...captureErrors.length ? { capture_errors: [...new Set(captureErrors)] } : {},
|
|
5974
6040
|
tool_calls: toolCalls,
|
|
5975
6041
|
// `null`, not `[]`: the stream says nothing about the filesystem. The runner
|
|
5976
6042
|
// overwrites this after observing the workspace. Defaulting to `[]` claimed
|
|
@@ -5982,13 +6048,13 @@ function parseTrace(lines, meta) {
|
|
|
5982
6048
|
metrics
|
|
5983
6049
|
};
|
|
5984
6050
|
trace.trace_sha256 = traceSha256(trace);
|
|
5985
|
-
return { trace, isComplete: sawTerminal, malformedLines };
|
|
6051
|
+
return { trace, isComplete: settledContract ? !qualificationFailure && sawSettled && activeCalls === 0 : sawTerminal, malformedLines };
|
|
5986
6052
|
}
|
|
5987
6053
|
function addReported(current, value) {
|
|
5988
6054
|
return typeof value === "number" && value > 0 ? (current ?? 0) + value : current;
|
|
5989
6055
|
}
|
|
5990
6056
|
function assistantText(msg) {
|
|
5991
|
-
return (msg.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("
|
|
6057
|
+
return (msg.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
5992
6058
|
}
|
|
5993
6059
|
function resultMeta(result, homeDir) {
|
|
5994
6060
|
const body = JSON.stringify(result?.content ?? result ?? null);
|
|
@@ -6124,6 +6190,15 @@ import { join as join10 } from "node:path";
|
|
|
6124
6190
|
|
|
6125
6191
|
// packages/core/dist/provider-failure.js
|
|
6126
6192
|
var PROVIDER_FAILURE_MARKER = "[skill-harness] provider failure:";
|
|
6193
|
+
var EXECUTION_FAILURE_MARKER = "[skill-harness] execution failure:";
|
|
6194
|
+
function withExecutionFailure(transcript, failure) {
|
|
6195
|
+
return failure ? `${EXECUTION_FAILURE_MARKER} ${failure}
|
|
6196
|
+
|
|
6197
|
+
${transcript}` : transcript;
|
|
6198
|
+
}
|
|
6199
|
+
function executionFailureFromTranscript(transcript) {
|
|
6200
|
+
return failureFromPreamble(transcript, EXECUTION_FAILURE_MARKER);
|
|
6201
|
+
}
|
|
6127
6202
|
var TURN_HEADER_PREFIX = ">>> ";
|
|
6128
6203
|
function withProviderFailure(transcript, failure) {
|
|
6129
6204
|
return failure ? `${PROVIDER_FAILURE_MARKER} ${failure}
|
|
@@ -6154,11 +6229,14 @@ function providerFailureFromJsonLine(line) {
|
|
|
6154
6229
|
return null;
|
|
6155
6230
|
}
|
|
6156
6231
|
function providerFailureFromTranscript(transcript) {
|
|
6232
|
+
return failureFromPreamble(transcript, PROVIDER_FAILURE_MARKER);
|
|
6233
|
+
}
|
|
6234
|
+
function failureFromPreamble(transcript, marker) {
|
|
6157
6235
|
for (const line of transcript.split("\n")) {
|
|
6158
6236
|
if (line.startsWith(TURN_HEADER_PREFIX))
|
|
6159
6237
|
return null;
|
|
6160
|
-
if (line.startsWith(
|
|
6161
|
-
return line.slice(
|
|
6238
|
+
if (line.startsWith(marker))
|
|
6239
|
+
return line.slice(marker.length).trim();
|
|
6162
6240
|
}
|
|
6163
6241
|
return null;
|
|
6164
6242
|
}
|
|
@@ -6183,8 +6261,9 @@ async function judgeOneRep(opts) {
|
|
|
6183
6261
|
const startedAt = performance.now();
|
|
6184
6262
|
const repField = rep === void 0 ? {} : { rep };
|
|
6185
6263
|
const providerFailure = providerFailureFromTranscript(transcript);
|
|
6186
|
-
|
|
6187
|
-
|
|
6264
|
+
const executionFailure = executionFailureFromTranscript(transcript);
|
|
6265
|
+
if (providerFailure || executionFailure) {
|
|
6266
|
+
const reason = providerFailure ? `provider failure \u2014 ${providerFailure}` : `execution failure \u2014 ${executionFailure}`;
|
|
6188
6267
|
appendJournal(runDir, { event: "judge-verdict", ts: now(), id: scenario.id, verdict: "ERROR", reason, suspect: false, ...repField });
|
|
6189
6268
|
return {
|
|
6190
6269
|
verdict: "ERROR",
|
|
@@ -6945,6 +7024,7 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
6945
7024
|
}
|
|
6946
7025
|
infrastructureFailure = null;
|
|
6947
7026
|
adapterFailure = null;
|
|
7027
|
+
let executionUnavailable = false;
|
|
6948
7028
|
try {
|
|
6949
7029
|
if (scenario.mode === "seeded") {
|
|
6950
7030
|
const r = await runSeeded(scenario, {
|
|
@@ -6996,8 +7076,11 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
6996
7076
|
const structured = await ctx.adapter.runStructured({ ...req, scenarioId: scenario.id, rep });
|
|
6997
7077
|
transcript = structured.transcript;
|
|
6998
7078
|
traces = structured.traces;
|
|
7079
|
+
executionUnavailable = Boolean(structured.executionFailure);
|
|
6999
7080
|
if (structured.providerFailure)
|
|
7000
7081
|
infrastructureFailure = `provider failure \u2014 ${structured.providerFailure}`;
|
|
7082
|
+
else if (structured.executionFailure)
|
|
7083
|
+
infrastructureFailure = `execution failure \u2014 ${structured.executionFailure}`;
|
|
7001
7084
|
} else {
|
|
7002
7085
|
transcript = await ctx.adapter.run(req);
|
|
7003
7086
|
}
|
|
@@ -7013,9 +7096,16 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
7013
7096
|
const provider = providerFailureFromTranscript(transcript);
|
|
7014
7097
|
if (provider)
|
|
7015
7098
|
infrastructureFailure = `provider failure \u2014 ${provider}`;
|
|
7099
|
+
else {
|
|
7100
|
+
const execution = executionFailureFromTranscript(transcript);
|
|
7101
|
+
if (execution)
|
|
7102
|
+
infrastructureFailure = `execution failure \u2014 ${execution}`;
|
|
7103
|
+
}
|
|
7016
7104
|
}
|
|
7017
|
-
|
|
7018
|
-
|
|
7105
|
+
executionUnavailable ||= executionFailureFromTranscript(transcript) !== null;
|
|
7106
|
+
const deliveredText = traces.length > 0 && traces.every((trace) => trace.final_status === "complete" && trace.final_text.length > 0 && !trace.capture_errors?.length);
|
|
7107
|
+
noResponse = !deliveredText && hasEmptyAssistantTurn(transcript);
|
|
7108
|
+
if (executionUnavailable || !noResponse && !adapterFailure)
|
|
7019
7109
|
break;
|
|
7020
7110
|
}
|
|
7021
7111
|
if (adapterFailure && !infrastructureFailure) {
|
|
@@ -7574,8 +7664,17 @@ function runPiJson(opts) {
|
|
|
7574
7664
|
rl.on("line", (line) => {
|
|
7575
7665
|
if (!line.trim())
|
|
7576
7666
|
return;
|
|
7577
|
-
if (SKIPPED_TYPE_RE.test(line))
|
|
7667
|
+
if (SKIPPED_TYPE_RE.test(line)) {
|
|
7668
|
+
try {
|
|
7669
|
+
const event = JSON.parse(line);
|
|
7670
|
+
if (event.type === "message_update" && kept.at(-1) !== '{"type":"message_update"}') {
|
|
7671
|
+
kept.push('{"type":"message_update"}');
|
|
7672
|
+
}
|
|
7673
|
+
} catch {
|
|
7674
|
+
kept.push("null");
|
|
7675
|
+
}
|
|
7578
7676
|
return;
|
|
7677
|
+
}
|
|
7579
7678
|
kept.push(line);
|
|
7580
7679
|
if (providerFailure === null)
|
|
7581
7680
|
providerFailure = providerFailureFromJsonLine(line);
|
|
@@ -7749,12 +7848,12 @@ var piAdapter = {
|
|
|
7749
7848
|
* results.yaml as `harness_cli_version`.
|
|
7750
7849
|
*
|
|
7751
7850
|
* Null on any failure — a non-zero exit, empty output, or pi missing entirely.
|
|
7752
|
-
*
|
|
7753
|
-
*
|
|
7851
|
+
* The probe never fabricates a version. Fresh subject execution refuses an
|
|
7852
|
+
* unavailable or unqualified result before starting the model.
|
|
7754
7853
|
*/
|
|
7755
|
-
async version() {
|
|
7854
|
+
async version(options) {
|
|
7756
7855
|
try {
|
|
7757
|
-
const r = await exec("pi", ["--version"], { timeoutMs: 3e4 });
|
|
7856
|
+
const r = await exec("pi", ["--version"], { timeoutMs: 3e4, ...options });
|
|
7758
7857
|
const line = r.stdout.split("\n")[0]?.trim() ?? "";
|
|
7759
7858
|
if (r.code !== 0 || line === "")
|
|
7760
7859
|
return null;
|
|
@@ -7779,9 +7878,12 @@ var piAdapter = {
|
|
|
7779
7878
|
req.model.model
|
|
7780
7879
|
];
|
|
7781
7880
|
const flags = req.systemPromptFile ? ["--no-skills", "--append-system-prompt", readFileSync15(req.systemPromptFile, "utf8")] : skillFlags(req.mode, req.skillDir);
|
|
7881
|
+
const env = req.armEnv ? { ...process.env, ...req.armEnv } : void 0;
|
|
7882
|
+
const qualificationFailure = piQualificationFailure(await this.version({ cwd: req.cwd, env }));
|
|
7883
|
+
if (qualificationFailure)
|
|
7884
|
+
return withExecutionFailure("", qualificationFailure);
|
|
7782
7885
|
const total = req.turns.length;
|
|
7783
7886
|
const parts = [];
|
|
7784
|
-
const env = req.armEnv ? { ...process.env, ...req.armEnv } : void 0;
|
|
7785
7887
|
let providerFailure = null;
|
|
7786
7888
|
if (total === 1) {
|
|
7787
7889
|
const args = [...flags, ...common2, "--no-session", "-p", req.turns[0]];
|
|
@@ -7842,13 +7944,21 @@ ${r.stderr.trim()}
|
|
|
7842
7944
|
req.model.model
|
|
7843
7945
|
];
|
|
7844
7946
|
const flags = req.systemPromptFile ? ["--no-skills", "--append-system-prompt", readFileSync15(req.systemPromptFile, "utf8")] : skillFlags(req.mode, req.skillDir);
|
|
7845
|
-
const
|
|
7947
|
+
const env = req.armEnv ? { ...process.env, ...req.armEnv } : void 0;
|
|
7948
|
+
const piVersion = await this.version({ cwd: req.cwd, env });
|
|
7949
|
+
const qualificationFailure = piQualificationFailure(piVersion);
|
|
7950
|
+
if (qualificationFailure)
|
|
7951
|
+
return {
|
|
7952
|
+
transcript: withExecutionFailure("", qualificationFailure),
|
|
7953
|
+
traces: [],
|
|
7954
|
+
executionFailure: qualificationFailure
|
|
7955
|
+
};
|
|
7846
7956
|
const total = req.turns.length;
|
|
7847
7957
|
const traces = [];
|
|
7848
7958
|
const parts = [];
|
|
7849
7959
|
const session = total === 1 ? null : mkdtempSync2(join22(tmpdir2(), "sc-pi-session-"));
|
|
7850
7960
|
let providerFailure = null;
|
|
7851
|
-
|
|
7961
|
+
let executionFailure = null;
|
|
7852
7962
|
for (let i = 0; i < total; i++) {
|
|
7853
7963
|
const turnFlags = session === null ? ["--no-session"] : i === 0 ? ["--session-dir", session] : ["--session-dir", session, "-c"];
|
|
7854
7964
|
const args = [...flags, ...common2, "--mode", "json", ...turnFlags, "-p", req.turns[i]];
|
|
@@ -7865,30 +7975,33 @@ ${r.stderr.trim()}
|
|
|
7865
7975
|
homeDir: homedir2(),
|
|
7866
7976
|
env
|
|
7867
7977
|
});
|
|
7868
|
-
if (
|
|
7869
|
-
|
|
7978
|
+
if (r.trace.final_status !== void 0 && (r.trace.final_status !== "complete" || r.trace.capture_errors?.length || r.code !== 0)) {
|
|
7979
|
+
executionFailure = `Pi ${piVersion} turn ${i + 1}/${total}: final delivery ${r.trace.final_status} (exit ${r.code}); ${r.trace.capture_errors?.join("; ") || "successful process completion was not established"}`;
|
|
7870
7980
|
}
|
|
7871
|
-
if (r.
|
|
7872
|
-
|
|
7873
|
-
r.trace.trace_sha256 = traceSha256(r.trace);
|
|
7981
|
+
if (!r.isComplete && !executionFailure) {
|
|
7982
|
+
throw new Error(`pi --mode json produced no terminal events for turn ${i + 1}/${total} (exit ${r.code}${r.malformedLines ? `, ${r.malformedLines} malformed line(s)` : ""})` + (r.stderr.trim() ? `: ${r.stderr.trim()}` : ""));
|
|
7874
7983
|
}
|
|
7875
|
-
if (providerFailure === null && r.providerFailure)
|
|
7984
|
+
if (providerFailure === null && r.providerFailure && r.trace.final_status !== "complete") {
|
|
7876
7985
|
providerFailure = r.providerFailure;
|
|
7986
|
+
}
|
|
7877
7987
|
const pricedTrace = priceSubjectUsage(r.trace);
|
|
7878
7988
|
traces.push(pricedTrace);
|
|
7879
7989
|
parts.push(header(i + 1, total, req.turns[i]));
|
|
7880
7990
|
parts.push(`<<< ASSISTANT:
|
|
7881
|
-
${pricedTrace.final_text
|
|
7991
|
+
${pricedTrace.final_text}
|
|
7882
7992
|
`);
|
|
7883
7993
|
if (r.code !== 0)
|
|
7884
7994
|
parts.push(`[pi exited ${r.code} on turn ${i + 1}]
|
|
7885
7995
|
${r.stderr.trim()}
|
|
7886
7996
|
`);
|
|
7997
|
+
if (executionFailure || providerFailure)
|
|
7998
|
+
break;
|
|
7887
7999
|
}
|
|
7888
8000
|
return {
|
|
7889
|
-
transcript: withProviderFailure(parts.join("\n"), providerFailure),
|
|
8001
|
+
transcript: withProviderFailure(withExecutionFailure(parts.join("\n"), executionFailure), providerFailure),
|
|
7890
8002
|
traces,
|
|
7891
|
-
...providerFailure ? { providerFailure } : {}
|
|
8003
|
+
...providerFailure ? { providerFailure } : {},
|
|
8004
|
+
...executionFailure ? { executionFailure } : {}
|
|
7892
8005
|
};
|
|
7893
8006
|
},
|
|
7894
8007
|
/** Run the judge through Pi: no skills, context files, extensions or session. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skill-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Test/optimize loop for agent skills — run spec'd scenarios on pi, LLM-judge, score, review, re-run",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"README.md"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@skill-harness/cli": "0.
|
|
34
|
+
"@skill-harness/cli": "0.24.0",
|
|
35
35
|
"typebox": "^1.1.38"
|
|
36
36
|
},
|
|
37
37
|
"repository": {
|