@skill-harness/core 0.17.0 → 0.19.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 +1 -2
- package/dist/adapters/types.d.ts +2 -12
- package/dist/capture-trace-types.d.ts +8 -102
- package/dist/capture-trace-types.js +0 -4
- package/dist/command-cost.d.ts +1 -1
- package/dist/command-cost.js +0 -5
- package/dist/execution-trace.d.ts +0 -16
- package/dist/execution-trace.js +31 -42
- package/dist/index.d.ts +3 -23
- package/dist/index.js +3 -23
- package/dist/instruction-coverage.d.ts +0 -7
- package/dist/instruction-coverage.js +5 -7
- package/dist/journal.d.ts +0 -8
- package/dist/metrics.d.ts +19 -0
- package/dist/metrics.js +28 -0
- package/dist/redaction.d.ts +6 -0
- package/dist/redaction.js +55 -0
- package/dist/regate.js +6 -71
- package/dist/regrade.d.ts +1 -3
- package/dist/regrade.js +1 -21
- package/dist/report.d.ts +1 -15
- package/dist/report.js +1 -13
- package/dist/reps.d.ts +1 -2
- package/dist/reps.js +33 -10
- package/dist/results.d.ts +10 -9
- package/dist/results.js +3 -19
- package/dist/run.js +15 -74
- package/dist/seeded.d.ts +1 -7
- package/dist/seeded.js +5 -11
- package/dist/sources.js +4 -9
- package/dist/spec.d.ts +0 -13
- package/dist/spec.js +6 -48
- package/dist/trajectory-events.d.ts +58 -0
- package/dist/trajectory-events.js +119 -0
- package/package.json +1 -1
- package/schemas/results-v2.schema.json +394 -75
- package/schemas/results-v3.schema.json +18 -0
- package/schemas/specification-v1.schema.json +128 -174
- package/dist/adjudication.d.ts +0 -219
- package/dist/adjudication.js +0 -422
- package/dist/adoption.d.ts +0 -128
- package/dist/adoption.js +0 -102
- package/dist/affected.d.ts +0 -88
- package/dist/affected.js +0 -222
- package/dist/capture.d.ts +0 -193
- package/dist/capture.js +0 -344
- package/dist/comparison.d.ts +0 -97
- package/dist/comparison.js +0 -189
- package/dist/factory-calibration.d.ts +0 -63
- package/dist/factory-calibration.js +0 -115
- package/dist/intervention-results.d.ts +0 -6
- package/dist/intervention-results.js +0 -52
- package/dist/intervention.d.ts +0 -123
- package/dist/intervention.js +0 -218
- package/dist/investigation.d.ts +0 -94
- package/dist/investigation.js +0 -148
- package/dist/judge-agreement.d.ts +0 -19
- package/dist/judge-agreement.js +0 -31
- package/dist/qualification-capture.d.ts +0 -44
- package/dist/qualification-capture.js +0 -163
- package/dist/qualification-config.d.ts +0 -233
- package/dist/qualification-config.js +0 -694
- package/dist/qualification-lock.d.ts +0 -15
- package/dist/qualification-lock.js +0 -87
- package/dist/qualification-oauth-directory.d.ts +0 -79
- package/dist/qualification-oauth-directory.js +0 -444
- package/dist/qualification-panel-store.d.ts +0 -14
- package/dist/qualification-panel-store.js +0 -188
- package/dist/qualification-panels.d.ts +0 -99
- package/dist/qualification-panels.js +0 -91
- package/dist/qualification-process.d.ts +0 -13
- package/dist/qualification-process.js +0 -190
- package/dist/qualification-runner.d.ts +0 -203
- package/dist/qualification-runner.js +0 -2037
- package/dist/qualification-store.d.ts +0 -187
- package/dist/qualification-store.js +0 -833
- package/dist/qualification-validation.d.ts +0 -12
- package/dist/qualification-validation.js +0 -8
- package/dist/screen.d.ts +0 -31
- package/dist/screen.js +0 -105
- package/dist/trajectory-gates.d.ts +0 -137
- package/dist/trajectory-gates.js +0 -576
- package/dist/trusted-host-supervision.d.ts +0 -25
- package/dist/trusted-host-supervision.js +0 -94
- package/dist/work-capture-schema.d.ts +0 -38
- package/dist/work-capture-schema.js +0 -33
- package/dist/work-capture.d.ts +0 -43
- package/dist/work-capture.js +0 -81
- package/dist/work-signals.d.ts +0 -55
- package/dist/work-signals.js +0 -71
- package/schemas/qualification-cell-result-v1.schema.json +0 -32
- package/schemas/qualification-config-v1.schema.json +0 -244
- package/schemas/qualification-invocation-request-v1.schema.json +0 -59
- package/schemas/qualification-judge-panel-result-v1.schema.json +0 -42
- package/schemas/trajectory-event-v1.schema.json +0 -80
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# @skill-harness/core
|
|
2
2
|
|
|
3
3
|
The engine behind [skill-harness](https://github.com/mojomanyana/skill-harness):
|
|
4
|
-
spec parsing, discovery, run orchestration, LLM-judge grading, scoring, results
|
|
5
|
-
and the separately versioned durable qualification-runner-v1 primitives.
|
|
4
|
+
spec parsing, discovery, run orchestration, LLM-judge grading, scoring, and results.
|
|
6
5
|
|
|
7
6
|
Internal API — use the `skill-harness` CLI. See the repo for docs.
|
|
8
7
|
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { ExecutionTraceV1 } from "../capture-trace-types.js";
|
|
2
|
-
import type { TrajectoryEventSource } from "../spec.js";
|
|
3
|
-
import type { TrajectoryEventV1 } from "../trajectory-gates.js";
|
|
4
2
|
export type RunMode = "red" | "green" | "force";
|
|
5
3
|
/** A provider+model pair, e.g. { provider: "fireworks", model: "accounts/.../deepseek-v4-pro" }. */
|
|
6
4
|
export interface ModelRef {
|
|
@@ -57,16 +55,12 @@ export interface RunReq {
|
|
|
57
55
|
* extension the developer happens to have installed cannot join the test.
|
|
58
56
|
*/
|
|
59
57
|
extensions?: string[];
|
|
60
|
-
/** Workspace-local native ledgers to normalize after the subject finishes. */
|
|
61
|
-
eventSources?: TrajectoryEventSource[];
|
|
62
58
|
/**
|
|
63
59
|
* Extra env for the subject process, from the arm. Merged over `process.env` by
|
|
64
60
|
* the adapter — an arm must be able to add `PI_GRANTS_*` without the harness
|
|
65
61
|
* inheriting a caller's copy of them.
|
|
66
62
|
*/
|
|
67
63
|
armEnv?: Record<string, string>;
|
|
68
|
-
/** Adapter-produced observation callback. Callers receive evidence; they never supply delivery status. */
|
|
69
|
-
onPromptObservation?: (observation: PromptProvenance) => void;
|
|
70
64
|
}
|
|
71
65
|
/** A judge request: single prompt, no skills, no session. */
|
|
72
66
|
export interface JudgeReq {
|
|
@@ -79,10 +73,6 @@ export interface StructuredRun {
|
|
|
79
73
|
transcript: string;
|
|
80
74
|
/** One trace per turn — each `pi` invocation emits an independent event stream. */
|
|
81
75
|
traces: ExecutionTraceV1[];
|
|
82
|
-
/** Adapter-neutral workflow/tool events. Additive; absent on older adapters. */
|
|
83
|
-
events?: TrajectoryEventV1[];
|
|
84
|
-
/** Native sources that were required but missing/malformed. Never treated as an empty success. */
|
|
85
|
-
eventErrors?: string[];
|
|
86
76
|
/**
|
|
87
77
|
* Set when pi failed provider-side (auth, transport) rather than the model
|
|
88
78
|
* answering badly. `run.ts` turns this into ERROR — never a model verdict.
|
|
@@ -91,8 +81,8 @@ export interface StructuredRun {
|
|
|
91
81
|
}
|
|
92
82
|
export interface HarnessAdapter {
|
|
93
83
|
name: string;
|
|
94
|
-
/**
|
|
95
|
-
|
|
84
|
+
/** Prefer structured execution for ordinary runs so subject usage is retained. */
|
|
85
|
+
preferStructured?: boolean;
|
|
96
86
|
available(): Promise<boolean>;
|
|
97
87
|
run(req: RunReq): Promise<string>;
|
|
98
88
|
/**
|
|
@@ -83,14 +83,16 @@ export interface TraceResultMeta {
|
|
|
83
83
|
* conversation carries in the session dir, not in the event stream). This
|
|
84
84
|
* mirrors the loop the adapter already runs.
|
|
85
85
|
*/
|
|
86
|
-
export type CostSource = "provider-reported" | "subscription" | "unreported";
|
|
86
|
+
export type CostSource = "provider-reported" | "subscription" | "price-table" | "unreported";
|
|
87
87
|
export interface TraceMetrics {
|
|
88
|
-
input_tokens: number;
|
|
89
|
-
output_tokens: number;
|
|
90
|
-
cache_read_tokens: number;
|
|
91
|
-
cache_write_tokens: number;
|
|
92
|
-
cost_usd: number;
|
|
88
|
+
input_tokens: number | null;
|
|
89
|
+
output_tokens: number | null;
|
|
90
|
+
cache_read_tokens: number | null;
|
|
91
|
+
cache_write_tokens: number | null;
|
|
92
|
+
cost_usd: number | null;
|
|
93
93
|
cost_source: CostSource;
|
|
94
|
+
/** Date of the repository price table used for cost_usd; null when unpriced. */
|
|
95
|
+
price_as_of: string | null;
|
|
94
96
|
tool_calls: number;
|
|
95
97
|
delegated_children: number;
|
|
96
98
|
/** Maximum simultaneously outstanding tool calls observed in the pi stream. */
|
|
@@ -151,99 +153,3 @@ export interface ExecutionTraceV1 {
|
|
|
151
153
|
/** SHA-256 over the deterministic serialization of this trace, minus this field. */
|
|
152
154
|
trace_sha256?: string;
|
|
153
155
|
}
|
|
154
|
-
export declare const CAPTURE_SCHEMA_VERSION = 1;
|
|
155
|
-
/** Why a conversation was captured. */
|
|
156
|
-
export type CaptureClassification = "failure" | "good_example";
|
|
157
|
-
/** Where a capture is in the human review pipeline. */
|
|
158
|
-
export type CapturePromotionStatus = "pending" | "promoted" | "rejected";
|
|
159
|
-
/** What the human confirmed as responsible for the behavior. */
|
|
160
|
-
export interface CaptureTarget {
|
|
161
|
-
kind: "skill" | "subagent";
|
|
162
|
-
/** Path relative to the skills root or repo root — never absolute. */
|
|
163
|
-
path: string;
|
|
164
|
-
/** SHA-256 of the target's content at capture time, so drift is detectable. */
|
|
165
|
-
content_sha256: string;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Provenance for a capture.
|
|
169
|
-
*
|
|
170
|
-
* Hashed, not absolute: an absolute session path identifies a machine and a
|
|
171
|
-
* user, and the capture file is meant to be committed. The hash is enough to
|
|
172
|
-
* recognize the same session again locally, which is all provenance needs to do.
|
|
173
|
-
*/
|
|
174
|
-
export interface CaptureProvenance {
|
|
175
|
-
session_sha256: string;
|
|
176
|
-
/** Indices of the selected turn range within the active branch, inclusive. */
|
|
177
|
-
turn_range: {
|
|
178
|
-
start: number;
|
|
179
|
-
end: number;
|
|
180
|
-
};
|
|
181
|
-
subject?: ModelRef;
|
|
182
|
-
git_commit?: string;
|
|
183
|
-
/** True when the working tree was dirty at capture time. */
|
|
184
|
-
git_dirty?: boolean;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* A reviewed conversation awaiting promotion into a scenario.
|
|
188
|
-
*
|
|
189
|
-
* Lives under `<skill>/tests/captures/`, NOT in `specification.yaml`. A pending
|
|
190
|
-
* capture is not a test: putting it in the spec would drag it into ship-bar
|
|
191
|
-
* totals, staleness, lift, and stability, and the alternative — a `draft: true`
|
|
192
|
-
* flag every runner, scorer and linter has to honor — is a state that only has
|
|
193
|
-
* to be forgotten once to either corrupt a grade or silently drop a real
|
|
194
|
-
* scenario from a release run.
|
|
195
|
-
*/
|
|
196
|
-
export interface CaptureCaseV1 {
|
|
197
|
-
capture_schema: typeof CAPTURE_SCHEMA_VERSION;
|
|
198
|
-
id: string;
|
|
199
|
-
created: string;
|
|
200
|
-
classification: CaptureClassification;
|
|
201
|
-
/** Sanitized user turns only. Assistant prose is evidence, never an oracle. */
|
|
202
|
-
turns: string[];
|
|
203
|
-
/** Human-written, in their own words. Required — a capture with no expectation is not reviewable. */
|
|
204
|
-
expected_behavior: string;
|
|
205
|
-
/** Editable draft checklist. Offline-derived by default; LLM drafting is opt-in and costs tokens. */
|
|
206
|
-
checklist: string[];
|
|
207
|
-
target: CaptureTarget;
|
|
208
|
-
provenance: CaptureProvenance;
|
|
209
|
-
status: CapturePromotionStatus;
|
|
210
|
-
/** Set once promoted: the scenario id appended to `specification.yaml`. */
|
|
211
|
-
scenario_id?: string;
|
|
212
|
-
/**
|
|
213
|
-
* `covers` refs for this pending case, so `coverage` can park it against the
|
|
214
|
-
* instructions it is about before anyone promotes it.
|
|
215
|
-
*
|
|
216
|
-
* Derived from `target` — the author has already been made to choose which
|
|
217
|
-
* instructions are responsible, and asking again in different words would be
|
|
218
|
-
* asking the same question twice. File granularity, not `#section`: the target
|
|
219
|
-
* choice attributes a file, and inventing a section would be a guess the
|
|
220
|
-
* session cannot support.
|
|
221
|
-
*
|
|
222
|
-
* Written relative to the SPEC dir (`<skill>/tests`), matching how a scenario's
|
|
223
|
-
* own `covers` resolve. `coverage` read this field from the start; nothing ever
|
|
224
|
-
* wrote it, so every pending case parked against nothing and the feature was
|
|
225
|
-
* inert.
|
|
226
|
-
*/
|
|
227
|
-
covers?: string[];
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Local-only evidence sidecar for a capture.
|
|
231
|
-
*
|
|
232
|
-
* Written to `<skill>/tests/captures/.local/` and git-ignored by default. It may
|
|
233
|
-
* hold a sanitized assistant excerpt and tool-call summaries to help a human
|
|
234
|
-
* review the capture, and must NEVER hold hidden thinking, complete tool-result
|
|
235
|
-
* bodies, or the effective system prompt.
|
|
236
|
-
*
|
|
237
|
-
* pi's stream carries thinking in `message_end`, `turn_end` AND `agent_end`, so
|
|
238
|
-
* dropping it is an explicit filter at every one of those, not a side effect of
|
|
239
|
-
* reading the convenient field.
|
|
240
|
-
*/
|
|
241
|
-
export interface CaptureEvidenceV1 {
|
|
242
|
-
capture_id: string;
|
|
243
|
-
/** Sanitized, truncated excerpt of the assistant text of every selected turn, joined. */
|
|
244
|
-
assistant_excerpt: string;
|
|
245
|
-
/** Tool name, error state and redacted arguments — no bodies. */
|
|
246
|
-
tool_calls: Array<Pick<TraceToolCall, "name" | "isError"> & {
|
|
247
|
-
args: SanitizedArgs;
|
|
248
|
-
}>;
|
|
249
|
-
}
|
|
@@ -16,8 +16,4 @@
|
|
|
16
16
|
// Execution trace
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
export const EXECUTION_TRACE_VERSION = 2;
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Capture case
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
export const CAPTURE_SCHEMA_VERSION = 1;
|
|
23
19
|
//# sourceMappingURL=capture-trace-types.js.map
|
package/dist/command-cost.d.ts
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
* Keep this list narrow: commands that can conditionally spend (notably `regate`)
|
|
5
5
|
* do not belong here even when their common path only replays saved artifacts.
|
|
6
6
|
*/
|
|
7
|
-
export declare const FREE_OFFLINE_COMMANDS: readonly ["
|
|
7
|
+
export declare const FREE_OFFLINE_COMMANDS: readonly ["coverage", "init", "lint", "list", "rescore", "restamp", "stability"];
|
|
8
8
|
export type FreeOfflineCommand = typeof FREE_OFFLINE_COMMANDS[number];
|
|
9
9
|
export declare function isFreeOfflineCommand(command: string): command is FreeOfflineCommand;
|
package/dist/command-cost.js
CHANGED
|
@@ -5,17 +5,12 @@
|
|
|
5
5
|
* do not belong here even when their common path only replays saved artifacts.
|
|
6
6
|
*/
|
|
7
7
|
export const FREE_OFFLINE_COMMANDS = [
|
|
8
|
-
"affected",
|
|
9
|
-
"archive",
|
|
10
8
|
"coverage",
|
|
11
9
|
"init",
|
|
12
|
-
"judge-agreement",
|
|
13
|
-
"learning",
|
|
14
10
|
"lint",
|
|
15
11
|
"list",
|
|
16
12
|
"rescore",
|
|
17
13
|
"restamp",
|
|
18
|
-
"screen",
|
|
19
14
|
"stability",
|
|
20
15
|
];
|
|
21
16
|
export function isFreeOfflineCommand(command) {
|
|
@@ -40,22 +40,6 @@ export declare function traceSha256(trace: ExecutionTraceV1): string;
|
|
|
40
40
|
export declare function serializeTrace(trace: ExecutionTraceV1): string;
|
|
41
41
|
/** Read a saved trace artifact back, for `regate`. Returns null when unusable. */
|
|
42
42
|
export declare function deserializeTrace(text: string): ExecutionTraceV1 | null;
|
|
43
|
-
/**
|
|
44
|
-
* Collapse a scenario's per-turn traces into one view for gate evaluation.
|
|
45
|
-
*
|
|
46
|
-
* Assertions are written about the scenario ("it delegated to `plan` at least
|
|
47
|
-
* once"), not about turn 3 — a multi-turn scenario would otherwise need the
|
|
48
|
-
* author to know which turn a tool call landed in, which is a property of the
|
|
49
|
-
* model's choices, not of the test.
|
|
50
|
-
*
|
|
51
|
-
* Indices are renumbered across the whole scenario so `issueIndex` stays a total
|
|
52
|
-
* order. `completionIndex` is renumbered within the concatenation too: turns are
|
|
53
|
-
* strictly sequential (each is a separate `pi` invocation), so no completion in
|
|
54
|
-
* turn 2 can precede one in turn 1.
|
|
55
|
-
*
|
|
56
|
-
* Returns null for an empty list — "no turns produced evidence" must not look
|
|
57
|
-
* like "a run in which nothing happened".
|
|
58
|
-
*/
|
|
59
43
|
export declare function mergeTraces(traces: ExecutionTraceV1[]): ExecutionTraceV1 | null;
|
|
60
44
|
/** Split a raw stdout blob into lines. Prefer streaming; this is for saved blobs. */
|
|
61
45
|
export declare function lines(text: string): Generator<string>;
|
package/dist/execution-trace.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { normalizeSubagentCall } from "./trace-gates.js";
|
|
3
3
|
import { EXECUTION_TRACE_VERSION } from "./capture-trace-types.js";
|
|
4
|
-
import { redactArgs, redactText } from "./
|
|
4
|
+
import { redactArgs, redactText } from "./redaction.js";
|
|
5
5
|
/**
|
|
6
6
|
* Parse pi's `--mode json` event stream into an `ExecutionTraceV1`.
|
|
7
7
|
*
|
|
@@ -43,12 +43,10 @@ export function parseTrace(lines, meta) {
|
|
|
43
43
|
let sawTerminal = false;
|
|
44
44
|
let finalText = "";
|
|
45
45
|
let lastAssistantText = "";
|
|
46
|
-
let
|
|
47
|
-
let
|
|
48
|
-
let
|
|
49
|
-
let
|
|
50
|
-
let cacheWriteTokens = 0;
|
|
51
|
-
let sawUsage = false;
|
|
46
|
+
let inputTokens = null;
|
|
47
|
+
let outputTokens = null;
|
|
48
|
+
let cacheReadTokens = null;
|
|
49
|
+
let cacheWriteTokens = null;
|
|
52
50
|
let activeCalls = 0;
|
|
53
51
|
let maxConcurrency = 0;
|
|
54
52
|
for (const line of lines) {
|
|
@@ -130,21 +128,10 @@ export function parseTrace(lines, meta) {
|
|
|
130
128
|
if (msg.stopReason === "stop")
|
|
131
129
|
finalText = text;
|
|
132
130
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const total = msg.usage?.cost?.total;
|
|
138
|
-
if (typeof total === "number")
|
|
139
|
-
cost = (cost ?? 0) + total;
|
|
140
|
-
if (typeof msg.usage?.input === "number")
|
|
141
|
-
inputTokens += msg.usage.input;
|
|
142
|
-
if (typeof msg.usage?.output === "number")
|
|
143
|
-
outputTokens += msg.usage.output;
|
|
144
|
-
if (typeof msg.usage?.cacheRead === "number")
|
|
145
|
-
cacheReadTokens += msg.usage.cacheRead;
|
|
146
|
-
if (typeof msg.usage?.cacheWrite === "number")
|
|
147
|
-
cacheWriteTokens += msg.usage.cacheWrite;
|
|
131
|
+
inputTokens = addReported(inputTokens, msg.usage?.input);
|
|
132
|
+
outputTokens = addReported(outputTokens, msg.usage?.output);
|
|
133
|
+
cacheReadTokens = addReported(cacheReadTokens, msg.usage?.cacheRead);
|
|
134
|
+
cacheWriteTokens = addReported(cacheWriteTokens, msg.usage?.cacheWrite);
|
|
148
135
|
continue;
|
|
149
136
|
}
|
|
150
137
|
if (type === "turn_end" || type === "agent_end" || type === "agent_settled") {
|
|
@@ -157,19 +144,14 @@ export function parseTrace(lines, meta) {
|
|
|
157
144
|
}
|
|
158
145
|
}
|
|
159
146
|
const toolCalls = [...calls.values()].sort((a, b) => a.issueIndex - b.issueIndex);
|
|
160
|
-
const subscription = meta.subject.provider === "openai-codex" || meta.subject.provider === "claude-code";
|
|
161
|
-
const costSource = subscription
|
|
162
|
-
? "subscription"
|
|
163
|
-
: cost !== null
|
|
164
|
-
? "provider-reported"
|
|
165
|
-
: "unreported";
|
|
166
147
|
const metrics = {
|
|
167
148
|
input_tokens: inputTokens,
|
|
168
149
|
output_tokens: outputTokens,
|
|
169
150
|
cache_read_tokens: cacheReadTokens,
|
|
170
151
|
cache_write_tokens: cacheWriteTokens,
|
|
171
|
-
cost_usd:
|
|
172
|
-
cost_source:
|
|
152
|
+
cost_usd: null,
|
|
153
|
+
cost_source: "unreported",
|
|
154
|
+
price_as_of: null,
|
|
173
155
|
tool_calls: toolCalls.length,
|
|
174
156
|
delegated_children: toolCalls
|
|
175
157
|
.filter((call) => call.name === "Agent")
|
|
@@ -197,15 +179,17 @@ export function parseTrace(lines, meta) {
|
|
|
197
179
|
// overwrites this after observing the workspace. Defaulting to `[]` claimed
|
|
198
180
|
// "observed, nothing changed" for every trace ever parsed.
|
|
199
181
|
changed_paths: meta.changedPaths ? [...meta.changedPaths].sort() : null,
|
|
200
|
-
cost_usd:
|
|
201
|
-
// Tool
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
...(sawUsage ? { metrics } : {}),
|
|
182
|
+
cost_usd: null,
|
|
183
|
+
// Tool metrics are observed directly. Token and cost fields stay null when
|
|
184
|
+
// Pi does not report them; zero would falsely mean "free".
|
|
185
|
+
metrics,
|
|
205
186
|
};
|
|
206
187
|
trace.trace_sha256 = traceSha256(trace);
|
|
207
188
|
return { trace, isComplete: sawTerminal, malformedLines };
|
|
208
189
|
}
|
|
190
|
+
function addReported(current, value) {
|
|
191
|
+
return typeof value === "number" && value > 0 ? (current ?? 0) + value : current;
|
|
192
|
+
}
|
|
209
193
|
/** Visible assistant text. Thinking is dropped here, and at every other reader. */
|
|
210
194
|
function assistantText(msg) {
|
|
211
195
|
return (msg.content ?? [])
|
|
@@ -308,6 +292,9 @@ export function deserializeTrace(text) {
|
|
|
308
292
|
* Returns null for an empty list — "no turns produced evidence" must not look
|
|
309
293
|
* like "a run in which nothing happened".
|
|
310
294
|
*/
|
|
295
|
+
function sumNullable(left, right) {
|
|
296
|
+
return left === null ? right : right === null ? left : left + right;
|
|
297
|
+
}
|
|
311
298
|
export function mergeTraces(traces) {
|
|
312
299
|
if (traces.length === 0)
|
|
313
300
|
return null;
|
|
@@ -350,18 +337,20 @@ export function mergeTraces(traces) {
|
|
|
350
337
|
const completeMetrics = traces.every((trace) => trace.metrics !== undefined);
|
|
351
338
|
const metrics = completeMetrics
|
|
352
339
|
? traces.reduce((sum, trace) => ({
|
|
353
|
-
input_tokens: sum.input_tokens
|
|
354
|
-
output_tokens: sum.output_tokens
|
|
355
|
-
cache_read_tokens: sum.cache_read_tokens
|
|
356
|
-
cache_write_tokens: sum.cache_write_tokens
|
|
357
|
-
cost_usd: sum.cost_usd
|
|
340
|
+
input_tokens: sumNullable(sum.input_tokens, trace.metrics.input_tokens),
|
|
341
|
+
output_tokens: sumNullable(sum.output_tokens, trace.metrics.output_tokens),
|
|
342
|
+
cache_read_tokens: sumNullable(sum.cache_read_tokens, trace.metrics.cache_read_tokens),
|
|
343
|
+
cache_write_tokens: sumNullable(sum.cache_write_tokens, trace.metrics.cache_write_tokens),
|
|
344
|
+
cost_usd: sumNullable(sum.cost_usd, trace.metrics.cost_usd),
|
|
358
345
|
cost_source: sum.cost_source === trace.metrics.cost_source ? sum.cost_source : "unreported",
|
|
346
|
+
price_as_of: sum.price_as_of === trace.metrics.price_as_of ? sum.price_as_of : null,
|
|
359
347
|
tool_calls: sum.tool_calls + trace.metrics.tool_calls,
|
|
360
348
|
delegated_children: sum.delegated_children + trace.metrics.delegated_children,
|
|
361
349
|
max_concurrency: Math.max(sum.max_concurrency, trace.metrics.max_concurrency),
|
|
362
350
|
}), {
|
|
363
|
-
input_tokens:
|
|
364
|
-
cost_usd:
|
|
351
|
+
input_tokens: null, output_tokens: null, cache_read_tokens: null, cache_write_tokens: null,
|
|
352
|
+
cost_usd: null, cost_source: traces[0].metrics.cost_source, price_as_of: traces[0].metrics.price_as_of,
|
|
353
|
+
tool_calls: 0, delegated_children: 0, max_concurrency: 0,
|
|
365
354
|
})
|
|
366
355
|
: undefined;
|
|
367
356
|
const last = traces[traces.length - 1];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from "./trusted-host-supervision.js";
|
|
2
1
|
export * from "./spec.js";
|
|
3
2
|
export * from "./discover.js";
|
|
4
3
|
export * from "./run.js";
|
|
@@ -13,6 +12,7 @@ export * from "./rescore.js";
|
|
|
13
12
|
export * from "./workspace.js";
|
|
14
13
|
export * from "./seeded.js";
|
|
15
14
|
export * from "./report.js";
|
|
15
|
+
export * from "./metrics.js";
|
|
16
16
|
export * from "./trends.js";
|
|
17
17
|
export * from "./lint.js";
|
|
18
18
|
export * from "./restamp.js";
|
|
@@ -26,39 +26,19 @@ export * from "./version.js";
|
|
|
26
26
|
export * from "./defaults.js";
|
|
27
27
|
export * from "./command-cost.js";
|
|
28
28
|
export * from "./judge-policy.js";
|
|
29
|
-
export * from "./judge-agreement.js";
|
|
30
29
|
export * from "./regate.js";
|
|
31
|
-
export * from "./screen.js";
|
|
32
30
|
export * from "./prompt-normalization.js";
|
|
33
31
|
export * from "./downgrade.js";
|
|
34
32
|
export * from "./canary.js";
|
|
35
33
|
export * from "./stability.js";
|
|
36
34
|
export * from "./capture-trace-types.js";
|
|
37
|
-
export * from "./work-capture.js";
|
|
38
|
-
export * from "./work-signals.js";
|
|
39
|
-
export * from "./work-capture-schema.js";
|
|
40
|
-
export * from "./factory-calibration.js";
|
|
41
|
-
export * from "./adoption.js";
|
|
42
|
-
export * from "./investigation.js";
|
|
43
|
-
export * from "./intervention.js";
|
|
44
|
-
export * from "./intervention-results.js";
|
|
45
35
|
export * from "./spec-write.js";
|
|
46
|
-
export * from "./
|
|
36
|
+
export * from "./redaction.js";
|
|
47
37
|
export * from "./execution-trace.js";
|
|
48
38
|
export * from "./trace-gates.js";
|
|
49
|
-
export * from "./trajectory-
|
|
39
|
+
export * from "./trajectory-events.js";
|
|
50
40
|
export * from "./instruction-coverage.js";
|
|
51
|
-
export * from "./affected.js";
|
|
52
|
-
export * from "./adjudication.js";
|
|
53
41
|
export * from "./vote-panel.js";
|
|
54
|
-
export * from "./qualification-panels.js";
|
|
55
|
-
export * from "./qualification-panel-store.js";
|
|
56
|
-
export * from "./qualification-validation.js";
|
|
57
|
-
export * from "./comparison.js";
|
|
58
42
|
export * from "./sandbox.js";
|
|
59
43
|
export * from "./provider-failure.js";
|
|
60
44
|
export * from "./arms.js";
|
|
61
|
-
export * from "./qualification-config.js";
|
|
62
|
-
export * from "./qualification-oauth-directory.js";
|
|
63
|
-
export * from "./qualification-store.js";
|
|
64
|
-
export * from "./qualification-runner.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from "./trusted-host-supervision.js";
|
|
2
1
|
export * from "./spec.js";
|
|
3
2
|
export * from "./discover.js";
|
|
4
3
|
export * from "./run.js";
|
|
@@ -13,6 +12,7 @@ export * from "./rescore.js";
|
|
|
13
12
|
export * from "./workspace.js";
|
|
14
13
|
export * from "./seeded.js";
|
|
15
14
|
export * from "./report.js";
|
|
15
|
+
export * from "./metrics.js";
|
|
16
16
|
export * from "./trends.js";
|
|
17
17
|
export * from "./lint.js";
|
|
18
18
|
export * from "./restamp.js";
|
|
@@ -26,40 +26,20 @@ export * from "./version.js";
|
|
|
26
26
|
export * from "./defaults.js";
|
|
27
27
|
export * from "./command-cost.js";
|
|
28
28
|
export * from "./judge-policy.js";
|
|
29
|
-
export * from "./judge-agreement.js";
|
|
30
29
|
export * from "./regate.js";
|
|
31
|
-
export * from "./screen.js";
|
|
32
30
|
export * from "./prompt-normalization.js";
|
|
33
31
|
export * from "./downgrade.js";
|
|
34
32
|
export * from "./canary.js";
|
|
35
33
|
export * from "./stability.js";
|
|
36
34
|
export * from "./capture-trace-types.js";
|
|
37
|
-
export * from "./work-capture.js";
|
|
38
|
-
export * from "./work-signals.js";
|
|
39
|
-
export * from "./work-capture-schema.js";
|
|
40
|
-
export * from "./factory-calibration.js";
|
|
41
|
-
export * from "./adoption.js";
|
|
42
|
-
export * from "./investigation.js";
|
|
43
|
-
export * from "./intervention.js";
|
|
44
|
-
export * from "./intervention-results.js";
|
|
45
35
|
export * from "./spec-write.js";
|
|
46
|
-
export * from "./
|
|
36
|
+
export * from "./redaction.js";
|
|
47
37
|
export * from "./execution-trace.js";
|
|
48
38
|
export * from "./trace-gates.js";
|
|
49
|
-
export * from "./trajectory-
|
|
39
|
+
export * from "./trajectory-events.js";
|
|
50
40
|
export * from "./instruction-coverage.js";
|
|
51
|
-
export * from "./affected.js";
|
|
52
|
-
export * from "./adjudication.js";
|
|
53
41
|
export * from "./vote-panel.js";
|
|
54
|
-
export * from "./qualification-panels.js";
|
|
55
|
-
export * from "./qualification-panel-store.js";
|
|
56
|
-
export * from "./qualification-validation.js";
|
|
57
|
-
export * from "./comparison.js";
|
|
58
42
|
export * from "./sandbox.js";
|
|
59
43
|
export * from "./provider-failure.js";
|
|
60
44
|
export * from "./arms.js";
|
|
61
|
-
export * from "./qualification-config.js";
|
|
62
|
-
export * from "./qualification-oauth-directory.js";
|
|
63
|
-
export * from "./qualification-store.js";
|
|
64
|
-
export * from "./qualification-runner.js";
|
|
65
45
|
//# sourceMappingURL=index.js.map
|
|
@@ -61,8 +61,6 @@ export interface SectionCoverage {
|
|
|
61
61
|
section: Section;
|
|
62
62
|
/** Scenario ids that declare a reference to this section. */
|
|
63
63
|
scenarios: string[];
|
|
64
|
-
/** Capture ids parked against this section but not yet promoted. */
|
|
65
|
-
pendingCaptures: string[];
|
|
66
64
|
}
|
|
67
65
|
export interface BrokenRef {
|
|
68
66
|
scenarioId: string;
|
|
@@ -87,11 +85,6 @@ export interface CoverageOptions {
|
|
|
87
85
|
scenarios: Scenario[];
|
|
88
86
|
/** Instruction files to report on even if nothing references them. */
|
|
89
87
|
baseFiles?: string[];
|
|
90
|
-
/** capture id → covers refs, for parking a pending case against a section. */
|
|
91
|
-
pendingCaptures?: {
|
|
92
|
-
id: string;
|
|
93
|
-
covers: string[];
|
|
94
|
-
}[];
|
|
95
88
|
}
|
|
96
89
|
/**
|
|
97
90
|
* Build the coverage report.
|
|
@@ -130,7 +130,7 @@ export function computeCoverage(opts) {
|
|
|
130
130
|
const k = key(file, section.slug);
|
|
131
131
|
let entry = bySection.get(k);
|
|
132
132
|
if (!entry) {
|
|
133
|
-
entry = { file, section, scenarios: []
|
|
133
|
+
entry = { file, section, scenarios: [] };
|
|
134
134
|
bySection.set(k, entry);
|
|
135
135
|
}
|
|
136
136
|
return entry;
|
|
@@ -142,7 +142,7 @@ export function computeCoverage(opts) {
|
|
|
142
142
|
ensure(file, s);
|
|
143
143
|
const broken = [];
|
|
144
144
|
const unmapped = [];
|
|
145
|
-
const attach = (id, refs
|
|
145
|
+
const attach = (id, refs) => {
|
|
146
146
|
for (const raw of refs) {
|
|
147
147
|
const ref = parseCoversRef(raw);
|
|
148
148
|
const sections = readSections(ref.file);
|
|
@@ -155,7 +155,7 @@ export function computeCoverage(opts) {
|
|
|
155
155
|
if (ref.slug === undefined) {
|
|
156
156
|
// A whole-file reference covers every section in it.
|
|
157
157
|
for (const s of sections)
|
|
158
|
-
ensure(ref.file, s)
|
|
158
|
+
ensure(ref.file, s).scenarios.push(id);
|
|
159
159
|
continue;
|
|
160
160
|
}
|
|
161
161
|
const match = sections.find((s) => s.slug === ref.slug);
|
|
@@ -168,7 +168,7 @@ export function computeCoverage(opts) {
|
|
|
168
168
|
});
|
|
169
169
|
continue;
|
|
170
170
|
}
|
|
171
|
-
ensure(ref.file, match)
|
|
171
|
+
ensure(ref.file, match).scenarios.push(id);
|
|
172
172
|
}
|
|
173
173
|
};
|
|
174
174
|
for (const s of opts.scenarios) {
|
|
@@ -176,10 +176,8 @@ export function computeCoverage(opts) {
|
|
|
176
176
|
unmapped.push(s.id);
|
|
177
177
|
continue;
|
|
178
178
|
}
|
|
179
|
-
attach(s.id, s.covers
|
|
179
|
+
attach(s.id, s.covers);
|
|
180
180
|
}
|
|
181
|
-
for (const c of opts.pendingCaptures ?? [])
|
|
182
|
-
attach(c.id, c.covers, "pendingCaptures");
|
|
183
181
|
const sections = [...bySection.values()].sort((a, b) => a.file.localeCompare(b.file) || a.section.startLine - b.section.startLine);
|
|
184
182
|
const covered = sections.filter((s) => s.scenarios.length > 0);
|
|
185
183
|
const uncovered = sections.filter((s) => s.scenarios.length === 0);
|
package/dist/journal.d.ts
CHANGED
|
@@ -63,14 +63,6 @@ export type JournalEvent = {
|
|
|
63
63
|
ok: boolean;
|
|
64
64
|
detail: string;
|
|
65
65
|
rep?: number;
|
|
66
|
-
}
|
|
67
|
-
/** One adjudication pass: which cells were re-judged, what it cost in CALLS, what stayed unresolved. */
|
|
68
|
-
| {
|
|
69
|
-
event: "adjudication";
|
|
70
|
-
ts: string;
|
|
71
|
-
triggered: string[];
|
|
72
|
-
judge_calls: number;
|
|
73
|
-
unresolved: string[];
|
|
74
66
|
} | {
|
|
75
67
|
event: "judge-verdict";
|
|
76
68
|
ts: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ScenarioResult } from "./results.js";
|
|
2
|
+
import type { CostSource } from "./capture-trace-types.js";
|
|
3
|
+
export interface AggregateMetrics {
|
|
4
|
+
wall_time_ms: number;
|
|
5
|
+
judge_calls: number;
|
|
6
|
+
judge_rejudge_calls: number;
|
|
7
|
+
subject_metrics_reps: number;
|
|
8
|
+
total_reps: number;
|
|
9
|
+
input_tokens: number | null;
|
|
10
|
+
output_tokens: number | null;
|
|
11
|
+
cache_read_tokens: number | null;
|
|
12
|
+
cache_write_tokens: number | null;
|
|
13
|
+
subject_cost_usd: number | null;
|
|
14
|
+
cost_source: CostSource | null;
|
|
15
|
+
tool_calls: number | null;
|
|
16
|
+
delegated_children: number | null;
|
|
17
|
+
max_concurrency: number | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function aggregateMetrics(scenarios: ScenarioResult[]): AggregateMetrics;
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function aggregateMetrics(scenarios) {
|
|
2
|
+
const metrics = scenarios.map((scenario) => scenario.metrics).filter((value) => value !== undefined);
|
|
3
|
+
const sumOptional = (field) => {
|
|
4
|
+
const values = metrics.map((value) => value[field]).filter((value) => typeof value === "number");
|
|
5
|
+
return values.length ? values.reduce((sum, value) => sum + value, 0) : null;
|
|
6
|
+
};
|
|
7
|
+
const maxValues = metrics.map((value) => value.max_concurrency).filter((value) => typeof value === "number");
|
|
8
|
+
return {
|
|
9
|
+
wall_time_ms: metrics.reduce((sum, value) => sum + value.wall_time_ms, 0),
|
|
10
|
+
judge_calls: metrics.reduce((sum, value) => sum + value.judge_calls, 0),
|
|
11
|
+
judge_rejudge_calls: metrics.reduce((sum, value) => sum + value.judge_rejudge_calls, 0),
|
|
12
|
+
subject_metrics_reps: metrics.reduce((sum, value) => sum + value.subject_metrics_reps, 0),
|
|
13
|
+
total_reps: metrics.reduce((sum, value) => sum + value.total_reps, 0),
|
|
14
|
+
input_tokens: sumOptional("input_tokens"),
|
|
15
|
+
output_tokens: sumOptional("output_tokens"),
|
|
16
|
+
cache_read_tokens: sumOptional("cache_read_tokens"),
|
|
17
|
+
cache_write_tokens: sumOptional("cache_write_tokens"),
|
|
18
|
+
subject_cost_usd: sumOptional("subject_cost_usd"),
|
|
19
|
+
cost_source: (() => {
|
|
20
|
+
const sources = scenarios.map((scenario) => scenario.metrics?.cost_source).filter((source) => source !== undefined);
|
|
21
|
+
return sources.length === 0 ? null : sources.every((source) => source === sources[0]) ? sources[0] : "unreported";
|
|
22
|
+
})(),
|
|
23
|
+
tool_calls: sumOptional("tool_calls"),
|
|
24
|
+
delegated_children: sumOptional("delegated_children"),
|
|
25
|
+
max_concurrency: maxValues.length ? Math.max(...maxValues) : null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SanitizedArgs } from "./capture-trace-types.js";
|
|
2
|
+
/** Values longer than this are truncated rather than persisted whole. */
|
|
3
|
+
export declare const MAX_VALUE_CHARS = 2000;
|
|
4
|
+
export declare function redactText(input: string, homeDir?: string): string;
|
|
5
|
+
export declare function truncate(input: string, max?: number): string;
|
|
6
|
+
export declare function redactArgs(args: unknown, homeDir?: string, depth?: number): SanitizedArgs;
|