@skill-harness/core 0.18.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/dist/adapters/types.d.ts +2 -0
- package/dist/capture-trace-types.d.ts +8 -6
- package/dist/execution-trace.d.ts +0 -16
- package/dist/execution-trace.js +30 -41
- package/dist/metrics.d.ts +2 -1
- package/dist/reps.js +33 -8
- package/dist/results.d.ts +10 -0
- package/dist/results.js +2 -1
- package/dist/run.js +4 -2
- package/package.json +1 -1
- package/schemas/results-v2.schema.json +18 -0
- package/schemas/results-v3.schema.json +18 -0
package/dist/adapters/types.d.ts
CHANGED
|
@@ -81,6 +81,8 @@ export interface StructuredRun {
|
|
|
81
81
|
}
|
|
82
82
|
export interface HarnessAdapter {
|
|
83
83
|
name: string;
|
|
84
|
+
/** Prefer structured execution for ordinary runs so subject usage is retained. */
|
|
85
|
+
preferStructured?: boolean;
|
|
84
86
|
available(): Promise<boolean>;
|
|
85
87
|
run(req: RunReq): Promise<string>;
|
|
86
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. */
|
|
@@ -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
|
@@ -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/metrics.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ScenarioResult } from "./results.js";
|
|
2
|
+
import type { CostSource } from "./capture-trace-types.js";
|
|
2
3
|
export interface AggregateMetrics {
|
|
3
4
|
wall_time_ms: number;
|
|
4
5
|
judge_calls: number;
|
|
@@ -10,7 +11,7 @@ export interface AggregateMetrics {
|
|
|
10
11
|
cache_read_tokens: number | null;
|
|
11
12
|
cache_write_tokens: number | null;
|
|
12
13
|
subject_cost_usd: number | null;
|
|
13
|
-
cost_source:
|
|
14
|
+
cost_source: CostSource | null;
|
|
14
15
|
tool_calls: number | null;
|
|
15
16
|
delegated_children: number | null;
|
|
16
17
|
max_concurrency: number | null;
|
package/dist/reps.js
CHANGED
|
@@ -67,17 +67,32 @@ export function outcomesToResult(id, outcomes, repCount, threshold) {
|
|
|
67
67
|
const objectiveField = objective ? { objective } : {};
|
|
68
68
|
const metrics = aggregateMetrics(outcomes);
|
|
69
69
|
const metricsField = metrics ? { metrics } : {};
|
|
70
|
+
const usage = outcomes.flatMap((outcome, repetition) => {
|
|
71
|
+
const subject = outcome.metrics?.subject;
|
|
72
|
+
if (!subject)
|
|
73
|
+
return [];
|
|
74
|
+
const reported = (value) => value !== null && value > 0 ? value : null;
|
|
75
|
+
return [{
|
|
76
|
+
repetition,
|
|
77
|
+
inputTokens: reported(subject.input_tokens),
|
|
78
|
+
outputTokens: reported(subject.output_tokens),
|
|
79
|
+
cacheReadTokens: reported(subject.cache_read_tokens),
|
|
80
|
+
costUsd: reported(subject.cost_usd),
|
|
81
|
+
priceAsOf: subject.price_as_of,
|
|
82
|
+
}];
|
|
83
|
+
});
|
|
84
|
+
const usageField = usage.length ? { usage } : {};
|
|
70
85
|
const repJudgments = outcomes.map((outcome, repetition) => ({ repetition, judgments: outcome.judgment ? [outcome.judgment] : [], recorded_verdict: outcome.verdict, ...(outcome.objective ? { objective: outcome.objective } : {}) }));
|
|
71
86
|
const repJudgmentField = outcomes.some(outcome => outcome.judgment) ? { rep_judgments: repJudgments } : {};
|
|
72
87
|
if (repCount === 1) {
|
|
73
88
|
const o = outcomes[0];
|
|
74
|
-
return { id, judge_verdict: o.verdict, judge_reason: o.reason, suspect: o.suspect, ...metricsField, override: null, note: "", ...objectiveField, ...repJudgmentField };
|
|
89
|
+
return { id, judge_verdict: o.verdict, judge_reason: o.reason, suspect: o.suspect, ...metricsField, ...usageField, override: null, note: "", ...objectiveField, ...repJudgmentField };
|
|
75
90
|
}
|
|
76
91
|
const agg = aggregateReps(outcomes, threshold);
|
|
77
92
|
return {
|
|
78
93
|
id, judge_verdict: agg.verdict, judge_reason: agg.reason, suspect: agg.suspect,
|
|
79
94
|
reps: agg.reps, passes: agg.passes, clean: agg.clean, flakiness: agg.flakiness,
|
|
80
|
-
pass_threshold: threshold, ...metricsField, override: null, note: "", ...objectiveField, ...repJudgmentField,
|
|
95
|
+
pass_threshold: threshold, ...metricsField, ...usageField, override: null, note: "", ...objectiveField, ...repJudgmentField,
|
|
81
96
|
};
|
|
82
97
|
}
|
|
83
98
|
function aggregateMetrics(outcomes) {
|
|
@@ -85,22 +100,32 @@ function aggregateMetrics(outcomes) {
|
|
|
85
100
|
if (present.length === 0)
|
|
86
101
|
return undefined;
|
|
87
102
|
const subjects = present.map((metrics) => metrics.subject).filter((metrics) => metrics !== undefined);
|
|
103
|
+
const reportedSubjects = subjects.filter(metrics => metrics.input_tokens !== null || metrics.output_tokens !== null || metrics.cache_read_tokens !== null);
|
|
88
104
|
const base = {
|
|
89
105
|
wall_time_ms: present.reduce((sum, metrics) => sum + metrics.wall_time_ms, 0),
|
|
90
106
|
judge_calls: present.reduce((sum, metrics) => sum + metrics.judge_calls, 0),
|
|
91
107
|
judge_rejudge_calls: present.reduce((sum, metrics) => sum + metrics.judge_rejudge_calls, 0),
|
|
92
|
-
subject_metrics_reps:
|
|
108
|
+
subject_metrics_reps: reportedSubjects.length,
|
|
93
109
|
total_reps: outcomes.length,
|
|
94
110
|
};
|
|
95
111
|
if (subjects.length === 0)
|
|
96
112
|
return base;
|
|
113
|
+
const sumReported = (field) => {
|
|
114
|
+
const values = subjects.map(metrics => metrics[field]).filter((value) => value !== null && value > 0);
|
|
115
|
+
return values.length ? values.reduce((sum, value) => sum + value, 0) : undefined;
|
|
116
|
+
};
|
|
117
|
+
const inputTokens = sumReported("input_tokens");
|
|
118
|
+
const outputTokens = sumReported("output_tokens");
|
|
119
|
+
const cacheReadTokens = sumReported("cache_read_tokens");
|
|
120
|
+
const cacheWriteTokens = sumReported("cache_write_tokens");
|
|
121
|
+
const subjectCost = sumReported("cost_usd");
|
|
97
122
|
return {
|
|
98
123
|
...base,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
124
|
+
...(inputTokens === undefined ? {} : { input_tokens: inputTokens }),
|
|
125
|
+
...(outputTokens === undefined ? {} : { output_tokens: outputTokens }),
|
|
126
|
+
...(cacheReadTokens === undefined ? {} : { cache_read_tokens: cacheReadTokens }),
|
|
127
|
+
...(cacheWriteTokens === undefined ? {} : { cache_write_tokens: cacheWriteTokens }),
|
|
128
|
+
...(subjectCost === undefined ? {} : { subject_cost_usd: subjectCost }),
|
|
104
129
|
cost_source: subjects.every((metrics) => metrics.cost_source === subjects[0].cost_source)
|
|
105
130
|
? subjects[0].cost_source
|
|
106
131
|
: "unreported",
|
package/dist/results.d.ts
CHANGED
|
@@ -3,6 +3,14 @@ import { type ScenarioVerdict } from "./score.js";
|
|
|
3
3
|
import type { Verdict } from "./score.js";
|
|
4
4
|
import type { ShipBar, Scenario } from "./spec.js";
|
|
5
5
|
import type { CostSource } from "./capture-trace-types.js";
|
|
6
|
+
export interface SubjectUsage {
|
|
7
|
+
repetition: number;
|
|
8
|
+
inputTokens: number | null;
|
|
9
|
+
outputTokens: number | null;
|
|
10
|
+
cacheReadTokens: number | null;
|
|
11
|
+
costUsd: number | null;
|
|
12
|
+
priceAsOf: string | null;
|
|
13
|
+
}
|
|
6
14
|
export interface ScenarioMetrics {
|
|
7
15
|
wall_time_ms: number;
|
|
8
16
|
judge_calls: number;
|
|
@@ -56,6 +64,8 @@ export interface ScenarioResult {
|
|
|
56
64
|
pass_threshold?: number;
|
|
57
65
|
/** Cost/latency counters. Separate from verdicts: cheaper failure is still failure. */
|
|
58
66
|
metrics?: ScenarioMetrics;
|
|
67
|
+
/** Per-repetition subject usage; null values mean Pi did not report that counter. */
|
|
68
|
+
usage?: SubjectUsage[];
|
|
59
69
|
/**
|
|
60
70
|
* Objective trace-gate evidence. ADDITIVE and optional.
|
|
61
71
|
*
|
package/dist/results.js
CHANGED
|
@@ -599,7 +599,7 @@ export function diffPath(runDir, scenarioId, mode, rep) {
|
|
|
599
599
|
export function rebuildScenarioResult(fresh, prior, policy) {
|
|
600
600
|
// Exhaustive destructure. Do not replace with a spread: the spread is what
|
|
601
601
|
// allowed a new field to pass through unconsidered in the first place.
|
|
602
|
-
const { id, criterion_count: freshCriterionCount, judge_verdict, judge_reason, suspect, override: _freshOverride, note: _freshNote, reps, passes, clean, flakiness, pass_threshold, metrics: freshMetrics, objective: freshObjective, adjudication: freshAdjudication, rep_judgments: freshRepJudgments, ...rest } = fresh;
|
|
602
|
+
const { id, criterion_count: freshCriterionCount, judge_verdict, judge_reason, suspect, override: _freshOverride, note: _freshNote, reps, passes, clean, flakiness, pass_threshold, metrics: freshMetrics, usage: freshUsage, objective: freshObjective, adjudication: freshAdjudication, rep_judgments: freshRepJudgments, ...rest } = fresh;
|
|
603
603
|
const _exhaustive = rest;
|
|
604
604
|
void _exhaustive;
|
|
605
605
|
void _freshOverride;
|
|
@@ -652,6 +652,7 @@ export function rebuildScenarioResult(fresh, prior, policy) {
|
|
|
652
652
|
...(flakiness === undefined ? {} : { flakiness }),
|
|
653
653
|
...(pass_threshold === undefined ? {} : { pass_threshold }),
|
|
654
654
|
...((freshMetrics ?? prior?.metrics) ? { metrics: freshMetrics ?? prior.metrics } : {}),
|
|
655
|
+
...((freshUsage ?? prior?.usage) ? { usage: freshUsage ?? prior.usage } : {}),
|
|
655
656
|
// The author owns the verdict; a re-measurement never discards their call.
|
|
656
657
|
override: prior?.override ?? null,
|
|
657
658
|
note: prior?.note ?? "",
|
package/dist/run.js
CHANGED
|
@@ -276,7 +276,7 @@ async function runRep(scenario, rep, repCount, ctx) {
|
|
|
276
276
|
throw new Error(`scenario \`${scenario.id}\` declares structured objective assertions, but the \`${ctx.adapter.name}\` adapter` +
|
|
277
277
|
` cannot produce execution traces — the gate would have no evidence to read.`);
|
|
278
278
|
}
|
|
279
|
-
const useStructured = (Boolean(ctx.structured) || needsStructuredEvidence) && Boolean(ctx.adapter.runStructured);
|
|
279
|
+
const useStructured = (Boolean(ctx.structured) || needsStructuredEvidence || Boolean(ctx.adapter.preferStructured)) && Boolean(ctx.adapter.runStructured);
|
|
280
280
|
// A blank assistant turn is a harness timeout, not model behavior: retry ONCE in a
|
|
281
281
|
// fresh workspace (the first attempt may have half-mutated a seeded repo), and if
|
|
282
282
|
// it happens again the verdict is ERROR — never a judged FAIL on an empty reply.
|
|
@@ -581,7 +581,9 @@ export function formatScorecard(summary, lift, stability) {
|
|
|
581
581
|
? " · WARNING: subject tokens were used but the provider reported $0 cost"
|
|
582
582
|
: metrics.subject_cost_usd === null
|
|
583
583
|
? " · subject cost unavailable"
|
|
584
|
-
:
|
|
584
|
+
: metrics.cost_source === "price-table"
|
|
585
|
+
? ` · $${metrics.subject_cost_usd.toFixed(6)} price table`
|
|
586
|
+
: ` · $${metrics.subject_cost_usd.toFixed(6)} provider-reported`;
|
|
585
587
|
lines.push(` COST: ${tokens} (${metrics.subject_metrics_reps}/${metrics.total_reps} reps reported)${cost} · ${metrics.judge_calls} judge + ${metrics.judge_rejudge_calls} re-judge call(s) · ${metrics.wall_time_ms}ms · ${tools}`);
|
|
586
588
|
}
|
|
587
589
|
// Lift is a statement about a skill-delivered run (green or force). On a red run
|
package/package.json
CHANGED
|
@@ -130,6 +130,19 @@
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
|
+
"usage": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"additionalProperties": false,
|
|
136
|
+
"required": ["repetition", "inputTokens", "outputTokens", "cacheReadTokens", "costUsd", "priceAsOf"],
|
|
137
|
+
"properties": {
|
|
138
|
+
"repetition": { "type": "integer", "minimum": 0 },
|
|
139
|
+
"inputTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
140
|
+
"outputTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
141
|
+
"cacheReadTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
142
|
+
"costUsd": { "type": ["number", "null"], "exclusiveMinimum": 0 },
|
|
143
|
+
"priceAsOf": { "type": ["string", "null"] }
|
|
144
|
+
}
|
|
145
|
+
},
|
|
133
146
|
"metrics": {
|
|
134
147
|
"type": "object",
|
|
135
148
|
"required": [
|
|
@@ -184,6 +197,7 @@
|
|
|
184
197
|
"enum": [
|
|
185
198
|
"provider-reported",
|
|
186
199
|
"subscription",
|
|
200
|
+
"price-table",
|
|
187
201
|
"unreported"
|
|
188
202
|
]
|
|
189
203
|
},
|
|
@@ -308,6 +322,10 @@
|
|
|
308
322
|
"metrics": {
|
|
309
323
|
"$ref": "#/$defs/metrics"
|
|
310
324
|
},
|
|
325
|
+
"usage": {
|
|
326
|
+
"type": "array",
|
|
327
|
+
"items": { "$ref": "#/$defs/usage" }
|
|
328
|
+
},
|
|
311
329
|
"objective": {
|
|
312
330
|
"type": "object",
|
|
313
331
|
"additionalProperties": false,
|
|
@@ -141,6 +141,19 @@
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
|
+
"usage": {
|
|
145
|
+
"type": "object",
|
|
146
|
+
"additionalProperties": false,
|
|
147
|
+
"required": ["repetition", "inputTokens", "outputTokens", "cacheReadTokens", "costUsd", "priceAsOf"],
|
|
148
|
+
"properties": {
|
|
149
|
+
"repetition": { "type": "integer", "minimum": 0 },
|
|
150
|
+
"inputTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
151
|
+
"outputTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
152
|
+
"cacheReadTokens": { "type": ["integer", "null"], "minimum": 1 },
|
|
153
|
+
"costUsd": { "type": ["number", "null"], "exclusiveMinimum": 0 },
|
|
154
|
+
"priceAsOf": { "type": ["string", "null"] }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
144
157
|
"metrics": {
|
|
145
158
|
"type": "object",
|
|
146
159
|
"required": [
|
|
@@ -195,6 +208,7 @@
|
|
|
195
208
|
"enum": [
|
|
196
209
|
"provider-reported",
|
|
197
210
|
"subscription",
|
|
211
|
+
"price-table",
|
|
198
212
|
"unreported"
|
|
199
213
|
]
|
|
200
214
|
},
|
|
@@ -334,6 +348,10 @@
|
|
|
334
348
|
"metrics": {
|
|
335
349
|
"$ref": "#/$defs/metrics"
|
|
336
350
|
},
|
|
351
|
+
"usage": {
|
|
352
|
+
"type": "array",
|
|
353
|
+
"items": { "$ref": "#/$defs/usage" }
|
|
354
|
+
},
|
|
337
355
|
"objective": {
|
|
338
356
|
"type": "object",
|
|
339
357
|
"additionalProperties": false,
|