sdkproof 0.1.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/LICENSE +21 -0
- package/README.md +192 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +201 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/classify.d.ts +4 -0
- package/dist/core/classify.js +72 -0
- package/dist/core/classify.js.map +1 -0
- package/dist/core/model.d.ts +30 -0
- package/dist/core/model.js +27 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/prompt.d.ts +4 -0
- package/dist/core/prompt.js +43 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/score.d.ts +3 -0
- package/dist/core/score.js +37 -0
- package/dist/core/score.js.map +1 -0
- package/dist/core/stats.d.ts +99 -0
- package/dist/core/stats.js +121 -0
- package/dist/core/stats.js.map +1 -0
- package/dist/core/types.d.ts +162 -0
- package/dist/core/types.js +11 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/verify.d.ts +33 -0
- package/dist/core/verify.js +191 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/drift.d.ts +87 -0
- package/dist/drift.js +105 -0
- package/dist/drift.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +33 -0
- package/dist/models.js +186 -0
- package/dist/models.js.map +1 -0
- package/dist/registry.d.ts +65 -0
- package/dist/registry.js +98 -0
- package/dist/registry.js.map +1 -0
- package/dist/report.d.ts +26 -0
- package/dist/report.js +240 -0
- package/dist/report.js.map +1 -0
- package/dist/run.d.ts +36 -0
- package/dist/run.js +188 -0
- package/dist/run.js.map +1 -0
- package/dist/surface.d.ts +95 -0
- package/dist/surface.js +281 -0
- package/dist/surface.js.map +1 -0
- package/dist/tarball.d.ts +7 -0
- package/dist/tarball.js +66 -0
- package/dist/tarball.js.map +1 -0
- package/dist/tasks.d.ts +41 -0
- package/dist/tasks.js +199 -0
- package/dist/tasks.js.map +1 -0
- package/dist/workspace.d.ts +42 -0
- package/dist/workspace.js +251 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kalpit Rathore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# sdkproof
|
|
2
|
+
|
|
3
|
+
`npx sdkproof zod` asks a model to write small realistic jobs against a package, then compiles every answer against the real installed package with `tsc`, the TypeScript compiler. Pass = it compiles. No model judges another model.
|
|
4
|
+
|
|
5
|
+
You need an API key for the model you want to score. The other half of the tool, `npx sdkproof drift <package>`, needs no key at all.
|
|
6
|
+
|
|
7
|
+
## What a run looks like
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
$ npx sdkproof @tanstack/react-table --model claude-sonnet-5 --limit 3
|
|
11
|
+
|
|
12
|
+
Target: @tanstack/react-table@9.1.2
|
|
13
|
+
Drift: v8 -> v9 — 40 functions, hooks and classes gone from the entrypoint with no deprecation first, in a major that is 1 months old
|
|
14
|
+
Generating 3 x 1 ...
|
|
15
|
+
Verifying ✗✗✗
|
|
16
|
+
|
|
17
|
+
────────────────────────────────────────────────────────────
|
|
18
|
+
@tanstack/react-table@9.1.2 — 0 of 3 answers compiled
|
|
19
|
+
────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
claude-sonnet-5 0/3 compiled (0%, 95% CI 0–56.1%)
|
|
22
|
+
|
|
23
|
+
WHAT BROKE
|
|
24
|
+
|
|
25
|
+
basic-people-table (core, easy)
|
|
26
|
+
TS2724: '"@tanstack/react-table"' has no exported member named 'getCoreRowModel'. Did you mean 'createCoreRowModel'?
|
|
27
|
+
+6 more diagnostics
|
|
28
|
+
|
|
29
|
+
sortable-product-headers (sorting, easy)
|
|
30
|
+
TS2724: '"@tanstack/react-table"' has no exported member named 'useReactTable'. Did you mean 'ReactTable'?
|
|
31
|
+
+7 more diagnostics
|
|
32
|
+
|
|
33
|
+
global-search-employees (filtering, medium)
|
|
34
|
+
TS2724: '"@tanstack/react-table"' has no exported member named 'useReactTable'. Did you mean 'ReactTable'?
|
|
35
|
+
+7 more diagnostics
|
|
36
|
+
|
|
37
|
+
FAILURE PATTERNS
|
|
38
|
+
deprecated-or-removed 8x
|
|
39
|
+
wrong-arguments 3x
|
|
40
|
+
other 12x
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That is a real run on 22 Aug 2026, trimmed at the last line. `TS2724` is the compiler saying "this import doesn't exist, did you mean this other thing".
|
|
44
|
+
|
|
45
|
+
TanStack Table v9 renamed `useReactTable` to `useTable` & renamed the whole row-model family, so `getCoreRowModel` became `createCoreRowModel`. The model still writes v8 from memory. It looks right and it doesn't compile.
|
|
46
|
+
|
|
47
|
+
## What it measures, and what it doesn't
|
|
48
|
+
|
|
49
|
+
It measures **the model**, not the library. A 0 of 3 says the model has not caught up with v9 yet. It says nothing about whether v9 is a good release.
|
|
50
|
+
|
|
51
|
+
The one real limit: it type-checks, it doesn't run anything. Code that compiles can still be wrong at runtime. That is on purpose — a type error against the real installed package is a fact the compiler produced, and anything softer would be one AI grading another, which is the thing this exists to avoid.
|
|
52
|
+
|
|
53
|
+
## Use it
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx sdkproof <package> # score it
|
|
57
|
+
npx sdkproof drift <package> # what its latest major removed, no API key
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Keys, in the environment or a `.env` in the current directory:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export ANTHROPIC_API_KEY=... # scores claude-opus-5 by default
|
|
64
|
+
export OPENAI_API_KEY=... # scores gpt-5 by default
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Both set = both get scored on the same tasks, side by side.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx sdkproof zod
|
|
71
|
+
npx sdkproof @apollo/client@3 --model claude-opus-5 --model gpt-5
|
|
72
|
+
npx sdkproof next --task-count 20 --out ./reports
|
|
73
|
+
npx sdkproof drift @tanstack/react-table
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Check whether it's worth scoring first
|
|
77
|
+
|
|
78
|
+
`drift` reads two published versions straight off npm and diffs the type declarations. No install, no model, no key, a few seconds.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ npx sdkproof drift @apollo/client
|
|
82
|
+
|
|
83
|
+
────────────────────────────────────────────────────────────
|
|
84
|
+
@apollo/client v3.14.1 -> v4.2.12
|
|
85
|
+
────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
v4 landed 12 months ago
|
|
88
|
+
132 exported symbols -> 133 (entry-only diff)
|
|
89
|
+
|
|
90
|
+
WHAT LEFT (24) — functions, hooks and classes gone from the entrypoint with no deprecation first
|
|
91
|
+
This is what a model trained on v3 will still write.
|
|
92
|
+
|
|
93
|
+
ApolloConsumer
|
|
94
|
+
ApolloProvider
|
|
95
|
+
createQueryPreloader
|
|
96
|
+
getApolloContext
|
|
97
|
+
useApolloClient
|
|
98
|
+
useLazyQuery
|
|
99
|
+
useMutation
|
|
100
|
+
useQuery
|
|
101
|
+
useSubscription
|
|
102
|
+
useSuspenseQuery
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
Deprecated first, then removed (8) — these rarely produce drift:
|
|
106
|
+
ApolloError, fromError, fromPromise, isApolloError, parser, resetApolloContext, throwServerError, toPromise
|
|
107
|
+
|
|
108
|
+
(29 type-only export(s) also left the entrypoint. They are listed in --json; a model writes a hook far more often than it writes a type name.)
|
|
109
|
+
|
|
110
|
+
WORTH SCORING — 24 functions, hooks and classes gone from the entrypoint with no deprecation first, in a major that is 12 months old
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Apollo Client 4 moved its React hooks to a different import path, which is why `useQuery` reads as gone from the package root. That's the drift a model hits: the import it writes from memory no longer resolves.
|
|
114
|
+
|
|
115
|
+
Three things narrow that list, all measured across 35 libraries:
|
|
116
|
+
|
|
117
|
+
- **Values before types.** A model writes a hook far more often than it writes a type name, so the 29 removed type aliases sit behind the 24 callables.
|
|
118
|
+
- **A removal that was deprecated first produces almost no drift.** The deprecation is a signal in the training data; a silent removal isn't. Apollo's eight deprecated-then-removed symbols are separated out for that reason.
|
|
119
|
+
- **The window closes.** How old the major is predicts drift better than how big it was. Apollo v4 scored 0 of 12 once it had been out long enough, & zustand v5 was fully absorbed by about 19 months.
|
|
120
|
+
|
|
121
|
+
## How a run works
|
|
122
|
+
|
|
123
|
+
1. Installs the package into a throwaway sandbox under `~/.cache/sdkproof`. Lifecycle scripts are off unless you pass `--allow-scripts`.
|
|
124
|
+
2. Writes 12 small coding tasks from that exact version's own README.
|
|
125
|
+
3. Asks the model to solve them, with no hints about the API surface.
|
|
126
|
+
4. Compiles every answer against the real installed package and reports what broke.
|
|
127
|
+
|
|
128
|
+
Step 2 has two rules the tool enforces rather than requests. A task skeleton may not import from the package under test, and neither the task nor its skeleton may name an export of it. Hand the model `import { useTable }` and you have answered the hard half of the question before it starts. Tasks that break either rule are thrown away and counted in the output.
|
|
129
|
+
|
|
130
|
+
Tasks are cached per `package@version`, so a re-run scores the same set. `--no-task-cache` writes a fresh one.
|
|
131
|
+
|
|
132
|
+
## Options
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
--model <id> model under test; repeatable. claude-*, gpt-*, or an
|
|
136
|
+
explicit anthropic:<id> / openai:<id>
|
|
137
|
+
--task-model <id> model that writes the tasks (default: the first --model)
|
|
138
|
+
--tasks <file> use a task file instead of writing one
|
|
139
|
+
--task-count <n> how many tasks to write (default 12)
|
|
140
|
+
--limit <n> run only the first n tasks
|
|
141
|
+
--concurrency <n> generations in flight (default 5; drop it if the API 429s)
|
|
142
|
+
--max-tokens <n> per-generation cap (default 16000)
|
|
143
|
+
--out <dir> write <package>@<version>.md and .json there
|
|
144
|
+
--json print the raw result JSON instead of the report
|
|
145
|
+
--markdown print the markdown report instead of the terminal one
|
|
146
|
+
--no-task-cache re-write the tasks even if this version has a cached set
|
|
147
|
+
--fresh reinstall the sandbox instead of reusing the cached one
|
|
148
|
+
--allow-scripts run npm lifecycle scripts during install (off by default)
|
|
149
|
+
--tsc <path> use a different tsc entrypoint
|
|
150
|
+
--from <v> --to <v> drift: compare two specific versions
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Things that will trip you up
|
|
154
|
+
|
|
155
|
+
**A package with no type declarations can't be scored.** There is nothing for the compiler to check against, and the run stops and says so.
|
|
156
|
+
|
|
157
|
+
**A refusal is not a failure.** If the model won't attempt a task, no code was written, so nothing about the library was measured. Refusals are reported separately and left out of the score.
|
|
158
|
+
|
|
159
|
+
**A lost task is worse than a slow one.** If generation errors out — an overloaded API, a timeout — the run prints `INCOMPLETE RUN` and exits 3. A partial run that loses the hard tasks scores higher than the real one, so don't publish that number.
|
|
160
|
+
|
|
161
|
+
**A thin README makes weak tasks.** Plenty of packages ship a badge wall that links out to a docs site. The tasks then lean on what the model already knows about the package. The run says so when it happens.
|
|
162
|
+
|
|
163
|
+
## In a script
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { run, computeDrift, driftVerdict, fetchPackument, anthropicAdapter } from "sdkproof";
|
|
167
|
+
|
|
168
|
+
// Check the cheap thing first — no model call, no install.
|
|
169
|
+
const packument = await fetchPackument("@apollo/client");
|
|
170
|
+
const verdict = driftVerdict(await computeDrift(packument));
|
|
171
|
+
if (!verdict.worth) {
|
|
172
|
+
console.log(`skipping: ${verdict.reason}`);
|
|
173
|
+
} else {
|
|
174
|
+
const out = await run({
|
|
175
|
+
spec: "@apollo/client",
|
|
176
|
+
adapters: [anthropicAdapter("claude-opus-5")],
|
|
177
|
+
taskCount: 12,
|
|
178
|
+
concurrency: 5,
|
|
179
|
+
log: console.error,
|
|
180
|
+
});
|
|
181
|
+
console.log(out.markdown);
|
|
182
|
+
if (out.incomplete) process.exit(3);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Exit codes: `0` fine, `1` bad arguments or a broken target, `2` the API key or account is dead, `3` the run lost tasks and is not publishable.
|
|
187
|
+
|
|
188
|
+
## Requirements
|
|
189
|
+
|
|
190
|
+
Node 20.12 or newer, `npm` on the PATH, and network access to the npm registry. One dependency: `typescript`.
|
|
191
|
+
|
|
192
|
+
MIT. Part of [SDKProof](https://sdkproof.dev) — [source](https://github.com/Kalpitrathore/sdkproof/tree/main/packages/cli).
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { FatalApiError } from "./core/model.js";
|
|
6
|
+
import { computeDrift, driftVerdict } from "./drift.js";
|
|
7
|
+
import { adapterFor, defaultAdapters, DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL, hasKeyFor, parseModelRef, } from "./models.js";
|
|
8
|
+
import { fetchPackument, resolveVersion } from "./registry.js";
|
|
9
|
+
import { renderDrift } from "./report.js";
|
|
10
|
+
import { parseSpec, run } from "./run.js";
|
|
11
|
+
const require_ = createRequire(import.meta.url);
|
|
12
|
+
function version() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(readFileSync(require_.resolve("../package.json"), "utf8")).version;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "0.0.0";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const HELP = `
|
|
21
|
+
sdkproof — does the code an AI writes for this package actually compile?
|
|
22
|
+
|
|
23
|
+
USAGE
|
|
24
|
+
npx sdkproof <package>[@version] score a package
|
|
25
|
+
npx sdkproof drift <package> what its latest major removed (no API key needed)
|
|
26
|
+
|
|
27
|
+
HOW A RUN WORKS
|
|
28
|
+
1. installs the package into a throwaway sandbox
|
|
29
|
+
2. writes small, realistic coding tasks from that version's own README
|
|
30
|
+
3. asks the model to solve them, with no hints about the API surface
|
|
31
|
+
4. compiles every answer against the real installed package with tsc
|
|
32
|
+
|
|
33
|
+
Pass = it compiles. No model judges another model.
|
|
34
|
+
|
|
35
|
+
OPTIONS
|
|
36
|
+
--model <id> model under test; repeatable. Accepts claude-*, gpt-*,
|
|
37
|
+
or an explicit anthropic:<id> / openai:<id>.
|
|
38
|
+
Default: every provider you have a key for.
|
|
39
|
+
--task-model <id> model that writes the tasks (default: the first --model)
|
|
40
|
+
--tasks <file> use a task file instead of writing one
|
|
41
|
+
--task-count <n> how many tasks to write (default 12)
|
|
42
|
+
--limit <n> run only the first n tasks
|
|
43
|
+
--concurrency <n> generations in flight (default 5; drop it if the API 429s)
|
|
44
|
+
--max-tokens <n> per-generation cap (default 16000)
|
|
45
|
+
--out <dir> write <package>@<version>.md and .json there
|
|
46
|
+
--json print the raw result JSON instead of the report
|
|
47
|
+
--markdown print the markdown report instead of the terminal one
|
|
48
|
+
--no-task-cache re-write the tasks even if this version has a cached set
|
|
49
|
+
--fresh reinstall the sandbox instead of reusing the cached one
|
|
50
|
+
--allow-scripts run npm lifecycle scripts during install (off by default)
|
|
51
|
+
--tsc <path> use a different tsc entrypoint
|
|
52
|
+
--from <v> --to <v> drift: compare two specific versions
|
|
53
|
+
-h, --help this
|
|
54
|
+
-v, --version print the version
|
|
55
|
+
|
|
56
|
+
KEYS
|
|
57
|
+
ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN, and/or OPENAI_API_KEY.
|
|
58
|
+
A .env in the current directory is loaded if present.
|
|
59
|
+
\`drift\` needs no key at all.
|
|
60
|
+
|
|
61
|
+
EXAMPLES
|
|
62
|
+
npx sdkproof zod
|
|
63
|
+
npx sdkproof @tanstack/react-table@9 --model claude-opus-5 --out ./reports
|
|
64
|
+
npx sdkproof drift @apollo/client
|
|
65
|
+
`;
|
|
66
|
+
const VALUE_FLAGS = new Set([
|
|
67
|
+
"--model", "--task-model", "--tasks", "--task-count", "--limit",
|
|
68
|
+
"--concurrency", "--max-tokens", "--out", "--tsc", "--from", "--to",
|
|
69
|
+
]);
|
|
70
|
+
function parseArgs(argv) {
|
|
71
|
+
const positional = [];
|
|
72
|
+
const values = new Map();
|
|
73
|
+
const bools = new Set();
|
|
74
|
+
for (let i = 0; i < argv.length; i++) {
|
|
75
|
+
const a = argv[i];
|
|
76
|
+
if (!a.startsWith("-")) {
|
|
77
|
+
positional.push(a);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (VALUE_FLAGS.has(a)) {
|
|
81
|
+
const v = argv[++i];
|
|
82
|
+
if (v === undefined)
|
|
83
|
+
throw new Error(`${a} needs a value`);
|
|
84
|
+
values.set(a, [...(values.get(a) ?? []), v]);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
bools.add(a);
|
|
88
|
+
}
|
|
89
|
+
return { positional, values, bools };
|
|
90
|
+
}
|
|
91
|
+
const num = (f, name, fallback) => {
|
|
92
|
+
const raw = f.values.get(name)?.[0];
|
|
93
|
+
if (raw === undefined)
|
|
94
|
+
return fallback;
|
|
95
|
+
const n = Number(raw);
|
|
96
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
97
|
+
throw new Error(`${name} must be a positive number, got "${raw}"`);
|
|
98
|
+
return Math.floor(n);
|
|
99
|
+
};
|
|
100
|
+
async function main() {
|
|
101
|
+
// A .env beside the user's project is where an API key usually already lives.
|
|
102
|
+
try {
|
|
103
|
+
process.loadEnvFile?.(path.join(process.cwd(), ".env"));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// no .env — use the ambient environment
|
|
107
|
+
}
|
|
108
|
+
const flags = parseArgs(process.argv.slice(2));
|
|
109
|
+
// Checked before the help fallback: `--version` carries no positional, and
|
|
110
|
+
// the fallback would otherwise answer it with the whole help text.
|
|
111
|
+
if (flags.bools.has("-v") || flags.bools.has("--version")) {
|
|
112
|
+
console.log(version());
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
const askedForHelp = flags.bools.has("-h") || flags.bools.has("--help");
|
|
116
|
+
if (askedForHelp || !flags.positional.length) {
|
|
117
|
+
console.log(HELP.trim());
|
|
118
|
+
return askedForHelp ? 0 : 1;
|
|
119
|
+
}
|
|
120
|
+
const log = (line) => console.error(line);
|
|
121
|
+
if (flags.positional[0] === "drift") {
|
|
122
|
+
const target = flags.positional[1];
|
|
123
|
+
if (!target)
|
|
124
|
+
throw new Error("drift needs a package name: npx sdkproof drift <package>");
|
|
125
|
+
const { name, version: wanted } = parseSpec(target);
|
|
126
|
+
const packument = await fetchPackument(name);
|
|
127
|
+
const d = await computeDrift(packument, {
|
|
128
|
+
from: flags.values.get("--from")?.[0],
|
|
129
|
+
to: flags.values.get("--to")?.[0] ?? (wanted ? resolveVersion(packument, wanted) : undefined),
|
|
130
|
+
});
|
|
131
|
+
if (flags.bools.has("--json")) {
|
|
132
|
+
console.log(JSON.stringify({ ...d, verdict: driftVerdict(d) }, null, 2));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log(renderDrift(d, driftVerdict(d)));
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
const modelFlags = flags.values.get("--model") ?? [];
|
|
140
|
+
const adapters = modelFlags.length
|
|
141
|
+
? modelFlags.map((m) => {
|
|
142
|
+
const ref = parseModelRef(m);
|
|
143
|
+
if (!hasKeyFor(ref.provider)) {
|
|
144
|
+
throw new Error(ref.provider === "anthropic"
|
|
145
|
+
? `--model ${m} needs ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) in the environment`
|
|
146
|
+
: `--model ${m} needs OPENAI_API_KEY in the environment`);
|
|
147
|
+
}
|
|
148
|
+
return adapterFor(ref);
|
|
149
|
+
})
|
|
150
|
+
: defaultAdapters();
|
|
151
|
+
if (!adapters.length) {
|
|
152
|
+
console.error([
|
|
153
|
+
"No model API key found.",
|
|
154
|
+
"",
|
|
155
|
+
" export ANTHROPIC_API_KEY=... scores " + DEFAULT_ANTHROPIC_MODEL,
|
|
156
|
+
" export OPENAI_API_KEY=... scores " + DEFAULT_OPENAI_MODEL,
|
|
157
|
+
"",
|
|
158
|
+
"Or run the part that needs no key:",
|
|
159
|
+
` npx sdkproof drift ${flags.positional[0]}`,
|
|
160
|
+
].join("\n"));
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
const taskModelFlag = flags.values.get("--task-model")?.[0];
|
|
164
|
+
const outcome = await run({
|
|
165
|
+
spec: flags.positional[0],
|
|
166
|
+
adapters,
|
|
167
|
+
taskModel: taskModelFlag ? adapterFor(parseModelRef(taskModelFlag)) : undefined,
|
|
168
|
+
taskCount: num(flags, "--task-count", 12),
|
|
169
|
+
tasksFile: flags.values.get("--tasks")?.[0],
|
|
170
|
+
limit: flags.values.get("--limit") ? num(flags, "--limit", 0) : undefined,
|
|
171
|
+
concurrency: num(flags, "--concurrency", 5),
|
|
172
|
+
maxTokens: flags.values.get("--max-tokens") ? num(flags, "--max-tokens", 16000) : undefined,
|
|
173
|
+
fresh: flags.bools.has("--fresh"),
|
|
174
|
+
allowScripts: flags.bools.has("--allow-scripts"),
|
|
175
|
+
noTaskCache: flags.bools.has("--no-task-cache"),
|
|
176
|
+
tscEntry: flags.values.get("--tsc")?.[0],
|
|
177
|
+
outDir: flags.values.get("--out")?.[0],
|
|
178
|
+
log,
|
|
179
|
+
});
|
|
180
|
+
if (flags.bools.has("--json"))
|
|
181
|
+
console.log(outcome.json);
|
|
182
|
+
else if (flags.bools.has("--markdown"))
|
|
183
|
+
console.log(outcome.markdown);
|
|
184
|
+
else
|
|
185
|
+
console.log(outcome.terminal);
|
|
186
|
+
// An incomplete run is not a result. Exiting non-zero keeps it out of CI
|
|
187
|
+
// dashboards that would otherwise publish a score built on a shrunk denominator.
|
|
188
|
+
return outcome.incomplete ? 3 : 0;
|
|
189
|
+
}
|
|
190
|
+
main()
|
|
191
|
+
.then((code) => process.exit(code))
|
|
192
|
+
.catch((e) => {
|
|
193
|
+
if (e instanceof FatalApiError) {
|
|
194
|
+
console.error(`\nRUN ABORTED — ${e.message}`);
|
|
195
|
+
console.error("Nothing further was attempted. Fix the account, then re-run.");
|
|
196
|
+
process.exit(2);
|
|
197
|
+
}
|
|
198
|
+
console.error(`\nsdkproof: ${e.message}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|
|
201
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FailureCategory, FailurePattern, Verdict } from "./types.ts";
|
|
2
|
+
export declare function categorize(code: string): FailureCategory;
|
|
3
|
+
/** Bucket every error across all verdicts into ranked failure patterns. */
|
|
4
|
+
export declare function classify(verdicts: Verdict[]): FailurePattern[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Map tsc diagnostic codes to human failure categories.
|
|
2
|
+
const CODE_CATEGORY = {
|
|
3
|
+
TS2339: "hallucinated-member", // property does not exist
|
|
4
|
+
TS2551: "hallucinated-member", // property does not exist — did you mean
|
|
5
|
+
TS2353: "hallucinated-member", // invented object-literal field
|
|
6
|
+
TS2561: "hallucinated-member",
|
|
7
|
+
TS2694: "hallucinated-member",
|
|
8
|
+
// The three canonical shapes of "you imported the API that used to be here":
|
|
9
|
+
// the member moved entrypoint (TS2305), the same with a did-you-mean attached
|
|
10
|
+
// (TS2724), or the default export is gone (TS1192).
|
|
11
|
+
//
|
|
12
|
+
// TS2305 and TS1192 were added to API_SHAPE_CODES on 2026-08-13 but never
|
|
13
|
+
// here, so they fell through to "other" — and "other" is what the scorecard
|
|
14
|
+
// prints as the top failure pattern. Apollo Client 4, this project's flagship
|
|
15
|
+
// finding, publishes "other: 4" for four TS2305s that are the entire point of
|
|
16
|
+
// the measurement. Found 2026-08-18 re-scoring the AI SDK, whose scorecard
|
|
17
|
+
// led with "other: 13x".
|
|
18
|
+
//
|
|
19
|
+
// TS2724 moves with them: it is TS2305 with a suggestion attached, and
|
|
20
|
+
// splitting one error family across two categories is the same inconsistency
|
|
21
|
+
// the 08-13 fix was about. `deprecated-or-removed` was declared in
|
|
22
|
+
// FailureCategory and mapped from nothing; for a harness whose whole subject
|
|
23
|
+
// is removed and moved APIs, it is the accurate label for all three.
|
|
24
|
+
TS2305: "deprecated-or-removed",
|
|
25
|
+
TS2724: "deprecated-or-removed",
|
|
26
|
+
TS1192: "deprecated-or-removed",
|
|
27
|
+
TS2554: "wrong-arguments", // wrong number of arguments
|
|
28
|
+
TS2345: "wrong-arguments", // argument type not assignable
|
|
29
|
+
// The same family one level up: the call or the generic was handed the wrong
|
|
30
|
+
// shape. Unmapped until 2026-08-22, when a `zod` run through the published
|
|
31
|
+
// CLI reported "other: 2x" for two TS2769s that were the whole finding —
|
|
32
|
+
// the identical failure the 08-18 note describes for TS2305.
|
|
33
|
+
TS2769: "wrong-arguments", // no overload matches this call
|
|
34
|
+
TS2314: "wrong-arguments", // generic type requires N type argument(s)
|
|
35
|
+
TS2558: "wrong-arguments", // expected N type arguments, but got M
|
|
36
|
+
TS2707: "wrong-arguments", // generic type requires between N and M type arguments
|
|
37
|
+
TS2347: "wrong-arguments", // untyped function calls may not accept type arguments
|
|
38
|
+
TS2559: "type-mismatch", // no properties in common
|
|
39
|
+
TS2322: "type-mismatch", // type not assignable
|
|
40
|
+
TS2307: "bad-import", // cannot find module
|
|
41
|
+
};
|
|
42
|
+
export function categorize(code) {
|
|
43
|
+
return CODE_CATEGORY[code] ?? "other";
|
|
44
|
+
}
|
|
45
|
+
/** Bucket every error across all verdicts into ranked failure patterns. */
|
|
46
|
+
export function classify(verdicts) {
|
|
47
|
+
const byCategory = new Map();
|
|
48
|
+
for (const v of verdicts) {
|
|
49
|
+
for (const e of v.errors) {
|
|
50
|
+
const category = categorize(e.code);
|
|
51
|
+
const existing = byCategory.get(category);
|
|
52
|
+
if (existing) {
|
|
53
|
+
existing.count += 1;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
byCategory.set(category, {
|
|
57
|
+
category,
|
|
58
|
+
count: 1,
|
|
59
|
+
example: { taskId: v.taskId, model: v.model, message: e.message },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// "other" is the residual bucket and must never lead the scorecard. Ranking
|
|
65
|
+
// purely by count let it: a single failed type import produces one TS2305
|
|
66
|
+
// plus one TS7031 per parameter that lost its annotation, so on the AI SDK
|
|
67
|
+
// the DOWNSTREAM noise (11x "implicitly has an 'any' type") outranked the
|
|
68
|
+
// four removed types that caused it. The consequence was printed as the
|
|
69
|
+
// headline and the cause was printed underneath.
|
|
70
|
+
return [...byCategory.values()].sort((a, b) => Number(a.category === "other") - Number(b.category === "other") || b.count - a.count);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/core/classify.ts"],"names":[],"mappings":"AAEA,wDAAwD;AACxD,MAAM,aAAa,GAAoC;IACrD,MAAM,EAAE,qBAAqB,EAAE,0BAA0B;IACzD,MAAM,EAAE,qBAAqB,EAAE,yCAAyC;IACxE,MAAM,EAAE,qBAAqB,EAAE,gCAAgC;IAC/D,MAAM,EAAE,qBAAqB;IAC7B,MAAM,EAAE,qBAAqB;IAC7B,6EAA6E;IAC7E,8EAA8E;IAC9E,oDAAoD;IACpD,EAAE;IACF,0EAA0E;IAC1E,4EAA4E;IAC5E,8EAA8E;IAC9E,8EAA8E;IAC9E,2EAA2E;IAC3E,yBAAyB;IACzB,EAAE;IACF,uEAAuE;IACvE,6EAA6E;IAC7E,mEAAmE;IACnE,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,iBAAiB,EAAE,4BAA4B;IACvD,MAAM,EAAE,iBAAiB,EAAE,+BAA+B;IAC1D,6EAA6E;IAC7E,2EAA2E;IAC3E,yEAAyE;IACzE,6DAA6D;IAC7D,MAAM,EAAE,iBAAiB,EAAE,gCAAgC;IAC3D,MAAM,EAAE,iBAAiB,EAAE,2CAA2C;IACtE,MAAM,EAAE,iBAAiB,EAAE,uCAAuC;IAClE,MAAM,EAAE,iBAAiB,EAAE,uDAAuD;IAClF,MAAM,EAAE,iBAAiB,EAAE,uDAAuD;IAClF,MAAM,EAAE,eAAe,EAAE,0BAA0B;IACnD,MAAM,EAAE,eAAe,EAAE,sBAAsB;IAC/C,MAAM,EAAE,YAAY,EAAE,qBAAqB;CAC5C,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC;AACxC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,QAAmB;IAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmC,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;oBACvB,QAAQ;oBACR,KAAK,EAAE,CAAC;oBACR,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;iBAClE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,4EAA4E;IAC5E,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,iDAAiD;IACjD,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAC/F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface GenerateRequest {
|
|
2
|
+
system: string;
|
|
3
|
+
user: string;
|
|
4
|
+
maxTokens?: number;
|
|
5
|
+
}
|
|
6
|
+
/** A model that generates code for a task. The "agent under test." */
|
|
7
|
+
export interface ModelAdapter {
|
|
8
|
+
/** the model id, used as the scorecard column name */
|
|
9
|
+
id: string;
|
|
10
|
+
generate(req: GenerateRequest): Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when a model refuses the identical prompt every time it is sampled.
|
|
14
|
+
* Typed so the CLI can record it as a refusal rather than a generic failure —
|
|
15
|
+
* a refusal is an unmeasured task, not a failed one.
|
|
16
|
+
*/
|
|
17
|
+
export declare class RefusalError extends Error {
|
|
18
|
+
readonly attempts: number;
|
|
19
|
+
constructor(attempts: number);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An error that makes every remaining request pointless — an exhausted credit
|
|
23
|
+
* balance, a revoked key. Distinct from a refusal (one task) or a 529 (retry),
|
|
24
|
+
* because the right response is to stop the run, not to keep paying for
|
|
25
|
+
* failures. On 2026-08-05 a credit exhaustion burned 30 further attempts
|
|
26
|
+
* against a dead key before the run ended with nothing.
|
|
27
|
+
*/
|
|
28
|
+
export declare class FatalApiError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a model refuses the identical prompt every time it is sampled.
|
|
3
|
+
* Typed so the CLI can record it as a refusal rather than a generic failure —
|
|
4
|
+
* a refusal is an unmeasured task, not a failed one.
|
|
5
|
+
*/
|
|
6
|
+
export class RefusalError extends Error {
|
|
7
|
+
attempts;
|
|
8
|
+
constructor(attempts) {
|
|
9
|
+
super(`model refused this task ${attempts}x — not library drift, excluded from the score`);
|
|
10
|
+
this.attempts = attempts;
|
|
11
|
+
this.name = "RefusalError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An error that makes every remaining request pointless — an exhausted credit
|
|
16
|
+
* balance, a revoked key. Distinct from a refusal (one task) or a 529 (retry),
|
|
17
|
+
* because the right response is to stop the run, not to keep paying for
|
|
18
|
+
* failures. On 2026-08-05 a credit exhaustion burned 30 further attempts
|
|
19
|
+
* against a dead key before the run ended with nothing.
|
|
20
|
+
*/
|
|
21
|
+
export class FatalApiError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "FatalApiError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAChB,QAAQ;IAA7B,YAAqB,QAAgB;QACnC,KAAK,CAAC,2BAA2B,QAAQ,gDAAgD,CAAC,CAAC;wBADxE,QAAQ;QAE3B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const GENERATION_SYSTEM = "You are an expert TypeScript developer. Implement the requested function using the specified library. " +
|
|
2
|
+
"Respond with ONLY the complete TypeScript module inside a single ```ts code block — no explanation before or after. " +
|
|
3
|
+
"Keep the provided imports and any `declare` lines exactly as given. " +
|
|
4
|
+
"Fill in the function body so the module type-checks against the real installed package.";
|
|
5
|
+
export function buildUserPrompt(task, spec, context) {
|
|
6
|
+
const base = [
|
|
7
|
+
`Library: ${spec.displayName}, imported from "${spec.packageName}".`,
|
|
8
|
+
spec.docsHint,
|
|
9
|
+
"",
|
|
10
|
+
`Task: ${task.prompt}`,
|
|
11
|
+
"",
|
|
12
|
+
"Complete this module:",
|
|
13
|
+
"```ts",
|
|
14
|
+
task.skeleton,
|
|
15
|
+
"```",
|
|
16
|
+
].join("\n");
|
|
17
|
+
// No context => byte-identical to every prompt that produced a published
|
|
18
|
+
// score. That equality is asserted in test/prompt.test.ts, because a silent
|
|
19
|
+
// change here would move six numbers that are already on the web.
|
|
20
|
+
if (!context)
|
|
21
|
+
return base;
|
|
22
|
+
return [
|
|
23
|
+
"Project context — the agent files this library ships:",
|
|
24
|
+
"",
|
|
25
|
+
context,
|
|
26
|
+
"",
|
|
27
|
+
"---",
|
|
28
|
+
"",
|
|
29
|
+
base,
|
|
30
|
+
].join("\n");
|
|
31
|
+
}
|
|
32
|
+
// Prefer the *longest* fenced ts/js block; fall back to the raw text with any
|
|
33
|
+
// stray fence markers stripped. Taking the longest (not the first) guards
|
|
34
|
+
// against a stray early ``` — in a comment or string — truncating the capture,
|
|
35
|
+
// which otherwise produces a broken fragment and a false compile failure.
|
|
36
|
+
const FENCE = /```(?:ts|typescript|tsx|js|javascript)?[^\S\r\n]*\r?\n([\s\S]*?)```/g;
|
|
37
|
+
export function extractCode(text) {
|
|
38
|
+
const blocks = [...text.matchAll(FENCE)].map((m) => m[1].trim());
|
|
39
|
+
if (blocks.length > 0)
|
|
40
|
+
return blocks.reduce((a, b) => (b.length > a.length ? b : a));
|
|
41
|
+
return text.replace(/^```[a-z]*[^\S\r\n]*\r?\n?/i, "").replace(/```\s*$/i, "").trim();
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/core/prompt.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAC5B,wGAAwG;IACxG,sHAAsH;IACtH,sEAAsE;IACtE,yFAAyF,CAAC;AAE5F,MAAM,UAAU,eAAe,CAAC,IAAU,EAAE,IAAkB,EAAE,OAAgB;IAC9E,MAAM,IAAI,GAAG;QACX,YAAY,IAAI,CAAC,WAAW,oBAAoB,IAAI,CAAC,WAAW,IAAI;QACpE,IAAI,CAAC,QAAQ;QACb,EAAE;QACF,SAAS,IAAI,CAAC,MAAM,EAAE;QACtB,EAAE;QACF,uBAAuB;QACvB,OAAO;QACP,IAAI,CAAC,QAAQ;QACb,KAAK;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,yEAAyE;IACzE,4EAA4E;IAC5E,kEAAkE;IAClE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO;QACL,uDAAuD;QACvD,EAAE;QACF,OAAO;QACP,EAAE;QACF,KAAK;QACL,EAAE;QACF,IAAI;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,+EAA+E;AAC/E,0EAA0E;AAC1E,MAAM,KAAK,GAAG,sEAAsE,CAAC;AAErF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,OAAO,IAAI,CAAC,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LostTask, ArmScore, Refusal, Result, Verdict } from "./types.ts";
|
|
2
|
+
/** Aggregate verdicts into a per-model + overall score with ranked failures. */
|
|
3
|
+
export declare function score(library: string, libraryVersion: string, generatedAt: string, verdicts: Verdict[], refusals?: Refusal[], contextArms?: ArmScore[], lost?: LostTask[]): Result;
|