@bjoernrochel/sevro 0.1.0-rc.4 → 0.1.0-rc.5
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/CONTRIBUTING.md +43 -2
- package/docs/installing.md +2 -2
- package/docs/releases.md +47 -23
- package/docs/runtime-v1.md +28 -5
- package/docs/typescript-quality.md +16 -3
- package/package.json +1 -1
- package/src/case-validation.ts +82 -0
- package/src/cli.ts +16 -93
- package/src/evaluation-check-plan.ts +510 -0
- package/src/evaluation-context.ts +257 -0
- package/src/evaluation-fixture.ts +3 -5
- package/src/evaluation-grading-host.ts +28 -0
- package/src/evaluation-invocation-plan.ts +548 -0
- package/src/evaluation-plan.ts +15 -1034
- package/src/evaluation-run.ts +19 -1404
- package/src/evaluation-semantic.ts +287 -0
- package/src/evaluation-trial.ts +946 -0
- package/src/evaluation-types.ts +1 -0
- package/src/extension-session.ts +8 -1
- package/src/hosts/claude-events.ts +6 -9
- package/src/hosts/claude-nested-skills.ts +7 -10
- package/src/hosts/claude-process.ts +24 -59
- package/src/hosts/claude-repository-invocation.ts +4 -7
- package/src/hosts/claude-settings.ts +86 -16
- package/src/hosts/claude-state.ts +376 -0
- package/src/hosts/claude-tool-calls.ts +10 -15
- package/src/hosts/claude.ts +28 -345
- package/src/hosts/codex-command-output.ts +7 -11
- package/src/hosts/codex-events.ts +5 -7
- package/src/hosts/codex-native-calls.ts +14 -16
- package/src/hosts/codex-process.ts +26 -66
- package/src/hosts/codex-profile.ts +52 -15
- package/src/hosts/codex-state.ts +386 -0
- package/src/hosts/codex.ts +37 -396
- package/src/hosts/native-controls.ts +5 -9
- package/src/hosts/process-lifecycle.ts +60 -0
- package/src/identity.ts +6 -3
- package/src/native-transcript-state.ts +20 -2
- package/src/native-transcript-view.ts +1 -1
- package/src/native-transcripts.ts +1 -1
- package/src/preparation.ts +2 -2
- package/src/provenance.ts +1 -6
- package/src/runtime-config.ts +12 -4
- package/src/runtime-paths.ts +40 -11
- package/src/runtime-seeds.ts +2 -5
- package/src/value-guards.ts +6 -0
package/CONTRIBUTING.md
CHANGED
|
@@ -71,6 +71,41 @@ Run `bun run check:docs:external` separately and [triage network failures](docs/
|
|
|
71
71
|
Guide changes also need [native evals](docs/guide-evaluation.md).
|
|
72
72
|
Documentation checks supplement #1 rather than replacing its contract.
|
|
73
73
|
|
|
74
|
+
## Delivery and CI ownership
|
|
75
|
+
|
|
76
|
+
An implementation PR is complete only when required CI checks pass for its exact
|
|
77
|
+
published commit. A request to open or update a PR includes monitoring CI and
|
|
78
|
+
repairing in-scope failures; it does not authorize merging or unrelated changes.
|
|
79
|
+
|
|
80
|
+
- Use GitHub CI in `.github/workflows/verify.yml` for full acceptance validation.
|
|
81
|
+
It provides the configured OS, architecture, runtime and native-tool versions,
|
|
82
|
+
non-root user, and isolation prerequisites. Reproducing that environment locally
|
|
83
|
+
with Docker is optional for diagnosis and is not a publication prerequisite.
|
|
84
|
+
- After the local pre-commit checks, publish the authorized candidate to its PR
|
|
85
|
+
and require `bun run check:typescript` to succeed in GitHub CI before reporting
|
|
86
|
+
delivery as complete. A local run, fast check, targeted suite, or historical
|
|
87
|
+
result does not establish that the current Ubuntu gate passes.
|
|
88
|
+
- Keep the quality bar intact. Repair behavior or add meaningful coverage for
|
|
89
|
+
accepted behavior; do not lower thresholds, exempt authored sources, skip tests,
|
|
90
|
+
bypass checks, or add artificial tests just to move a counter. Remove impossible
|
|
91
|
+
branches by clarifying validated invariants rather than inventing impossible inputs.
|
|
92
|
+
- Bind validation to the candidate's exact source and commit. Changes to source,
|
|
93
|
+
tests, tooling, dependencies, schemas, workflows, or guidance invalidate prior
|
|
94
|
+
candidate evidence; require the applicable CI gates on the updated published head.
|
|
95
|
+
- After authorized publication, observe required checks for the exact PR head
|
|
96
|
+
until they finish. Queued or running checks, a successful older commit, and a
|
|
97
|
+
passing subset are not completion.
|
|
98
|
+
- On failure, inspect the failed job and retained artifacts, reproduce the cause
|
|
99
|
+
locally when useful, repair within the authorized scope, and publish a non-force
|
|
100
|
+
update to the same PR. Continue monitoring without waiting for the user to notice
|
|
101
|
+
the failure or request correction; do not open a duplicate PR or merge it.
|
|
102
|
+
- If an external blocker prevents progress, report its concrete evidence and the
|
|
103
|
+
unfinished checks. Keep the PR's status and description honest; do not report
|
|
104
|
+
delivery as complete while required checks remain failing or unobserved.
|
|
105
|
+
|
|
106
|
+
Release metadata follows `docs/releases.md` and its existing verified-source CI
|
|
107
|
+
reuse policy; these implementation gates do not require release test reruns.
|
|
108
|
+
|
|
74
109
|
## Tests, fixtures, and schemas
|
|
75
110
|
|
|
76
111
|
Preserve real Bun filesystem/process integration tests and native isolation.
|
|
@@ -105,10 +140,16 @@ native trials or remote CI results.
|
|
|
105
140
|
|
|
106
141
|
## Maintainer releases
|
|
107
142
|
|
|
108
|
-
Follow [releases](docs/releases.md):
|
|
109
|
-
the
|
|
143
|
+
Follow [releases](docs/releases.md): reuse successful source CI, choose a new
|
|
144
|
+
version, and submit the release commit through a PR. Release PRs reuse the
|
|
145
|
+
existing full quality gate and run static metadata checks; releases do not
|
|
146
|
+
rerun local or CI tests. After the PR merges, prepare and inspect the exact
|
|
147
|
+
tarball and verify its license, provenance, and checksums before publication.
|
|
110
148
|
After changing `package.json.version`, run `bun run docs:sync` to refresh the
|
|
111
149
|
[current release](docs/installing.md#current-release). `check:docs`, release
|
|
112
150
|
preparation, and package-install validation reject stale release documentation.
|
|
151
|
+
Package-install, native, and Darrow compatibility tests belong to development
|
|
152
|
+
or an explicitly requested diagnostic, not the release process. Darrow's
|
|
153
|
+
dependency update is a separate PR.
|
|
113
154
|
Publication, tags, registry changes, and alternative licensing are separate
|
|
114
155
|
maintainer actions; preparing a contribution does not authorize them.
|
package/docs/installing.md
CHANGED
|
@@ -22,10 +22,10 @@ Local installation records the selected version in the project lockfile.
|
|
|
22
22
|
|
|
23
23
|
## Current release
|
|
24
24
|
|
|
25
|
-
The current release is `0.1.0-rc.
|
|
25
|
+
The current release is `0.1.0-rc.5`. Install this exact version:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
bun add --exact @bjoernrochel/sevro@0.1.0-rc.
|
|
28
|
+
bun add --exact @bjoernrochel/sevro@0.1.0-rc.5
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
<!-- sevro-current-release:end -->
|
package/docs/releases.md
CHANGED
|
@@ -28,8 +28,9 @@ The record identifies the package, version, release tag, distribution tag,
|
|
|
28
28
|
tarball SHA-256 and npm SHA-512 integrity, and the preparation runtimes.
|
|
29
29
|
Preparation does not publish a package or create a Git tag.
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
For development or explicitly requested diagnostics, the package installation
|
|
32
|
+
check accepts `--tarball <absolute file>` to verify an exact candidate without
|
|
33
|
+
repacking. It is not a release step. The supplied tarball remains intact.
|
|
33
34
|
Its installed name and version must match the source metadata. Installation,
|
|
34
35
|
CLI execution, generic grading, prompt-only execution, release provenance,
|
|
35
36
|
reporting, and supported deterministic native fixtures run without source Git
|
|
@@ -37,43 +38,66 @@ metadata in the installed package.
|
|
|
37
38
|
|
|
38
39
|
## Maintainer workflow
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
Release already verified code. Do not rerun tests locally or in CI as part of a
|
|
42
|
+
release: reuse the successful source CI run. Code, dependencies, schemas, tests,
|
|
43
|
+
examples, license changes, and other quality inputs belong in ordinary changes
|
|
44
|
+
with their own CI before starting the release.
|
|
45
|
+
|
|
46
|
+
1. Start a release branch from verified `main`. Update `package.json.version`
|
|
47
|
+
to a new release version, keep the selected license, and run `bun run docs:sync`.
|
|
48
|
+
Set `publishConfig.tag` to `latest` for the recommended default or `next`
|
|
49
|
+
for an opt-in preview. Commit and open a pull request; every release commit
|
|
50
|
+
must reach `main` through a PR. Never attempt a direct release push to `main`.
|
|
51
|
+
2. Merge the release PR after its static checks and reused CI evidence pass.
|
|
52
|
+
Release CI compares the candidate against a successful ancestor run on
|
|
53
|
+
`main`, permitting only version/distribution-tag changes and the exact
|
|
54
|
+
release documentation and workflow files inventoried in
|
|
55
|
+
[the CI selector](../.github/scripts/release-ci.py). All other tracked inputs
|
|
56
|
+
must match. The original run must have completed the full quality gate;
|
|
57
|
+
another release's reused status is insufficient. CI retains the source SHA
|
|
58
|
+
and run URL in `release-ci.json`. Missing evidence fails the release check;
|
|
59
|
+
it does not trigger another test run. Ordinary code changes and explicit
|
|
60
|
+
workflow dispatches continue to run the full gate.
|
|
61
|
+
3. From the clean, merged release commit, use macOS, Bun 1.3.13, Node 24,
|
|
62
|
+
and npm to prepare one exact candidate:
|
|
47
63
|
|
|
48
64
|
```sh
|
|
49
65
|
bun install --frozen-lockfile
|
|
50
|
-
bun run
|
|
66
|
+
bun run release:prepare --tag "v${SEVRO_RELEASE_VERSION:?set the selected new release version}" --output /absolute/new/release-directory
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
4. Inspect the version, repository, license, distribution tag, file inventory,
|
|
70
|
+
and retained checksums. Publish that tarball with lifecycle scripts disabled
|
|
71
|
+
using the commands below. Do not run `check:typescript`, `test:package-install`,
|
|
72
|
+
Darrow's archive/installed gates, or native trials during release. Diagnostic
|
|
73
|
+
tests require a separate explicit request.
|
|
74
|
+
5. Once npm serves the version, compare `dist.integrity` with `release.json`
|
|
75
|
+
and verify the intended distribution tag:
|
|
54
76
|
|
|
55
77
|
```sh
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
npm view "@bjoernrochel/sevro@${SEVRO_RELEASE_VERSION}" version dist.integrity --json
|
|
79
|
+
npm view @bjoernrochel/sevro dist-tags --json
|
|
58
80
|
```
|
|
59
81
|
|
|
60
|
-
|
|
82
|
+
npm may take a few minutes to process the accepted upload. Wait for the
|
|
83
|
+
registry record rather than republishing. Retain the reviewed tarball,
|
|
84
|
+
checksums, and original CI evidence.
|
|
85
|
+
|
|
86
|
+
6. Create the matching Git tag at the merged release commit and push only the tag:
|
|
61
87
|
|
|
62
88
|
```sh
|
|
63
|
-
|
|
89
|
+
git tag -a "v${SEVRO_RELEASE_VERSION}" -m "Release ${SEVRO_RELEASE_VERSION}"
|
|
90
|
+
git push origin "refs/tags/v${SEVRO_RELEASE_VERSION}"
|
|
64
91
|
```
|
|
65
92
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Darrow, and run its frozen installed compatibility gate. Retain the prior
|
|
71
|
-
version's evidence for updates. Roll back through a new dependency/lockfile
|
|
72
|
-
commit and rerun the installed gate; never relabel failed-version evidence.
|
|
93
|
+
Darrow dependency/lockfile updates and compatibility gates are a separate
|
|
94
|
+
Darrow PR, not prerequisites for npm publication. A prerelease does not establish
|
|
95
|
+
full Darrow migration completion. Preserve prior-version evidence for updates
|
|
96
|
+
and rollback; never relabel failed-version evidence.
|
|
73
97
|
|
|
74
98
|
## Local macOS publication
|
|
75
99
|
|
|
76
|
-
There is no GitHub release job. The maintainer prepares,
|
|
100
|
+
There is no GitHub release job. The maintainer prepares, inspects, and publishes
|
|
77
101
|
the reviewed tarball from macOS with an authenticated npm account. Check its
|
|
78
102
|
retained checksum immediately before publication:
|
|
79
103
|
|
package/docs/runtime-v1.md
CHANGED
|
@@ -50,8 +50,19 @@ declare only non-secret values. Credential names and runner-owned variables
|
|
|
50
50
|
such as `HOME`, temporary-directory variables, `CODEX_HOME`, `CLAUDE_CONFIG_DIR`,
|
|
51
51
|
shell startup overrides, subprocess injection flags, and `UV_OFFLINE` are refused.
|
|
52
52
|
|
|
53
|
-
An inherited or explicit `PATH` must contain nonempty absolute entries.
|
|
54
|
-
|
|
53
|
+
An inherited or explicit `PATH` must contain nonempty absolute entries. For an
|
|
54
|
+
inherited `PATH`, Sevro removes entries whose directory, installation prefix, or
|
|
55
|
+
symlinked tools overlap credential roots, including `~/.codex` and `~/.claude`.
|
|
56
|
+
This lets evaluations invoked from a Codex CLI session use ordinary host tools
|
|
57
|
+
without manually removing injected paths. Lexical paths and canonical targets
|
|
58
|
+
are checked, including absent credential paths. The remaining entries keep their
|
|
59
|
+
original order; the effective `PATH` is retained in runtime evidence. A `PATH`
|
|
60
|
+
with no remaining entries fails rather than selecting fallback tools.
|
|
61
|
+
|
|
62
|
+
An explicit `environment.set.PATH` remains authoritative and conflicting grants
|
|
63
|
+
fail, even when `PATH` is also inherited. Source and evaluator overlaps, invalid
|
|
64
|
+
entries, and unreadable unprotected directories remain errors for either form.
|
|
65
|
+
Missing unprotected entries are inert. Sevro grants read-only access to existing directories,
|
|
55
66
|
resolved symlinked tools, and bounded installation prefixes above `bin`.
|
|
56
67
|
An inaccessible individual symlink target is inert; an unreadable PATH directory
|
|
57
68
|
still fails configuration. Explicitly declared read roots must remain readable.
|
|
@@ -145,9 +156,21 @@ than credential-bearing homes or evaluator directories.
|
|
|
145
156
|
`{{sevro.runtime}}` in an environment value resolves to that role's writable
|
|
146
157
|
root. Sevro supplies private `HOME` and temporary storage and sets `UV_OFFLINE=1`
|
|
147
158
|
and `PYTHONDONTWRITEBYTECODE=1`. Candidate, hook, checker, semantic, and advisory
|
|
148
|
-
state are separate.
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
state are separate. With a runtime policy, native candidate, semantic, and
|
|
160
|
+
advisory roots live in a unique runner-owned host directory outside the Git
|
|
161
|
+
fixture. The root contains `home`, `tmp`, and declared seed slots; plugins can
|
|
162
|
+
use paths such as `~/.darrow/reviews` without creating repository-local review
|
|
163
|
+
state. Command runtime directories are separate from host authentication,
|
|
164
|
+
configuration, and live transcript sources. Checker and hook caches also live
|
|
165
|
+
outside candidate-controlled workspace state. Private roots survive native
|
|
166
|
+
continuation turns and children, then are removed with their owning trial or
|
|
167
|
+
host state after owned hooks terminate. Runs without a selected runtime policy
|
|
168
|
+
retain their previous writable-state locations.
|
|
169
|
+
|
|
170
|
+
Claude runtime policies refuse a private namespace overlapping the native CLI's
|
|
171
|
+
implicit `/tmp/claude` or `/private/tmp/claude` write roots, including canonical
|
|
172
|
+
symlink targets. A conflicting `TMPDIR` placement fails before native launch;
|
|
173
|
+
choose a temporary base outside those cache trees.
|
|
151
174
|
|
|
152
175
|
The same declared tool environment reaches native candidates, isolated shell
|
|
153
176
|
checks, and native semantic/advisory hosts. Candidate plugin-hook authority is
|
|
@@ -170,9 +170,22 @@ GitHub documents the [Ubuntu runner labels](https://docs.github.com/en/actions/r
|
|
|
170
170
|
Ubuntu installs `bubblewrap` and `socat` and runs the same native isolation cases;
|
|
171
171
|
local macOS runs retain `sandbox-exec`. Windows remains unverified.
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
or failed gate cannot turn that status
|
|
173
|
+
Ordinary changes and explicit workflow dispatches invoke the canonical gate,
|
|
174
|
+
upload `.quality` even when coverage fails, and publish the stable aggregate
|
|
175
|
+
`TypeScript quality` status. A cancelled or failed gate cannot turn that status
|
|
176
|
+
green.
|
|
177
|
+
|
|
178
|
+
Release metadata changes follow [the release workflow](releases.md) and reuse a
|
|
179
|
+
successful full gate from an ancestor on `main`, with identical code,
|
|
180
|
+
dependencies, schemas, tests, and other quality inputs. The selector permits
|
|
181
|
+
only version/distribution-tag changes and its exact release-file inventory.
|
|
182
|
+
It verifies the original full-gate step, so a reused release run cannot serve
|
|
183
|
+
as fresh test evidence. Release CI runs `check:typescript --fast`, records the
|
|
184
|
+
tested source and CI run URL, and skips the test/coverage job. The aggregate
|
|
185
|
+
status requires both verified prior evidence and successful static checks;
|
|
186
|
+
missing, cancelled, failed, or ambiguous evidence cannot establish a pass.
|
|
187
|
+
Reused results remain attributed to their original run rather than being
|
|
188
|
+
reported as newly measured coverage. Releases perform no local test reruns.
|
|
176
189
|
|
|
177
190
|
The [coverage tooling README](../scripts/coverage/README.md) explains the maintained
|
|
178
191
|
pipeline and file responsibilities. Fresh CI reports provide candidate-specific evidence.
|
package/package.json
CHANGED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type ResolvedCase } from "./engine";
|
|
2
|
+
import { prepareGeneratedFixture } from "./generated-fixture";
|
|
3
|
+
import { prepareRepositoryFixture } from "./repository-fixture";
|
|
4
|
+
import {
|
|
5
|
+
isOptionalNonblankString,
|
|
6
|
+
isRecord,
|
|
7
|
+
isStringArray,
|
|
8
|
+
} from "./value-guards";
|
|
9
|
+
|
|
10
|
+
function validGeneratedFixture(value: Record<string, unknown>): boolean {
|
|
11
|
+
try {
|
|
12
|
+
prepareGeneratedFixture(value);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function validRepositoryFixture(value: Record<string, unknown>): boolean {
|
|
20
|
+
try {
|
|
21
|
+
prepareRepositoryFixture({ kind: "repository", ...value });
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function validInlineFixture(value: Record<string, unknown>): boolean {
|
|
29
|
+
return (
|
|
30
|
+
!Object.hasOwn(value, "sourceRef") &&
|
|
31
|
+
Object.hasOwn(value, "files") &&
|
|
32
|
+
isRecord(value.files) &&
|
|
33
|
+
Object.values(value.files).every((content) => typeof content === "string")
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fixtureFields(value: Record<string, unknown>): boolean {
|
|
38
|
+
if (Object.hasOwn(value, "sourceRef")) return validRepositoryFixture(value);
|
|
39
|
+
return validInlineFixture(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validFixture(value: unknown): boolean {
|
|
43
|
+
if (!isRecord(value)) return false;
|
|
44
|
+
if (value.kind === "generated") return validGeneratedFixture(value);
|
|
45
|
+
if (value.kind !== undefined) return false;
|
|
46
|
+
return fixtureFields(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function nonemptyString(value: unknown): value is string {
|
|
50
|
+
return typeof value === "string" && value.length > 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validCaseDetails(value: Record<string, unknown>): boolean {
|
|
54
|
+
return (
|
|
55
|
+
nonemptyString(value.id) &&
|
|
56
|
+
nonemptyString(value.prompt) &&
|
|
57
|
+
isOptionalNonblankString(value.followUpPrompt) &&
|
|
58
|
+
validFixture(value.fixture)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function validCheck(value: unknown): boolean {
|
|
63
|
+
return (
|
|
64
|
+
isRecord(value) &&
|
|
65
|
+
typeof value.id === "string" &&
|
|
66
|
+
typeof value.grader === "string" &&
|
|
67
|
+
isRecord(value.configuration)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validChecks(value: unknown): boolean {
|
|
72
|
+
return Array.isArray(value) && value.every(validCheck);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isResolvedCase(value: unknown): value is ResolvedCase {
|
|
76
|
+
return (
|
|
77
|
+
isRecord(value) &&
|
|
78
|
+
validCaseDetails(value) &&
|
|
79
|
+
validChecks(value.checks) &&
|
|
80
|
+
isStringArray(value.requiredEvidence)
|
|
81
|
+
);
|
|
82
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { resolvedFixture } from "./resolved-case";
|
|
3
|
-
import { isStringArray } from "./value-guards";
|
|
4
2
|
import { readFile } from "node:fs/promises";
|
|
5
3
|
import { isAbsolute, join } from "node:path";
|
|
6
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { isResolvedCase } from "./case-validation";
|
|
7
6
|
import {
|
|
8
7
|
EvaluationConfigurationError,
|
|
9
8
|
runEvaluation,
|
|
10
9
|
type HostAdapter,
|
|
11
10
|
type ResolvedCase,
|
|
12
11
|
} from "./engine";
|
|
12
|
+
import { openExtensionSession, type ExtensionCase } from "./extension-session";
|
|
13
|
+
import { createClaudeHost } from "./hosts/claude";
|
|
13
14
|
import { createCodexHost } from "./hosts/codex";
|
|
14
15
|
import {
|
|
15
16
|
codexAgentConcurrency,
|
|
16
17
|
configurationRoot,
|
|
17
18
|
} from "./hosts/codex-configuration";
|
|
18
|
-
import { createClaudeHost } from "./hosts/claude";
|
|
19
|
-
import { prepareGeneratedFixture } from "./generated-fixture";
|
|
20
|
-
import { prepareRepositoryFixture } from "./repository-fixture";
|
|
21
19
|
import { prepareInstrumentation } from "./instrumentation";
|
|
22
|
-
import { openExtensionSession, type ExtensionCase } from "./extension-session";
|
|
23
|
-
import { assertCliResult } from "./schema";
|
|
24
|
-
import { reportCommand } from "./report";
|
|
25
20
|
import {
|
|
26
21
|
packageBuildDigest,
|
|
27
22
|
projectIdentityDigest,
|
|
28
23
|
projectProvenance,
|
|
29
24
|
} from "./provenance";
|
|
25
|
+
import { reportCommand } from "./report";
|
|
26
|
+
import { resolvedFixture } from "./resolved-case";
|
|
27
|
+
import { assertCliResult } from "./schema";
|
|
28
|
+
import { isRecord, isStringArray } from "./value-guards";
|
|
30
29
|
|
|
31
30
|
import { InvocationError, parseInvocation } from "./cli-invocation";
|
|
32
31
|
import {
|
|
@@ -35,89 +34,10 @@ import {
|
|
|
35
34
|
} from "./runtime-config";
|
|
36
35
|
import { canonicalRuntimeRoot, requireRuntimeReadRoots } from "./runtime-paths";
|
|
37
36
|
|
|
38
|
-
function record(value: unknown): value is Record<string, unknown> {
|
|
39
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function validGeneratedFixture(value: Record<string, unknown>): boolean {
|
|
43
|
-
try {
|
|
44
|
-
prepareGeneratedFixture(value);
|
|
45
|
-
return true;
|
|
46
|
-
} catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function validRepositoryFixture(value: Record<string, unknown>): boolean {
|
|
52
|
-
try {
|
|
53
|
-
prepareRepositoryFixture({ kind: "repository", ...value });
|
|
54
|
-
return true;
|
|
55
|
-
} catch {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function validInlineFixture(value: Record<string, unknown>): boolean {
|
|
61
|
-
return (
|
|
62
|
-
!Object.hasOwn(value, "sourceRef") &&
|
|
63
|
-
record(value.files) &&
|
|
64
|
-
Object.values(value.files).every((content) => typeof content === "string")
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function fixtureFields(value: Record<string, unknown>): boolean {
|
|
69
|
-
if (Object.hasOwn(value, "sourceRef")) return validRepositoryFixture(value);
|
|
70
|
-
return Object.hasOwn(value, "files") && validInlineFixture(value);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function validFixture(value: unknown): boolean {
|
|
74
|
-
if (!record(value)) return false;
|
|
75
|
-
if (value.kind === "generated") return validGeneratedFixture(value);
|
|
76
|
-
if (value.kind !== undefined) return false;
|
|
77
|
-
return fixtureFields(value);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function nonemptyString(value: unknown): value is string {
|
|
81
|
-
return typeof value === "string" && value.length > 0;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function followUpPrompt(value: unknown): boolean {
|
|
85
|
-
return (
|
|
86
|
-
value === undefined || (typeof value === "string" && Boolean(value.trim()))
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function validCaseDetails(value: Record<string, unknown>): boolean {
|
|
91
|
-
return (
|
|
92
|
-
nonemptyString(value.id) &&
|
|
93
|
-
nonemptyString(value.prompt) &&
|
|
94
|
-
followUpPrompt(value.followUpPrompt) &&
|
|
95
|
-
validFixture(value.fixture)
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function validCheck(value: unknown): boolean {
|
|
100
|
-
return (
|
|
101
|
-
record(value) &&
|
|
102
|
-
typeof value.id === "string" &&
|
|
103
|
-
typeof value.grader === "string" &&
|
|
104
|
-
record(value.configuration)
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function validChecks(value: unknown): boolean {
|
|
109
|
-
return Array.isArray(value) && value.every(validCheck);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
37
|
function parseCase(value: unknown): ResolvedCase {
|
|
113
|
-
if (
|
|
114
|
-
!record(value) ||
|
|
115
|
-
!validCaseDetails(value) ||
|
|
116
|
-
!validChecks(value.checks) ||
|
|
117
|
-
!isStringArray(value.requiredEvidence)
|
|
118
|
-
)
|
|
38
|
+
if (!isResolvedCase(value))
|
|
119
39
|
throw new InvocationError("invalid resolved case file");
|
|
120
|
-
return value
|
|
40
|
+
return value;
|
|
121
41
|
}
|
|
122
42
|
|
|
123
43
|
async function loadCase(path: string): Promise<ResolvedCase> {
|
|
@@ -169,7 +89,7 @@ async function loadExtensionOptions(
|
|
|
169
89
|
"redacted extension configuration file",
|
|
170
90
|
)
|
|
171
91
|
: {};
|
|
172
|
-
if (!
|
|
92
|
+
if (!isRecord(configuration) || !isRecord(redactedConfiguration))
|
|
173
93
|
throw new InvocationError("extension configuration must be JSON objects");
|
|
174
94
|
return { command, configuration, redactedConfiguration };
|
|
175
95
|
}
|
|
@@ -183,7 +103,7 @@ async function loadPreparationSources(
|
|
|
183
103
|
"case source map file",
|
|
184
104
|
);
|
|
185
105
|
if (
|
|
186
|
-
!
|
|
106
|
+
!isRecord(refs) ||
|
|
187
107
|
!Object.values(refs).every(
|
|
188
108
|
(value) => typeof value === "string" && value.startsWith("file:///"),
|
|
189
109
|
)
|
|
@@ -221,7 +141,7 @@ async function adapterModule(path: string): Promise<unknown> {
|
|
|
221
141
|
|
|
222
142
|
async function loadHost(path: string): Promise<HostAdapter> {
|
|
223
143
|
const module = await adapterModule(path);
|
|
224
|
-
const host =
|
|
144
|
+
const host = isRecord(module) ? module.default : null;
|
|
225
145
|
if (!validHostAdapter(host))
|
|
226
146
|
throw new InvocationError(
|
|
227
147
|
"host adapter module has no valid default adapter",
|
|
@@ -248,7 +168,7 @@ function validateHostInstrumentation(host: Record<string, unknown>): void {
|
|
|
248
168
|
|
|
249
169
|
function validHostAdapter(value: unknown): value is Record<string, unknown> {
|
|
250
170
|
return (
|
|
251
|
-
|
|
171
|
+
isRecord(value) &&
|
|
252
172
|
typeof value.id === "string" &&
|
|
253
173
|
typeof value.model === "string" &&
|
|
254
174
|
typeof value.effort === "string" &&
|
|
@@ -295,6 +215,7 @@ function display(value: unknown, json: boolean): void {
|
|
|
295
215
|
grading: { status: string };
|
|
296
216
|
task: { verdict: string };
|
|
297
217
|
evidencePath: string | null;
|
|
218
|
+
diagnostic?: { code: string; message: string };
|
|
298
219
|
cases: Array<{
|
|
299
220
|
caseId: string;
|
|
300
221
|
trials: Array<{
|
|
@@ -307,6 +228,8 @@ function display(value: unknown, json: boolean): void {
|
|
|
307
228
|
`execution=${result.execution.status} grading=${result.grading.status} task=${result.task.verdict}\n`,
|
|
308
229
|
);
|
|
309
230
|
displayDomainOutcomes(result.cases);
|
|
231
|
+
if (result.diagnostic?.code === "sevro.grader.error")
|
|
232
|
+
process.stdout.write(`diagnostic=${result.diagnostic.message}\n`);
|
|
310
233
|
if (result.evidencePath)
|
|
311
234
|
process.stdout.write(`evidence=${result.evidencePath}\n`);
|
|
312
235
|
}
|