@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.
Files changed (95) hide show
  1. package/README.md +1 -2
  2. package/dist/adapters/types.d.ts +2 -12
  3. package/dist/capture-trace-types.d.ts +8 -102
  4. package/dist/capture-trace-types.js +0 -4
  5. package/dist/command-cost.d.ts +1 -1
  6. package/dist/command-cost.js +0 -5
  7. package/dist/execution-trace.d.ts +0 -16
  8. package/dist/execution-trace.js +31 -42
  9. package/dist/index.d.ts +3 -23
  10. package/dist/index.js +3 -23
  11. package/dist/instruction-coverage.d.ts +0 -7
  12. package/dist/instruction-coverage.js +5 -7
  13. package/dist/journal.d.ts +0 -8
  14. package/dist/metrics.d.ts +19 -0
  15. package/dist/metrics.js +28 -0
  16. package/dist/redaction.d.ts +6 -0
  17. package/dist/redaction.js +55 -0
  18. package/dist/regate.js +6 -71
  19. package/dist/regrade.d.ts +1 -3
  20. package/dist/regrade.js +1 -21
  21. package/dist/report.d.ts +1 -15
  22. package/dist/report.js +1 -13
  23. package/dist/reps.d.ts +1 -2
  24. package/dist/reps.js +33 -10
  25. package/dist/results.d.ts +10 -9
  26. package/dist/results.js +3 -19
  27. package/dist/run.js +15 -74
  28. package/dist/seeded.d.ts +1 -7
  29. package/dist/seeded.js +5 -11
  30. package/dist/sources.js +4 -9
  31. package/dist/spec.d.ts +0 -13
  32. package/dist/spec.js +6 -48
  33. package/dist/trajectory-events.d.ts +58 -0
  34. package/dist/trajectory-events.js +119 -0
  35. package/package.json +1 -1
  36. package/schemas/results-v2.schema.json +394 -75
  37. package/schemas/results-v3.schema.json +18 -0
  38. package/schemas/specification-v1.schema.json +128 -174
  39. package/dist/adjudication.d.ts +0 -219
  40. package/dist/adjudication.js +0 -422
  41. package/dist/adoption.d.ts +0 -128
  42. package/dist/adoption.js +0 -102
  43. package/dist/affected.d.ts +0 -88
  44. package/dist/affected.js +0 -222
  45. package/dist/capture.d.ts +0 -193
  46. package/dist/capture.js +0 -344
  47. package/dist/comparison.d.ts +0 -97
  48. package/dist/comparison.js +0 -189
  49. package/dist/factory-calibration.d.ts +0 -63
  50. package/dist/factory-calibration.js +0 -115
  51. package/dist/intervention-results.d.ts +0 -6
  52. package/dist/intervention-results.js +0 -52
  53. package/dist/intervention.d.ts +0 -123
  54. package/dist/intervention.js +0 -218
  55. package/dist/investigation.d.ts +0 -94
  56. package/dist/investigation.js +0 -148
  57. package/dist/judge-agreement.d.ts +0 -19
  58. package/dist/judge-agreement.js +0 -31
  59. package/dist/qualification-capture.d.ts +0 -44
  60. package/dist/qualification-capture.js +0 -163
  61. package/dist/qualification-config.d.ts +0 -233
  62. package/dist/qualification-config.js +0 -694
  63. package/dist/qualification-lock.d.ts +0 -15
  64. package/dist/qualification-lock.js +0 -87
  65. package/dist/qualification-oauth-directory.d.ts +0 -79
  66. package/dist/qualification-oauth-directory.js +0 -444
  67. package/dist/qualification-panel-store.d.ts +0 -14
  68. package/dist/qualification-panel-store.js +0 -188
  69. package/dist/qualification-panels.d.ts +0 -99
  70. package/dist/qualification-panels.js +0 -91
  71. package/dist/qualification-process.d.ts +0 -13
  72. package/dist/qualification-process.js +0 -190
  73. package/dist/qualification-runner.d.ts +0 -203
  74. package/dist/qualification-runner.js +0 -2037
  75. package/dist/qualification-store.d.ts +0 -187
  76. package/dist/qualification-store.js +0 -833
  77. package/dist/qualification-validation.d.ts +0 -12
  78. package/dist/qualification-validation.js +0 -8
  79. package/dist/screen.d.ts +0 -31
  80. package/dist/screen.js +0 -105
  81. package/dist/trajectory-gates.d.ts +0 -137
  82. package/dist/trajectory-gates.js +0 -576
  83. package/dist/trusted-host-supervision.d.ts +0 -25
  84. package/dist/trusted-host-supervision.js +0 -94
  85. package/dist/work-capture-schema.d.ts +0 -38
  86. package/dist/work-capture-schema.js +0 -33
  87. package/dist/work-capture.d.ts +0 -43
  88. package/dist/work-capture.js +0 -81
  89. package/dist/work-signals.d.ts +0 -55
  90. package/dist/work-signals.js +0 -71
  91. package/schemas/qualification-cell-result-v1.schema.json +0 -32
  92. package/schemas/qualification-config-v1.schema.json +0 -244
  93. package/schemas/qualification-invocation-request-v1.schema.json +0 -59
  94. package/schemas/qualification-judge-panel-result-v1.schema.json +0 -42
  95. 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
 
@@ -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
- /** True only when the adapter computes final provider-payload observations. */
95
- observesPrompts?: boolean;
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
@@ -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 ["affected", "archive", "coverage", "init", "judge-agreement", "learning", "lint", "list", "rescore", "restamp", "screen", "stability"];
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;
@@ -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>;
@@ -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 "./capture.js";
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 cost = null;
47
- let inputTokens = 0;
48
- let outputTokens = 0;
49
- let cacheReadTokens = 0;
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
- if (msg.usage && (typeof msg.usage.input === "number" || typeof msg.usage.output === "number" ||
134
- typeof msg.usage.cacheRead === "number" || typeof msg.usage.cacheWrite === "number" ||
135
- typeof msg.usage.cost?.total === "number"))
136
- sawUsage = true;
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: cost ?? 0,
172
- cost_source: costSource,
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: cost,
201
- // Tool calls remain in the trace for objective gates. Aggregate usage/cost/
202
- // tool metrics are published only when pi actually reported usage; otherwise
203
- // zero would mean "free" instead of "unavailable".
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 + trace.metrics.input_tokens,
354
- output_tokens: sum.output_tokens + trace.metrics.output_tokens,
355
- cache_read_tokens: sum.cache_read_tokens + trace.metrics.cache_read_tokens,
356
- cache_write_tokens: sum.cache_write_tokens + trace.metrics.cache_write_tokens,
357
- cost_usd: sum.cost_usd + trace.metrics.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: 0, output_tokens: 0, cache_read_tokens: 0, cache_write_tokens: 0,
364
- cost_usd: 0, cost_source: traces[0].metrics.cost_source, tool_calls: 0, delegated_children: 0, max_concurrency: 0,
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 "./capture.js";
36
+ export * from "./redaction.js";
47
37
  export * from "./execution-trace.js";
48
38
  export * from "./trace-gates.js";
49
- export * from "./trajectory-gates.js";
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 "./capture.js";
36
+ export * from "./redaction.js";
47
37
  export * from "./execution-trace.js";
48
38
  export * from "./trace-gates.js";
49
- export * from "./trajectory-gates.js";
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: [], pendingCaptures: [] };
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, into) => {
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)[into].push(id);
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)[into].push(id);
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, "scenarios");
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;
@@ -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;