@bjoernrochel/sevro 0.1.0-rc.7 → 1.0.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/CONTRIBUTING.md +22 -6
- package/README.md +5 -1
- package/docs/README.md +19 -15
- package/docs/architecture.md +138 -17
- package/docs/assets/README.md +5 -4
- package/docs/builtin-graders-v1.md +17 -3
- package/docs/codex-host.md +155 -49
- package/docs/contracts.md +2 -1
- package/docs/creating-cases.md +9 -2
- package/docs/development-cli.md +187 -19
- package/docs/documentation-quality.md +12 -2
- package/docs/evaluation-workflow.md +110 -0
- package/docs/extension-protocol-v1.md +26 -3
- package/docs/getting-started.md +23 -2
- package/docs/guide-evaluation.md +33 -1
- package/docs/identity-v1.md +25 -2
- package/docs/installing.md +11 -11
- package/docs/native-controls.md +76 -5
- package/docs/native-hosts.md +196 -7
- package/docs/reading-results.md +102 -0
- package/docs/recipes.md +489 -0
- package/docs/releases.md +31 -12
- package/docs/report-v1.md +29 -1
- package/docs/results-v1.md +141 -4
- package/docs/run-settings-v1.md +147 -0
- package/docs/runtime-v1.md +307 -18
- package/docs/troubleshooting.md +12 -12
- package/docs/typescript-quality.md +89 -26
- package/examples/recipes/candidate.ts +26 -0
- package/examples/recipes/cases/coding-change.json +21 -0
- package/examples/recipes/cases/host-tools.json +21 -0
- package/examples/recipes/cases/mise.json +21 -0
- package/examples/recipes/cases/node.json +21 -0
- package/examples/recipes/cases/python-uv.json +21 -0
- package/examples/recipes/cases/semantic.json +17 -0
- package/examples/recipes/coding-change/check.ts +23 -0
- package/examples/recipes/extension-artifacts.ts +65 -0
- package/examples/recipes/extension-methods.ts +174 -0
- package/examples/recipes/extension.ts +37 -0
- package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/README.md +3 -0
- package/examples/recipes/run-inputs.ts +188 -0
- package/examples/recipes/run-options.ts +131 -0
- package/examples/recipes/run.ts +63 -0
- package/examples/recipes/runtime/empty.json +1 -0
- package/examples/recipes/runtime/goals.json +1 -0
- package/examples/recipes/runtime/mise.json +6 -0
- package/examples/recipes/runtime/path.json +1 -0
- package/examples/recipes/runtime/plugins.json +4 -0
- package/examples/recipes/runtime/python-uv.json +10 -0
- package/examples/recipes/semantic-judge.ts +29 -0
- package/examples/templates/coding/adapter.ts +17 -0
- package/examples/templates/coding/case.json +15 -0
- package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
- package/examples/templates/repository-skill/extension-data.ts +64 -0
- package/examples/templates/repository-skill/extension.ts +31 -0
- package/examples/templates/response/adapter.ts +12 -0
- package/examples/templates/response/case.json +13 -0
- package/package.json +11 -5
- package/schemas/report-v1.schema.json +9 -0
- package/schemas/run-evidence-v1.schema.json +2 -1
- package/schemas/runtime-v1.schema.json +63 -7
- package/src/advisory.ts +2 -2
- package/src/candidate-artifacts.ts +206 -0
- package/src/candidate-codex-state.ts +185 -0
- package/src/case-validation.ts +73 -50
- package/src/cli/cancellation.ts +20 -0
- package/src/cli/command.ts +87 -0
- package/src/cli/completion.ts +15 -0
- package/src/cli/demo.ts +60 -0
- package/src/cli/evaluation.ts +55 -0
- package/src/cli/extension.ts +79 -0
- package/src/cli/help.ts +198 -0
- package/src/cli/hosts.ts +55 -0
- package/src/cli/init-settings.ts +30 -0
- package/src/cli/init.ts +111 -0
- package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
- package/src/cli/loading.ts +151 -0
- package/src/cli/preparation.ts +154 -0
- package/src/cli/presentation.ts +93 -0
- package/src/cli/progress.ts +6 -0
- package/src/cli/run-manifest.ts +128 -0
- package/src/cli/run-options.ts +403 -0
- package/src/cli/run-overrides.ts +69 -0
- package/src/cli/settings.ts +154 -0
- package/src/cli/summary.ts +43 -0
- package/src/cli.ts +13 -591
- package/src/contract-types.ts +94 -0
- package/src/engine.ts +13 -5
- package/src/evaluation/advisory.ts +150 -0
- package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
- package/src/evaluation/collection.ts +88 -0
- package/src/evaluation/context.ts +10 -0
- package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
- package/src/evaluation/grading.ts +235 -0
- package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
- package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
- package/src/evaluation/progress.ts +19 -0
- package/src/evaluation/resources.ts +241 -0
- package/src/evaluation/run.ts +206 -0
- package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
- package/src/evaluation/trial.ts +800 -0
- package/src/evaluation/types.ts +207 -0
- package/src/evidence/host-results.ts +85 -0
- package/src/evidence/retained-artifacts.ts +18 -0
- package/src/evidence/trial-writer.ts +203 -0
- package/src/evidence/usage.ts +17 -0
- package/src/extension-client.ts +34 -96
- package/src/extension-session.ts +24 -48
- package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
- package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
- package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
- package/src/fixtures/create.ts +183 -0
- package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
- package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
- package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
- package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
- package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
- package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
- package/src/fixtures/workspace-state.ts +212 -0
- package/src/generated/cli-result.d.cts +2 -1
- package/src/generated/extension.d.cts +2 -1
- package/src/generated/report.cjs +1 -1
- package/src/generated/report.d.cts +2 -1
- package/src/generated/run-evidence.cjs +1 -1
- package/src/generated/run-evidence.d.cts +2 -1
- package/src/generated/runtime.cjs +1 -1
- package/src/generated/runtime.d.cts +2 -1
- package/src/git.ts +93 -0
- package/src/graders/git-head.ts +29 -84
- package/src/graders/shell.ts +57 -68
- package/src/host-diagnostic-redaction.ts +52 -0
- package/src/host-diagnostics.ts +331 -0
- package/src/hosts/claude-continuation.ts +57 -40
- package/src/hosts/claude-credential.ts +160 -63
- package/src/hosts/claude-events.ts +34 -18
- package/src/hosts/claude-keychain.ts +78 -21
- package/src/hosts/claude-login-recovery.ts +85 -0
- package/src/hosts/claude-login-refresh.ts +178 -0
- package/src/hosts/claude-login-store.ts +162 -0
- package/src/hosts/claude-login.ts +107 -0
- package/src/hosts/claude-native-goal.ts +15 -12
- package/src/hosts/claude-process.ts +10 -50
- package/src/hosts/claude-refresh-lock.ts +81 -0
- package/src/hosts/claude-repository-invocation.ts +9 -34
- package/src/hosts/claude-settings.ts +91 -14
- package/src/hosts/claude-skill-activations.ts +169 -0
- package/src/hosts/claude-state.ts +46 -16
- package/src/hosts/claude-tool-calls.ts +13 -10
- package/src/hosts/claude.ts +122 -73
- package/src/hosts/codex-app-server.ts +78 -52
- package/src/hosts/codex-configuration.ts +5 -10
- package/src/hosts/codex-exec-controls.ts +516 -0
- package/src/hosts/codex-marketplace.ts +12 -10
- package/src/hosts/codex-native-calls.ts +15 -122
- package/src/hosts/codex-process.ts +10 -54
- package/src/hosts/codex-profile.ts +50 -21
- package/src/hosts/codex-session-files.ts +1 -9
- package/src/hosts/codex-state.ts +141 -19
- package/src/hosts/codex.ts +221 -179
- package/src/hosts/isolation-common.ts +15 -11
- package/src/hosts/isolation-roots.ts +13 -20
- package/src/hosts/linux-sandbox.ts +27 -3
- package/src/hosts/mac-sandbox.ts +111 -18
- package/src/hosts/native-controls.ts +72 -22
- package/src/hosts/native-request.ts +55 -0
- package/src/hosts/process-lifecycle.ts +221 -45
- package/src/hosts/protected-worktrees.ts +1 -1
- package/src/hosts/skill-hook-catalog.ts +131 -0
- package/src/hosts/skill-hook-handler.ts +374 -0
- package/src/hosts/skill-hooks.ts +577 -0
- package/src/instrumentation.ts +7 -8
- package/src/native-transcript-state.ts +22 -3
- package/src/native-transcript-view.ts +52 -13
- package/src/native-transcripts.ts +34 -15
- package/src/paths.ts +22 -0
- package/src/process-lifecycle.ts +150 -0
- package/src/provenance.ts +27 -7
- package/src/prune.ts +300 -0
- package/src/report.ts +63 -36
- package/src/resolved-case.ts +1 -1
- package/src/results-lock.ts +42 -0
- package/src/results.ts +14 -18
- package/src/run-owner.ts +4 -4
- package/src/{runtime-config.ts → runtime/config.ts} +182 -44
- package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
- package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
- package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
- package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
- package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
- package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
- package/src/{runtime-query.ts → runtime/query.ts} +5 -5
- package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
- package/src/runtime/shared-cache.ts +130 -0
- package/src/{runtime-state.ts → runtime/state.ts} +28 -11
- package/src/schema-types.ts +73 -161
- package/src/shell.ts +4 -0
- package/src/transcript-retention.ts +146 -0
- package/src/value-guards.ts +22 -0
- package/docs/assets/sevro-logo.png +0 -0
- package/docs/assets/sevro-symbol.svg +0 -1
- package/docs/assets/sevro-wordmark.svg +0 -1
- package/docs/cache-ownership-validation.md +0 -49
- package/docs/claude-continuation-validation.md +0 -47
- package/docs/concurrency-validation.md +0 -86
- package/docs/configuration-root-validation.md +0 -70
- package/docs/engine-slice.md +0 -114
- package/docs/evidence/runtime-startup-30/README.md +0 -185
- package/docs/evidence/runtime-startup-30/after.json +0 -319
- package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
- package/docs/evidence/runtime-startup-30/before.json +0 -403
- package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
- package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
- package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
- package/docs/evidence.md +0 -17
- package/docs/fixture-cleanup-validation.md +0 -70
- package/docs/guide-verification.json +0 -239
- package/src/evaluation-context.ts +0 -257
- package/src/evaluation-fixture.ts +0 -313
- package/src/evaluation-run.ts +0 -198
- package/src/evaluation-trial.ts +0 -946
- package/src/evaluation-types.ts +0 -149
- package/src/hosts/claude-nested-skills.ts +0 -272
- package/src/hosts/codex-command-output.ts +0 -510
- package/src/hosts/codex-executor-command.ts +0 -291
- package/src/hosts/codex-skill-reads.ts +0 -609
- /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
package/src/evaluation-trial.ts
DELETED
|
@@ -1,946 +0,0 @@
|
|
|
1
|
-
import { rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import {
|
|
5
|
-
advisoryPrompt,
|
|
6
|
-
parseAdvisoryAssessment,
|
|
7
|
-
type AdvisoryAssessment,
|
|
8
|
-
} from "./advisory";
|
|
9
|
-
import {
|
|
10
|
-
advisoryBaseRevision,
|
|
11
|
-
buildBlindAdvisoryFixture,
|
|
12
|
-
} from "./advisory-fixture";
|
|
13
|
-
import {
|
|
14
|
-
checkpointEvaluation,
|
|
15
|
-
prepareTrialWorkspace,
|
|
16
|
-
type EvaluationContext,
|
|
17
|
-
} from "./evaluation-context";
|
|
18
|
-
import {
|
|
19
|
-
hostArtifacts,
|
|
20
|
-
hostObservations,
|
|
21
|
-
sha256,
|
|
22
|
-
usage,
|
|
23
|
-
verifyRetainedArtifacts,
|
|
24
|
-
} from "./evaluation-fixture";
|
|
25
|
-
import {
|
|
26
|
-
gradingRuntimePolicy,
|
|
27
|
-
scopedHostArtifacts,
|
|
28
|
-
} from "./evaluation-grading-host";
|
|
29
|
-
import { gradeSemanticChecks } from "./evaluation-semantic";
|
|
30
|
-
import {
|
|
31
|
-
type EvaluationOptions,
|
|
32
|
-
type HostAdapter,
|
|
33
|
-
type HostResult,
|
|
34
|
-
type TrialSummary,
|
|
35
|
-
} from "./evaluation-types";
|
|
36
|
-
import {
|
|
37
|
-
ExtensionSourceChangedError,
|
|
38
|
-
type EvaluationResult,
|
|
39
|
-
} from "./extension-session";
|
|
40
|
-
import { clearFixtureContents } from "./fixture-cleanup";
|
|
41
|
-
import {
|
|
42
|
-
assessGitHeadCheck,
|
|
43
|
-
gitHeadRevision,
|
|
44
|
-
gitHeadState,
|
|
45
|
-
} from "./graders/git-head";
|
|
46
|
-
import { gradeOutput } from "./graders/output";
|
|
47
|
-
import { assessShellCheck, runShellCheck } from "./graders/shell";
|
|
48
|
-
import { evaluationProtectedRoots } from "./hosts/isolation-roots";
|
|
49
|
-
import {
|
|
50
|
-
InstrumentationEvidenceError,
|
|
51
|
-
snapshotInstrumentation,
|
|
52
|
-
verifyAppliedInstrumentation,
|
|
53
|
-
type InstrumentationRequest,
|
|
54
|
-
} from "./instrumentation";
|
|
55
|
-
import { materializeNativeTranscriptView } from "./native-transcript-view";
|
|
56
|
-
import {
|
|
57
|
-
applyTaskVerdictPolicy,
|
|
58
|
-
assessTrial,
|
|
59
|
-
type Assessment,
|
|
60
|
-
type CheckOutcome,
|
|
61
|
-
} from "./results";
|
|
62
|
-
import type { RunEvidenceData } from "./schema-types";
|
|
63
|
-
import { atomicWriteJson } from "./storage";
|
|
64
|
-
import { scheduleTrials } from "./trial-scheduler";
|
|
65
|
-
|
|
66
|
-
const MAX_FINAL_MESSAGE_BYTES = 8 * 1024 * 1024;
|
|
67
|
-
|
|
68
|
-
interface TrialObservation {
|
|
69
|
-
id: string;
|
|
70
|
-
source: string;
|
|
71
|
-
completeness: "complete" | "partial" | "unavailable";
|
|
72
|
-
data: Record<string, unknown>;
|
|
73
|
-
}
|
|
74
|
-
function candidateMarketplaceRequest(context: EvaluationContext) {
|
|
75
|
-
const marketplace = context.codexMarketplace;
|
|
76
|
-
if (!marketplace) return {};
|
|
77
|
-
return {
|
|
78
|
-
codexMarketplace: {
|
|
79
|
-
...marketplace,
|
|
80
|
-
artifactPaths: context.inlineArtifacts
|
|
81
|
-
.filter((artifact) =>
|
|
82
|
-
artifact.relativePath.startsWith(`${marketplace.artifactRoot}/`),
|
|
83
|
-
)
|
|
84
|
-
.map((artifact) => artifact.relativePath),
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function candidatePluginDirectoriesRequest(context: EvaluationContext) {
|
|
90
|
-
const directories = context.claudePluginDirs;
|
|
91
|
-
if (!directories) return {};
|
|
92
|
-
return {
|
|
93
|
-
claudePluginDirs: {
|
|
94
|
-
...directories,
|
|
95
|
-
artifactPaths: context.inlineArtifacts
|
|
96
|
-
.filter((artifact) =>
|
|
97
|
-
directories.artifactRoots.some((root) =>
|
|
98
|
-
artifact.relativePath.startsWith(`${root}/`),
|
|
99
|
-
),
|
|
100
|
-
)
|
|
101
|
-
.map((artifact) => artifact.relativePath),
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function candidateInvocationRequest(context: EvaluationContext) {
|
|
107
|
-
if (context.invocation === undefined || context.invocationToken === null)
|
|
108
|
-
return {};
|
|
109
|
-
return {
|
|
110
|
-
explicitSkillInvocation: {
|
|
111
|
-
...context.invocation,
|
|
112
|
-
token: context.invocationToken,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function ensureNotCancelled(signal: AbortSignal | undefined): void {
|
|
118
|
-
if (signal?.aborted) throw new Error("cancelled");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function hostFailureDiagnostic(execution: string, error: unknown) {
|
|
122
|
-
if (execution === "cancelled")
|
|
123
|
-
return { code: "sevro.run.cancelled", message: "run cancelled" };
|
|
124
|
-
if (error instanceof InstrumentationEvidenceError)
|
|
125
|
-
return { code: "sevro.instrumentation.mismatch", message: error.message };
|
|
126
|
-
return {
|
|
127
|
-
code: "sevro.host.failed",
|
|
128
|
-
message: "host execution did not complete",
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
type AdvisoryReview = {
|
|
133
|
-
status: "completed" | "failed" | "not_run";
|
|
134
|
-
assessment: AdvisoryAssessment | null;
|
|
135
|
-
usage: ReturnType<typeof usage>;
|
|
136
|
-
rawResult: {
|
|
137
|
-
source: string;
|
|
138
|
-
path: string | null;
|
|
139
|
-
sha256: string | null;
|
|
140
|
-
};
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
function defaultAdvisoryReview(host: HostAdapter): AdvisoryReview {
|
|
144
|
-
return {
|
|
145
|
-
status: "not_run",
|
|
146
|
-
assessment: null,
|
|
147
|
-
usage: usage(null),
|
|
148
|
-
rawResult: { source: host.id, path: null, sha256: null },
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function isTrialPersistenceFailure(error: unknown): boolean {
|
|
153
|
-
return (
|
|
154
|
-
error instanceof Error &&
|
|
155
|
-
error.message.startsWith("trial persistence failed")
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function completeAdvisoryAssessment(response: HostResult): AdvisoryAssessment {
|
|
160
|
-
if (
|
|
161
|
-
response.executionFailed ||
|
|
162
|
-
!response.complete ||
|
|
163
|
-
response.finalMessage === null
|
|
164
|
-
)
|
|
165
|
-
throw new Error("advisory response is incomplete");
|
|
166
|
-
return parseAdvisoryAssessment(response.finalMessage);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function extensionFinalMessageData(result: HostResult | null) {
|
|
170
|
-
return result?.finalMessage == null ? {} : { text: result.finalMessage };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function extensionCheckResult(
|
|
174
|
-
check: EvaluationResult["checks"][number],
|
|
175
|
-
declared: Map<string, string>,
|
|
176
|
-
): CheckOutcome {
|
|
177
|
-
const grader = declared.get(check.id);
|
|
178
|
-
if (grader === undefined)
|
|
179
|
-
throw new Error("extension returned an undeclared check");
|
|
180
|
-
return {
|
|
181
|
-
id: check.id,
|
|
182
|
-
grader,
|
|
183
|
-
status: check.status,
|
|
184
|
-
...(check.detail ? { detail: check.detail } : {}),
|
|
185
|
-
evidenceRefs: check.evidenceRefs,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function compareDeclaredChecks(
|
|
190
|
-
left: CheckOutcome,
|
|
191
|
-
right: CheckOutcome,
|
|
192
|
-
order: Map<string, number>,
|
|
193
|
-
): number {
|
|
194
|
-
const leftIndex = order.get(left.id);
|
|
195
|
-
const rightIndex = order.get(right.id);
|
|
196
|
-
// Match the stable NaN comparator fallback; assessment reports undeclared IDs.
|
|
197
|
-
if (leftIndex === undefined || rightIndex === undefined) return 0;
|
|
198
|
-
return leftIndex - rightIndex;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function selectedTrialTaskPolicy(
|
|
202
|
-
options: EvaluationOptions,
|
|
203
|
-
recommendation: "passed" | "failed" | "not_assessed" | null,
|
|
204
|
-
) {
|
|
205
|
-
const id = options.extension?.session.identity.selectedTaskVerdictPolicy;
|
|
206
|
-
return id ? { id, recommendation } : null;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
class EvaluationTrial {
|
|
210
|
-
private candidateTranscriptRoot?: string;
|
|
211
|
-
private diagnostic: { code: string; message: string } | undefined = undefined;
|
|
212
|
-
private persisted: boolean = false;
|
|
213
|
-
private gitHeadBase: string | null = null;
|
|
214
|
-
private advisoryRevision: string | null = null;
|
|
215
|
-
private hostResult: HostResult | null = null;
|
|
216
|
-
private appliedInstrumentation: InstrumentationRequest[] = [];
|
|
217
|
-
private additionalObservations: ReturnType<typeof hostObservations> = [];
|
|
218
|
-
private producedArtifacts: ReturnType<typeof hostArtifacts> = [];
|
|
219
|
-
private execution: "completed" | "failed" | "cancelled" | "not_run";
|
|
220
|
-
private candidateDurationMs: number | null = null;
|
|
221
|
-
private trialArtifactRefs: EvaluationContext["artifactRefs"] = [];
|
|
222
|
-
private rawDigest: string | null = null;
|
|
223
|
-
private completeness: "complete" | "partial" | "unavailable" = "unavailable";
|
|
224
|
-
private observation: TrialObservation = {
|
|
225
|
-
id: "sevro.observation.final-message",
|
|
226
|
-
source: "",
|
|
227
|
-
completeness: "unavailable",
|
|
228
|
-
data: {},
|
|
229
|
-
};
|
|
230
|
-
private shellObservations: TrialObservation[] = [];
|
|
231
|
-
private gitHeadObservations: TrialObservation[] = [];
|
|
232
|
-
private semanticObservations: TrialObservation[] = [];
|
|
233
|
-
private checks: CheckOutcome[] = [];
|
|
234
|
-
private extensionMetrics: EvaluationResult["metrics"] = [];
|
|
235
|
-
private domainOutcomes: NonNullable<EvaluationResult["domainOutcomes"]> = [];
|
|
236
|
-
private taskPolicyRecommendation:
|
|
237
|
-
"passed" | "failed" | "not_assessed" | null = null;
|
|
238
|
-
private graderError: boolean = false;
|
|
239
|
-
private assessment: Assessment = {
|
|
240
|
-
execution: { status: "not_run" },
|
|
241
|
-
grading: { status: "not_requested" },
|
|
242
|
-
task: { verdict: "not_assessed" },
|
|
243
|
-
};
|
|
244
|
-
private advisoryReview: AdvisoryReview | null = null;
|
|
245
|
-
private rawPath: string | null = null;
|
|
246
|
-
constructor(
|
|
247
|
-
private readonly options: EvaluationOptions,
|
|
248
|
-
private readonly context: EvaluationContext,
|
|
249
|
-
private readonly trial: number,
|
|
250
|
-
private readonly stopAdmission: () => void,
|
|
251
|
-
private readonly fixture: Awaited<ReturnType<typeof prepareTrialWorkspace>>,
|
|
252
|
-
) {
|
|
253
|
-
this.execution = options.dry ? "not_run" : "completed";
|
|
254
|
-
}
|
|
255
|
-
async run(): Promise<boolean> {
|
|
256
|
-
try {
|
|
257
|
-
if (this.fixture.preparationCancelled)
|
|
258
|
-
return await this.cancelPreparation();
|
|
259
|
-
await this.initializeGradingBases();
|
|
260
|
-
await this.runCandidate();
|
|
261
|
-
await this.retainCandidateArtifacts();
|
|
262
|
-
this.observeCandidate();
|
|
263
|
-
this.gradeOutput();
|
|
264
|
-
await this.gradeShell();
|
|
265
|
-
await this.gradeGitHead();
|
|
266
|
-
await this.gradeSemantic();
|
|
267
|
-
await this.gradeExtension();
|
|
268
|
-
this.assess();
|
|
269
|
-
await this.runAdvisory();
|
|
270
|
-
await this.retainRawCandidate();
|
|
271
|
-
return await this.persistTrial();
|
|
272
|
-
} finally {
|
|
273
|
-
await this.cleanup();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
private async cancelPreparation(): Promise<boolean> {
|
|
277
|
-
this.failCandidate(undefined);
|
|
278
|
-
await this.retainCandidateArtifacts();
|
|
279
|
-
this.observeCandidate();
|
|
280
|
-
this.assess();
|
|
281
|
-
return this.persistTrial();
|
|
282
|
-
}
|
|
283
|
-
private async initializeGradingBases(): Promise<void> {
|
|
284
|
-
this.gitHeadBase = this.context.preparedGitHead.length
|
|
285
|
-
? await gitHeadRevision(this.fixture.workspace, this.options.signal)
|
|
286
|
-
: null;
|
|
287
|
-
this.advisoryRevision = this.options.advisoryHost
|
|
288
|
-
? await advisoryBaseRevision(this.fixture.workspace)
|
|
289
|
-
: null;
|
|
290
|
-
}
|
|
291
|
-
private candidateRequest(): Parameters<HostAdapter["run"]>[0] {
|
|
292
|
-
return {
|
|
293
|
-
prompt: this.fixture.trialPrompt,
|
|
294
|
-
...(this.fixture.trialFollowUpPrompt !== undefined
|
|
295
|
-
? { followUpPrompt: this.fixture.trialFollowUpPrompt }
|
|
296
|
-
: {}),
|
|
297
|
-
workspace: this.fixture.workspace,
|
|
298
|
-
condition: this.options.condition,
|
|
299
|
-
fixtureBinDir: this.fixture.fixtureBinDir,
|
|
300
|
-
runtimePolicy: this.options.runtimePolicy,
|
|
301
|
-
instrumentation: snapshotInstrumentation(
|
|
302
|
-
this.context.requestedInstrumentation,
|
|
303
|
-
),
|
|
304
|
-
...candidateMarketplaceRequest(this.context),
|
|
305
|
-
...candidatePluginDirectoriesRequest(this.context),
|
|
306
|
-
...candidateInvocationRequest(this.context),
|
|
307
|
-
signal: this.options.signal,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
private candidateReservedIds(): Set<string> {
|
|
312
|
-
return new Set([
|
|
313
|
-
"sevro.observation.final-message",
|
|
314
|
-
...this.context.artifactRefs.map((item) => item.id),
|
|
315
|
-
...this.additionalObservations.map((item) => item.id),
|
|
316
|
-
...this.options.case.checks.map((item) => item.id),
|
|
317
|
-
...this.context.preparedShell.map(
|
|
318
|
-
(item) => `sevro.observation.shell.${item.id}`,
|
|
319
|
-
),
|
|
320
|
-
...(this.context.preparedGitHead.length
|
|
321
|
-
? ["sevro.observation.git-head"]
|
|
322
|
-
: []),
|
|
323
|
-
...(this.context.preparedSemantic.length
|
|
324
|
-
? ["sevro.semantic.verdicts"]
|
|
325
|
-
: []),
|
|
326
|
-
]);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private acceptCandidateResult(response: HostResult): void {
|
|
330
|
-
this.appliedInstrumentation = verifyAppliedInstrumentation(
|
|
331
|
-
this.context.requestedInstrumentation,
|
|
332
|
-
response.appliedInstrumentation,
|
|
333
|
-
this.options.condition,
|
|
334
|
-
response.actualCondition,
|
|
335
|
-
);
|
|
336
|
-
if (
|
|
337
|
-
response.finalMessage !== null &&
|
|
338
|
-
Buffer.byteLength(response.finalMessage, "utf8") > MAX_FINAL_MESSAGE_BYTES
|
|
339
|
-
)
|
|
340
|
-
throw new Error("oversized host result");
|
|
341
|
-
this.additionalObservations = hostObservations(
|
|
342
|
-
response,
|
|
343
|
-
this.options.host.id,
|
|
344
|
-
new Set([
|
|
345
|
-
...this.context.artifactRefs.map((item) => item.id),
|
|
346
|
-
...this.options.case.checks.map((item) => item.id),
|
|
347
|
-
]),
|
|
348
|
-
);
|
|
349
|
-
this.producedArtifacts = hostArtifacts(
|
|
350
|
-
response,
|
|
351
|
-
this.candidateReservedIds(),
|
|
352
|
-
);
|
|
353
|
-
if (response.executionFailed) {
|
|
354
|
-
this.execution = "failed";
|
|
355
|
-
this.stopAdmission();
|
|
356
|
-
this.diagnostic = {
|
|
357
|
-
code: "sevro.host.failed",
|
|
358
|
-
message: "host execution did not complete",
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
private failCandidate(error: unknown): void {
|
|
364
|
-
this.execution = this.options.signal?.aborted ? "cancelled" : "failed";
|
|
365
|
-
this.stopAdmission();
|
|
366
|
-
this.hostResult = null;
|
|
367
|
-
this.diagnostic = hostFailureDiagnostic(this.execution, error);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
private async runCandidate(): Promise<void> {
|
|
371
|
-
if (this.options.dry) return;
|
|
372
|
-
const started = performance.now();
|
|
373
|
-
try {
|
|
374
|
-
ensureNotCancelled(this.options.signal);
|
|
375
|
-
this.hostResult = await this.options.host.run(this.candidateRequest());
|
|
376
|
-
this.acceptCandidateResult(this.hostResult);
|
|
377
|
-
if (this.options.signal?.aborted) {
|
|
378
|
-
this.execution = "cancelled";
|
|
379
|
-
this.stopAdmission();
|
|
380
|
-
this.diagnostic = hostFailureDiagnostic("cancelled", undefined);
|
|
381
|
-
}
|
|
382
|
-
} catch (error) {
|
|
383
|
-
this.failCandidate(error);
|
|
384
|
-
} finally {
|
|
385
|
-
this.candidateDurationMs = Math.max(0, performance.now() - started);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private async writeTrialFile(
|
|
390
|
-
path: string,
|
|
391
|
-
bytes: string | Uint8Array,
|
|
392
|
-
): Promise<void> {
|
|
393
|
-
try {
|
|
394
|
-
await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
|
|
395
|
-
} catch (cause) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`trial persistence failed; fixture retained at ${this.fixture.workspace}`,
|
|
398
|
-
{ cause },
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
private async retainArtifacts(
|
|
404
|
-
artifacts: ReturnType<typeof hostArtifacts>,
|
|
405
|
-
prefix = "",
|
|
406
|
-
): Promise<void> {
|
|
407
|
-
for (const artifact of artifacts) {
|
|
408
|
-
const path = join(
|
|
409
|
-
this.context.runDir,
|
|
410
|
-
`trial-${this.trial}-${prefix}${artifact.id}.bin`,
|
|
411
|
-
);
|
|
412
|
-
await this.writeTrialFile(path, artifact.bytes);
|
|
413
|
-
this.trialArtifactRefs.push({
|
|
414
|
-
id: artifact.id,
|
|
415
|
-
path: pathToFileURL(path).href,
|
|
416
|
-
sha256: sha256(artifact.bytes),
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
private async retainCandidateArtifacts(): Promise<void> {
|
|
422
|
-
this.trialArtifactRefs = [...this.context.artifactRefs];
|
|
423
|
-
await this.retainArtifacts(this.producedArtifacts, "host-");
|
|
424
|
-
await verifyRetainedArtifacts(this.trialArtifactRefs);
|
|
425
|
-
const bundle = this.producedArtifacts.find(
|
|
426
|
-
(item) => item.id === "sevro.native-transcripts.bundle",
|
|
427
|
-
);
|
|
428
|
-
if (this.options.runtimePolicy?.nativeTranscripts && bundle)
|
|
429
|
-
this.candidateTranscriptRoot = await materializeNativeTranscriptView(
|
|
430
|
-
bundle.bytes,
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
private observeCandidate(): void {
|
|
435
|
-
const result = this.hostResult;
|
|
436
|
-
if (result === null || result.finalMessage == null) {
|
|
437
|
-
this.rawDigest = null;
|
|
438
|
-
this.completeness = "unavailable";
|
|
439
|
-
this.observation = {
|
|
440
|
-
id: "sevro.observation.final-message",
|
|
441
|
-
source: this.options.host.id,
|
|
442
|
-
completeness: this.completeness,
|
|
443
|
-
data: {},
|
|
444
|
-
};
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
this.rawDigest = sha256(result.finalMessage);
|
|
448
|
-
this.completeness = result.complete ? "complete" : "partial";
|
|
449
|
-
this.observation = {
|
|
450
|
-
id: "sevro.observation.final-message",
|
|
451
|
-
source: this.options.host.id,
|
|
452
|
-
completeness: this.completeness,
|
|
453
|
-
data: {
|
|
454
|
-
sha256: this.rawDigest,
|
|
455
|
-
byteLength: Buffer.byteLength(result.finalMessage, "utf8"),
|
|
456
|
-
},
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
private failGrading(message: string, cancelable = true): void {
|
|
461
|
-
if (cancelable && this.options.signal?.aborted) {
|
|
462
|
-
this.execution = "cancelled";
|
|
463
|
-
this.diagnostic = {
|
|
464
|
-
code: "sevro.run.cancelled",
|
|
465
|
-
message: "run cancelled",
|
|
466
|
-
};
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
this.graderError = true;
|
|
470
|
-
this.diagnostic = { code: "sevro.grader.error", message };
|
|
471
|
-
this.stopAdmission();
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
private canGrade(): boolean {
|
|
475
|
-
return this.execution === "completed" && !this.graderError;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
private gradeOutput(): void {
|
|
479
|
-
if (this.execution !== "completed") return;
|
|
480
|
-
try {
|
|
481
|
-
const result = this.hostResult;
|
|
482
|
-
if (!result) throw new Error("host result is unavailable");
|
|
483
|
-
this.checks = gradeOutput(
|
|
484
|
-
result.finalMessage,
|
|
485
|
-
result.complete,
|
|
486
|
-
this.context.prepared,
|
|
487
|
-
).map((check) => ({
|
|
488
|
-
...check,
|
|
489
|
-
evidenceRefs: this.rawDigest ? [this.observation.id] : [],
|
|
490
|
-
}));
|
|
491
|
-
} catch {
|
|
492
|
-
this.failGrading("output grading did not complete", false);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
private async shellProtectedRoots(): Promise<string[]> {
|
|
497
|
-
const isolation = this.options.shellIsolation;
|
|
498
|
-
if (!isolation)
|
|
499
|
-
throw new Error("shell checks require explicit protected source roots");
|
|
500
|
-
return evaluationProtectedRoots({
|
|
501
|
-
workspace: this.fixture.workspace,
|
|
502
|
-
projectRoot: this.context.projectRoot,
|
|
503
|
-
resultsRoot: this.options.resultsRoot,
|
|
504
|
-
additionalRoots: [
|
|
505
|
-
...isolation.protectedRoots,
|
|
506
|
-
...(this.options.preparationSources
|
|
507
|
-
? [this.options.preparationSources.root]
|
|
508
|
-
: []),
|
|
509
|
-
...(this.context.repository ? [this.context.repository.path] : []),
|
|
510
|
-
this.context.runStateRoot,
|
|
511
|
-
],
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
private async gradeShellCheck(
|
|
516
|
-
check: EvaluationContext["preparedShell"][number],
|
|
517
|
-
protectedRoots: string[],
|
|
518
|
-
): Promise<void> {
|
|
519
|
-
const result = await runShellCheck(check, {
|
|
520
|
-
candidateTranscriptRoot: this.candidateTranscriptRoot,
|
|
521
|
-
workspace: this.fixture.workspace,
|
|
522
|
-
fixtureBinDir: this.fixture.fixtureBinDir,
|
|
523
|
-
toolchainBinDir: this.options.shellIsolation?.toolchainBinDir,
|
|
524
|
-
runtimePolicy: this.options.runtimePolicy,
|
|
525
|
-
uvRuntimeCache: this.options.shellIsolation?.uvRuntimeCache,
|
|
526
|
-
protectedRoots,
|
|
527
|
-
protectedRootsCanonical: true,
|
|
528
|
-
privateStateRoot: join(this.context.stateDir, "shell-sandbox"),
|
|
529
|
-
signal: this.options.signal,
|
|
530
|
-
});
|
|
531
|
-
const graded = assessShellCheck(check, result);
|
|
532
|
-
const id = `sevro.observation.shell.${check.id}`;
|
|
533
|
-
this.shellObservations.push({
|
|
534
|
-
id,
|
|
535
|
-
source: "sevro.shell",
|
|
536
|
-
completeness: "complete",
|
|
537
|
-
data: {
|
|
538
|
-
exitCode: result.exitCode,
|
|
539
|
-
expectedExitCode: check.expectedExitCode,
|
|
540
|
-
...(result.stdout === null
|
|
541
|
-
? {}
|
|
542
|
-
: {
|
|
543
|
-
stdoutSha256: sha256(result.stdout),
|
|
544
|
-
stdoutByteLength: Buffer.byteLength(result.stdout, "utf8"),
|
|
545
|
-
}),
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
|
-
this.checks.push({
|
|
549
|
-
id: check.id,
|
|
550
|
-
grader: "sevro.shell",
|
|
551
|
-
status: graded.passed ? "passed" : "failed",
|
|
552
|
-
detail: graded.detail,
|
|
553
|
-
evidenceRefs: [id],
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
private async gradeShell(): Promise<void> {
|
|
558
|
-
if (!this.canGrade() || this.context.preparedShell.length === 0) return;
|
|
559
|
-
try {
|
|
560
|
-
const protectedRoots = await this.shellProtectedRoots();
|
|
561
|
-
for (const check of this.context.preparedShell)
|
|
562
|
-
await this.gradeShellCheck(check, protectedRoots);
|
|
563
|
-
} catch {
|
|
564
|
-
this.failGrading("shell grading did not complete");
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
private async gradeGitHead(): Promise<void> {
|
|
569
|
-
if (!this.canGrade() || this.context.preparedGitHead.length === 0) return;
|
|
570
|
-
try {
|
|
571
|
-
const base = this.gitHeadBase;
|
|
572
|
-
if (base === null)
|
|
573
|
-
throw new Error("Git HEAD base revision is unavailable");
|
|
574
|
-
const state = await gitHeadState(
|
|
575
|
-
this.fixture.workspace,
|
|
576
|
-
base,
|
|
577
|
-
this.options.signal,
|
|
578
|
-
);
|
|
579
|
-
const id = "sevro.observation.git-head";
|
|
580
|
-
this.gitHeadObservations.push({
|
|
581
|
-
id,
|
|
582
|
-
source: "sevro.git-head",
|
|
583
|
-
completeness: "complete",
|
|
584
|
-
data: {
|
|
585
|
-
baseRevision: base,
|
|
586
|
-
currentRevision: state.currentRevision,
|
|
587
|
-
baseAncestor: state.baseAncestor,
|
|
588
|
-
},
|
|
589
|
-
});
|
|
590
|
-
this.checks.push(
|
|
591
|
-
...this.context.preparedGitHead.map((check) => {
|
|
592
|
-
const graded = assessGitHeadCheck(check, base, state);
|
|
593
|
-
return {
|
|
594
|
-
id: check.id,
|
|
595
|
-
grader: "sevro.git-head",
|
|
596
|
-
status: graded.passed ? ("passed" as const) : ("failed" as const),
|
|
597
|
-
detail: graded.detail,
|
|
598
|
-
evidenceRefs: [id],
|
|
599
|
-
};
|
|
600
|
-
}),
|
|
601
|
-
);
|
|
602
|
-
} catch {
|
|
603
|
-
this.failGrading("Git HEAD grading did not complete");
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
private completeCandidateMessage(): string | null {
|
|
608
|
-
const result = this.hostResult;
|
|
609
|
-
if (result?.finalMessage == null || !result.complete) return null;
|
|
610
|
-
return result.finalMessage;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
private async gradeSemantic(): Promise<void> {
|
|
614
|
-
const graded = await gradeSemanticChecks(
|
|
615
|
-
{
|
|
616
|
-
checks: this.context.preparedSemantic,
|
|
617
|
-
declaredChecks: this.options.case.checks,
|
|
618
|
-
host: this.options.semanticHost,
|
|
619
|
-
message: this.completeCandidateMessage(),
|
|
620
|
-
candidateWorkspace: this.fixture.workspace,
|
|
621
|
-
candidateTranscriptRoot: this.candidateTranscriptRoot,
|
|
622
|
-
projectRoot: this.context.projectRoot,
|
|
623
|
-
reservedWorkspace:
|
|
624
|
-
this.context.reservedSemanticWorkspaces[this.trial - 1],
|
|
625
|
-
runDir: this.context.runDir,
|
|
626
|
-
trial: this.trial,
|
|
627
|
-
runtimePolicy: this.options.runtimePolicy,
|
|
628
|
-
signal: this.options.signal,
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
artifactRefs: this.trialArtifactRefs,
|
|
632
|
-
canGrade: () => this.canGrade(),
|
|
633
|
-
failGrading: (message, cancelable) => {
|
|
634
|
-
this.failGrading(message, cancelable);
|
|
635
|
-
},
|
|
636
|
-
retainArtifacts: (artifacts) => this.retainArtifacts(artifacts),
|
|
637
|
-
writeTrialFile: (path, bytes) => this.writeTrialFile(path, bytes),
|
|
638
|
-
},
|
|
639
|
-
);
|
|
640
|
-
this.semanticObservations.push(...graded.observations);
|
|
641
|
-
this.checks.push(...graded.checks);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
private trialObservations(): TrialObservation[] {
|
|
645
|
-
return [
|
|
646
|
-
this.observation,
|
|
647
|
-
...this.shellObservations,
|
|
648
|
-
...this.gitHeadObservations,
|
|
649
|
-
...this.semanticObservations,
|
|
650
|
-
...this.additionalObservations,
|
|
651
|
-
];
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
private extensionEvaluationRequest(): Parameters<
|
|
655
|
-
NonNullable<EvaluationOptions["extension"]>["session"]["evaluate"]
|
|
656
|
-
>[0] {
|
|
657
|
-
return {
|
|
658
|
-
caseId: this.options.case.id,
|
|
659
|
-
execution: { status: this.execution },
|
|
660
|
-
observations: this.trialObservations().map((observation) =>
|
|
661
|
-
observation === this.observation
|
|
662
|
-
? {
|
|
663
|
-
...observation,
|
|
664
|
-
completeness: this.completeness,
|
|
665
|
-
data: {
|
|
666
|
-
...observation.data,
|
|
667
|
-
...extensionFinalMessageData(this.hostResult),
|
|
668
|
-
},
|
|
669
|
-
}
|
|
670
|
-
: observation,
|
|
671
|
-
),
|
|
672
|
-
builtinChecks: this.checks.map((check) => ({
|
|
673
|
-
id: check.id,
|
|
674
|
-
status: check.status,
|
|
675
|
-
...(check.detail ? { detail: check.detail } : {}),
|
|
676
|
-
evidenceRefs: check.evidenceRefs ?? [],
|
|
677
|
-
})),
|
|
678
|
-
artifacts: this.trialArtifactRefs,
|
|
679
|
-
extensionData: this.context.extensionData,
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
private acceptExtensionResult(result: EvaluationResult): void {
|
|
684
|
-
const declared = new Map(
|
|
685
|
-
this.context.extensionDeclarations.map((check) => [
|
|
686
|
-
check.id,
|
|
687
|
-
check.grader,
|
|
688
|
-
]),
|
|
689
|
-
);
|
|
690
|
-
this.checks.push(
|
|
691
|
-
...result.checks.map((check) => extensionCheckResult(check, declared)),
|
|
692
|
-
);
|
|
693
|
-
this.extensionMetrics = result.metrics;
|
|
694
|
-
this.domainOutcomes = result.domainOutcomes ?? [];
|
|
695
|
-
this.taskPolicyRecommendation = result.taskVerdictRecommendation ?? null;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
private async gradeExtension(): Promise<void> {
|
|
699
|
-
const extension = this.options.extension;
|
|
700
|
-
if (!extension || !this.canGrade()) return;
|
|
701
|
-
try {
|
|
702
|
-
this.acceptExtensionResult(
|
|
703
|
-
await extension.session.evaluate(this.extensionEvaluationRequest()),
|
|
704
|
-
);
|
|
705
|
-
} catch (error) {
|
|
706
|
-
this.failGrading(
|
|
707
|
-
error instanceof ExtensionSourceChangedError
|
|
708
|
-
? "extension source inputs changed during evaluation; assessment was refused. Finish changing evaluator inputs, then start a fresh evaluation with inputs held stable."
|
|
709
|
-
: "extension grading did not complete",
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
private assess(): void {
|
|
715
|
-
const declaredOrder = new Map(
|
|
716
|
-
this.options.case.checks.map((check, index) => [check.id, index]),
|
|
717
|
-
);
|
|
718
|
-
this.checks.sort((left, right) =>
|
|
719
|
-
compareDeclaredChecks(left, right, declaredOrder),
|
|
720
|
-
);
|
|
721
|
-
const defaultAssessment = assessTrial({
|
|
722
|
-
execution: this.execution,
|
|
723
|
-
declaredChecks: this.options.case.checks
|
|
724
|
-
.filter((check) => !this.context.replaced.has(check.grader))
|
|
725
|
-
.map((check) => check.id),
|
|
726
|
-
checks: this.checks,
|
|
727
|
-
graderError: this.graderError,
|
|
728
|
-
requiredEvidenceUnavailable: this.options.case.requiredEvidence.some(
|
|
729
|
-
(id) =>
|
|
730
|
-
!this.producedArtifacts.some((item) => item.id === id) &&
|
|
731
|
-
!this.trialObservations().some(
|
|
732
|
-
(item) => item.id === id && item.completeness === "complete",
|
|
733
|
-
),
|
|
734
|
-
),
|
|
735
|
-
});
|
|
736
|
-
this.assessment = applyTaskVerdictPolicy(
|
|
737
|
-
defaultAssessment,
|
|
738
|
-
this.taskPolicyRecommendation,
|
|
739
|
-
Boolean(
|
|
740
|
-
this.options.extension?.session.identity.selectedTaskVerdictPolicy,
|
|
741
|
-
),
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
private async retainAdvisoryResponse(
|
|
745
|
-
host: HostAdapter,
|
|
746
|
-
response: HostResult,
|
|
747
|
-
review: AdvisoryReview,
|
|
748
|
-
): Promise<void> {
|
|
749
|
-
if (
|
|
750
|
-
response.finalMessage !== null &&
|
|
751
|
-
Buffer.byteLength(response.finalMessage, "utf8") <= 64 * 1024
|
|
752
|
-
) {
|
|
753
|
-
const path = join(
|
|
754
|
-
this.context.runDir,
|
|
755
|
-
`trial-${this.trial}-advisory.json`,
|
|
756
|
-
);
|
|
757
|
-
await this.writeTrialFile(path, response.finalMessage);
|
|
758
|
-
review.rawResult = {
|
|
759
|
-
source: host.id,
|
|
760
|
-
path: pathToFileURL(path).href,
|
|
761
|
-
sha256: sha256(response.finalMessage),
|
|
762
|
-
};
|
|
763
|
-
this.trialArtifactRefs.push({
|
|
764
|
-
id: "sevro.advisory.response",
|
|
765
|
-
path: pathToFileURL(path).href,
|
|
766
|
-
sha256: sha256(response.finalMessage),
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
private async cleanupAdvisoryWorkspace(
|
|
772
|
-
workspace: string | null,
|
|
773
|
-
): Promise<void> {
|
|
774
|
-
if (!workspace) return;
|
|
775
|
-
try {
|
|
776
|
-
await rm(workspace, { recursive: true, force: true });
|
|
777
|
-
} catch {
|
|
778
|
-
console.warn(`advisory fixture cleanup failed; retained at ${workspace}`);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
private async executeAdvisoryReview(
|
|
783
|
-
host: HostAdapter,
|
|
784
|
-
review: AdvisoryReview,
|
|
785
|
-
): Promise<void> {
|
|
786
|
-
let workspace: string | null = null;
|
|
787
|
-
try {
|
|
788
|
-
const baseRevision = this.advisoryRevision;
|
|
789
|
-
if (baseRevision === null)
|
|
790
|
-
throw new Error("advisory base revision is unavailable");
|
|
791
|
-
workspace = await buildBlindAdvisoryFixture(this.fixture.workspace, {
|
|
792
|
-
baseRevision,
|
|
793
|
-
excludedPaths: this.options.advisoryExcludedPaths,
|
|
794
|
-
});
|
|
795
|
-
const response = await host.run({
|
|
796
|
-
candidateTranscriptRoot: this.candidateTranscriptRoot,
|
|
797
|
-
prompt: advisoryPrompt(this.fixture.trialPrompt, this.checks),
|
|
798
|
-
runtimePolicy: gradingRuntimePolicy(this.options.runtimePolicy),
|
|
799
|
-
runtimeRole: "advisory",
|
|
800
|
-
workspace,
|
|
801
|
-
condition: "passive",
|
|
802
|
-
signal: this.options.signal,
|
|
803
|
-
});
|
|
804
|
-
review.usage = usage(response);
|
|
805
|
-
const artifacts = scopedHostArtifacts(
|
|
806
|
-
response,
|
|
807
|
-
"sevro.advisory.",
|
|
808
|
-
this.trialArtifactRefs,
|
|
809
|
-
this.options.case.checks,
|
|
810
|
-
"sevro.advisory.response",
|
|
811
|
-
);
|
|
812
|
-
await this.retainArtifacts(artifacts);
|
|
813
|
-
await this.retainAdvisoryResponse(host, response, review);
|
|
814
|
-
review.assessment = completeAdvisoryAssessment(response);
|
|
815
|
-
review.status = "completed";
|
|
816
|
-
} catch (error) {
|
|
817
|
-
if (isTrialPersistenceFailure(error)) throw error;
|
|
818
|
-
review.status = "failed";
|
|
819
|
-
} finally {
|
|
820
|
-
await this.cleanupAdvisoryWorkspace(workspace);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
private async runAdvisory(): Promise<void> {
|
|
825
|
-
const host = this.options.advisoryHost;
|
|
826
|
-
if (!host) return;
|
|
827
|
-
const review = defaultAdvisoryReview(host);
|
|
828
|
-
this.advisoryReview = review;
|
|
829
|
-
if (this.execution !== "completed" || this.options.signal?.aborted) return;
|
|
830
|
-
await this.executeAdvisoryReview(host, review);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
private async retainRawCandidate(): Promise<void> {
|
|
834
|
-
await verifyRetainedArtifacts(this.trialArtifactRefs);
|
|
835
|
-
const message = this.hostResult?.finalMessage;
|
|
836
|
-
if (message == null) {
|
|
837
|
-
this.rawPath = null;
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
this.rawPath = join(this.context.runDir, `trial-${this.trial}-raw.txt`);
|
|
841
|
-
await this.writeTrialFile(this.rawPath, message);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
private trialEvidenceRecord(): RunEvidenceData["trials"][number] {
|
|
845
|
-
return {
|
|
846
|
-
caseId: this.options.case.id,
|
|
847
|
-
trial: this.trial,
|
|
848
|
-
executionMode: this.options.dry ? "dry" : "executed",
|
|
849
|
-
candidateDurationMs: this.candidateDurationMs,
|
|
850
|
-
condition: {
|
|
851
|
-
requested: this.options.condition,
|
|
852
|
-
actual: this.hostResult?.actualCondition ?? "unknown",
|
|
853
|
-
appliedInstrumentation: this.appliedInstrumentation,
|
|
854
|
-
},
|
|
855
|
-
observationCompleteness: this.completeness,
|
|
856
|
-
observations: this.trialObservations(),
|
|
857
|
-
metrics: this.extensionMetrics,
|
|
858
|
-
domainOutcomes: this.domainOutcomes,
|
|
859
|
-
taskVerdictPolicy: selectedTrialTaskPolicy(
|
|
860
|
-
this.options,
|
|
861
|
-
this.taskPolicyRecommendation,
|
|
862
|
-
),
|
|
863
|
-
advisoryReview: this.advisoryReview,
|
|
864
|
-
routes: this.context.routes,
|
|
865
|
-
usage: usage(this.hostResult),
|
|
866
|
-
rawResult: {
|
|
867
|
-
source: this.options.host.id,
|
|
868
|
-
path: this.rawPath ? pathToFileURL(this.rawPath).href : null,
|
|
869
|
-
sha256: this.rawDigest,
|
|
870
|
-
},
|
|
871
|
-
artifactRefs: this.trialArtifactRefs,
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
private async persistTrial(): Promise<boolean> {
|
|
876
|
-
const evidence = this.trialEvidenceRecord();
|
|
877
|
-
const artifactPath = join(this.context.runDir, `trial-${this.trial}.json`);
|
|
878
|
-
const trialSummary: TrialSummary = {
|
|
879
|
-
trial: this.trial,
|
|
880
|
-
...this.assessment,
|
|
881
|
-
checks: this.checks,
|
|
882
|
-
domainOutcomes: this.domainOutcomes,
|
|
883
|
-
artifactPath,
|
|
884
|
-
};
|
|
885
|
-
try {
|
|
886
|
-
await atomicWriteJson(artifactPath, {
|
|
887
|
-
format: "sevro.trial-evidence.v1",
|
|
888
|
-
caseId: this.options.case.id,
|
|
889
|
-
trial: this.trial,
|
|
890
|
-
result: trialSummary,
|
|
891
|
-
evidence,
|
|
892
|
-
});
|
|
893
|
-
} catch {
|
|
894
|
-
throw new Error(
|
|
895
|
-
`trial persistence failed; fixture retained at ${this.fixture.workspace}`,
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
this.persisted = true;
|
|
899
|
-
this.context.trialSummaries.push(trialSummary);
|
|
900
|
-
this.context.trialSummaries.sort((left, right) => left.trial - right.trial);
|
|
901
|
-
this.context.trialEvidence.push(evidence);
|
|
902
|
-
this.context.trialEvidence.sort((left, right) => left.trial - right.trial);
|
|
903
|
-
if (this.diagnostic)
|
|
904
|
-
this.context.diagnostics.push({
|
|
905
|
-
trial: this.trial,
|
|
906
|
-
diagnostic: this.diagnostic,
|
|
907
|
-
});
|
|
908
|
-
checkpointEvaluation(this.context, "active");
|
|
909
|
-
return !(
|
|
910
|
-
(this.execution !== "completed" && this.execution !== "not_run") ||
|
|
911
|
-
this.graderError
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
private async cleanup(): Promise<void> {
|
|
915
|
-
if (this.candidateTranscriptRoot)
|
|
916
|
-
await rm(this.candidateTranscriptRoot, { recursive: true, force: true });
|
|
917
|
-
if (this.persisted) {
|
|
918
|
-
try {
|
|
919
|
-
await clearFixtureContents(this.fixture.workspace);
|
|
920
|
-
this.context.retainedWorkspaces.delete(this.fixture.reservation);
|
|
921
|
-
} catch {
|
|
922
|
-
console.warn(
|
|
923
|
-
`fixture cleanup failed; retained at ${this.fixture.workspace}`,
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
export async function executeEvaluationTrials(
|
|
930
|
-
options: EvaluationOptions,
|
|
931
|
-
context: EvaluationContext,
|
|
932
|
-
): Promise<void> {
|
|
933
|
-
await scheduleTrials(
|
|
934
|
-
{ count: options.trialCount, jobs: context.jobs, signal: options.signal },
|
|
935
|
-
async (trial, stopAdmission) => {
|
|
936
|
-
const fixture = await prepareTrialWorkspace(options, context, trial);
|
|
937
|
-
return new EvaluationTrial(
|
|
938
|
-
options,
|
|
939
|
-
context,
|
|
940
|
-
trial,
|
|
941
|
-
stopAdmission,
|
|
942
|
-
fixture,
|
|
943
|
-
).run();
|
|
944
|
-
},
|
|
945
|
-
);
|
|
946
|
-
}
|