@bjoernrochel/sevro 0.1.0-rc.8 → 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.
Files changed (228) hide show
  1. package/CONTRIBUTING.md +22 -6
  2. package/README.md +5 -1
  3. package/docs/README.md +19 -15
  4. package/docs/architecture.md +138 -17
  5. package/docs/assets/README.md +5 -4
  6. package/docs/builtin-graders-v1.md +9 -2
  7. package/docs/codex-host.md +155 -57
  8. package/docs/contracts.md +2 -1
  9. package/docs/creating-cases.md +9 -2
  10. package/docs/development-cli.md +187 -19
  11. package/docs/documentation-quality.md +12 -2
  12. package/docs/evaluation-workflow.md +110 -0
  13. package/docs/extension-protocol-v1.md +26 -3
  14. package/docs/getting-started.md +23 -2
  15. package/docs/guide-evaluation.md +33 -1
  16. package/docs/identity-v1.md +25 -2
  17. package/docs/installing.md +11 -11
  18. package/docs/native-controls.md +76 -5
  19. package/docs/native-hosts.md +196 -7
  20. package/docs/reading-results.md +102 -0
  21. package/docs/recipes.md +489 -0
  22. package/docs/releases.md +31 -12
  23. package/docs/report-v1.md +29 -1
  24. package/docs/results-v1.md +122 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +255 -22
  27. package/docs/troubleshooting.md +12 -12
  28. package/docs/typescript-quality.md +89 -26
  29. package/examples/recipes/candidate.ts +26 -0
  30. package/examples/recipes/cases/coding-change.json +21 -0
  31. package/examples/recipes/cases/host-tools.json +21 -0
  32. package/examples/recipes/cases/mise.json +21 -0
  33. package/examples/recipes/cases/node.json +21 -0
  34. package/examples/recipes/cases/python-uv.json +21 -0
  35. package/examples/recipes/cases/semantic.json +17 -0
  36. package/examples/recipes/coding-change/check.ts +23 -0
  37. package/examples/recipes/extension-artifacts.ts +65 -0
  38. package/examples/recipes/extension-methods.ts +174 -0
  39. package/examples/recipes/extension.ts +37 -0
  40. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  41. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  42. package/examples/recipes/repository-skill/README.md +3 -0
  43. package/examples/recipes/run-inputs.ts +188 -0
  44. package/examples/recipes/run-options.ts +131 -0
  45. package/examples/recipes/run.ts +63 -0
  46. package/examples/recipes/runtime/empty.json +1 -0
  47. package/examples/recipes/runtime/goals.json +1 -0
  48. package/examples/recipes/runtime/mise.json +6 -0
  49. package/examples/recipes/runtime/path.json +1 -0
  50. package/examples/recipes/runtime/plugins.json +4 -0
  51. package/examples/recipes/runtime/python-uv.json +10 -0
  52. package/examples/recipes/semantic-judge.ts +29 -0
  53. package/examples/templates/coding/adapter.ts +17 -0
  54. package/examples/templates/coding/case.json +15 -0
  55. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  56. package/examples/templates/repository-skill/extension-data.ts +64 -0
  57. package/examples/templates/repository-skill/extension.ts +31 -0
  58. package/examples/templates/response/adapter.ts +12 -0
  59. package/examples/templates/response/case.json +13 -0
  60. package/package.json +11 -5
  61. package/schemas/report-v1.schema.json +9 -0
  62. package/schemas/run-evidence-v1.schema.json +2 -1
  63. package/schemas/runtime-v1.schema.json +43 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +42 -11
  66. package/src/candidate-codex-state.ts +185 -0
  67. package/src/case-validation.ts +73 -50
  68. package/src/cli/cancellation.ts +20 -0
  69. package/src/cli/command.ts +87 -0
  70. package/src/cli/completion.ts +15 -0
  71. package/src/cli/demo.ts +60 -0
  72. package/src/cli/evaluation.ts +55 -0
  73. package/src/cli/extension.ts +79 -0
  74. package/src/cli/help.ts +198 -0
  75. package/src/cli/hosts.ts +55 -0
  76. package/src/cli/init-settings.ts +30 -0
  77. package/src/cli/init.ts +111 -0
  78. package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
  79. package/src/cli/loading.ts +151 -0
  80. package/src/cli/preparation.ts +154 -0
  81. package/src/cli/presentation.ts +93 -0
  82. package/src/cli/progress.ts +6 -0
  83. package/src/cli/run-manifest.ts +128 -0
  84. package/src/cli/run-options.ts +403 -0
  85. package/src/cli/run-overrides.ts +69 -0
  86. package/src/cli/settings.ts +154 -0
  87. package/src/cli/summary.ts +43 -0
  88. package/src/cli.ts +13 -591
  89. package/src/contract-types.ts +94 -0
  90. package/src/engine.ts +9 -5
  91. package/src/evaluation/advisory.ts +150 -0
  92. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  93. package/src/evaluation/collection.ts +88 -0
  94. package/src/evaluation/context.ts +10 -0
  95. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  96. package/src/evaluation/grading.ts +235 -0
  97. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  98. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  99. package/src/evaluation/progress.ts +19 -0
  100. package/src/evaluation/resources.ts +241 -0
  101. package/src/evaluation/run.ts +206 -0
  102. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +77 -87
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/{evaluation-types.ts → evaluation/types.ts} +79 -29
  105. package/src/evidence/host-results.ts +85 -0
  106. package/src/evidence/retained-artifacts.ts +18 -0
  107. package/src/evidence/trial-writer.ts +203 -0
  108. package/src/evidence/usage.ts +17 -0
  109. package/src/extension-client.ts +34 -96
  110. package/src/extension-session.ts +24 -48
  111. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +12 -72
  112. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  113. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  114. package/src/fixtures/create.ts +183 -0
  115. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  116. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  117. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  118. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  119. package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
  120. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  121. package/src/{workspace-state.ts → fixtures/workspace-state.ts} +5 -5
  122. package/src/generated/cli-result.d.cts +2 -1
  123. package/src/generated/extension.d.cts +2 -1
  124. package/src/generated/report.cjs +1 -1
  125. package/src/generated/report.d.cts +2 -1
  126. package/src/generated/run-evidence.cjs +1 -1
  127. package/src/generated/run-evidence.d.cts +2 -1
  128. package/src/generated/runtime.cjs +1 -1
  129. package/src/generated/runtime.d.cts +2 -1
  130. package/src/git.ts +93 -0
  131. package/src/graders/git-head.ts +29 -84
  132. package/src/graders/shell.ts +50 -68
  133. package/src/host-diagnostic-redaction.ts +52 -0
  134. package/src/host-diagnostics.ts +331 -0
  135. package/src/hosts/claude-continuation.ts +57 -40
  136. package/src/hosts/claude-credential.ts +160 -63
  137. package/src/hosts/claude-events.ts +34 -18
  138. package/src/hosts/claude-keychain.ts +78 -21
  139. package/src/hosts/claude-login-recovery.ts +85 -0
  140. package/src/hosts/claude-login-refresh.ts +178 -0
  141. package/src/hosts/claude-login-store.ts +162 -0
  142. package/src/hosts/claude-login.ts +107 -0
  143. package/src/hosts/claude-native-goal.ts +15 -12
  144. package/src/hosts/claude-process.ts +10 -50
  145. package/src/hosts/claude-refresh-lock.ts +81 -0
  146. package/src/hosts/claude-repository-invocation.ts +9 -34
  147. package/src/hosts/claude-settings.ts +46 -9
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +28 -15
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +121 -81
  152. package/src/hosts/codex-app-server.ts +78 -52
  153. package/src/hosts/codex-configuration.ts +5 -10
  154. package/src/hosts/codex-exec-controls.ts +516 -0
  155. package/src/hosts/codex-marketplace.ts +12 -10
  156. package/src/hosts/codex-native-calls.ts +15 -122
  157. package/src/hosts/codex-process.ts +10 -183
  158. package/src/hosts/codex-profile.ts +36 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +134 -18
  161. package/src/hosts/codex.ts +210 -183
  162. package/src/hosts/isolation-common.ts +8 -11
  163. package/src/hosts/isolation-roots.ts +2 -2
  164. package/src/hosts/linux-sandbox.ts +15 -2
  165. package/src/hosts/mac-sandbox.ts +87 -18
  166. package/src/hosts/native-controls.ts +72 -22
  167. package/src/hosts/native-request.ts +55 -0
  168. package/src/hosts/process-lifecycle.ts +216 -48
  169. package/src/hosts/protected-worktrees.ts +1 -1
  170. package/src/hosts/skill-hook-catalog.ts +131 -0
  171. package/src/hosts/skill-hook-handler.ts +374 -0
  172. package/src/hosts/skill-hooks.ts +577 -0
  173. package/src/instrumentation.ts +7 -8
  174. package/src/native-transcript-state.ts +22 -3
  175. package/src/native-transcript-view.ts +52 -13
  176. package/src/native-transcripts.ts +26 -11
  177. package/src/paths.ts +22 -0
  178. package/src/process-lifecycle.ts +150 -0
  179. package/src/provenance.ts +27 -7
  180. package/src/prune.ts +300 -0
  181. package/src/report.ts +63 -36
  182. package/src/resolved-case.ts +1 -1
  183. package/src/results-lock.ts +42 -0
  184. package/src/results.ts +14 -18
  185. package/src/{runtime-config.ts → runtime/config.ts} +160 -46
  186. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  187. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  188. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  189. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  190. package/src/{runtime-hooks.ts → runtime/hooks.ts} +32 -22
  191. package/src/{runtime-paths.ts → runtime/paths.ts} +5 -19
  192. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  193. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  194. package/src/runtime/shared-cache.ts +130 -0
  195. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  196. package/src/schema-types.ts +73 -161
  197. package/src/shell.ts +4 -0
  198. package/src/transcript-retention.ts +146 -0
  199. package/src/value-guards.ts +22 -0
  200. package/docs/assets/sevro-logo.png +0 -0
  201. package/docs/assets/sevro-symbol.svg +0 -1
  202. package/docs/assets/sevro-wordmark.svg +0 -1
  203. package/docs/cache-ownership-validation.md +0 -49
  204. package/docs/claude-continuation-validation.md +0 -47
  205. package/docs/concurrency-validation.md +0 -86
  206. package/docs/configuration-root-validation.md +0 -70
  207. package/docs/engine-slice.md +0 -114
  208. package/docs/evidence/runtime-startup-30/README.md +0 -185
  209. package/docs/evidence/runtime-startup-30/after.json +0 -319
  210. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  211. package/docs/evidence/runtime-startup-30/before.json +0 -403
  212. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  213. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  214. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  216. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  217. package/docs/evidence.md +0 -17
  218. package/docs/fixture-cleanup-validation.md +0 -73
  219. package/docs/guide-verification.json +0 -239
  220. package/src/evaluation-context.ts +0 -259
  221. package/src/evaluation-fixture.ts +0 -302
  222. package/src/evaluation-run.ts +0 -200
  223. package/src/evaluation-trial.ts +0 -1016
  224. package/src/hosts/claude-nested-skills.ts +0 -272
  225. package/src/hosts/codex-command-output.ts +0 -510
  226. package/src/hosts/codex-executor-command.ts +0 -291
  227. package/src/hosts/codex-skill-reads.ts +0 -783
  228. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,302 +0,0 @@
1
- import { isRecord } from "./value-guards";
2
- import { sha256 } from "./identity";
3
- import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
4
- import { allocateWorkspace, removeWorkspace } from "./workspace-state";
5
- import { dirname, join } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { canonicalJson } from "./identity";
8
- import { fixtureBinDirectory, installFixtureBin } from "./fixture-bin";
9
- import { installGitHooks } from "./git-hooks";
10
- import {
11
- materializeGeneratedFixture,
12
- type GeneratedFixture,
13
- } from "./generated-fixture";
14
- import { runFixtureSetup, type FixtureSetup } from "./fixture-setup";
15
- import {
16
- fixtureParts,
17
- safePreparationTarget,
18
- type InlineArtifact,
19
- type PreparationSources,
20
- } from "./preparation";
21
- import {
22
- applyRepositoryOverlay,
23
- cloneRepositorySource,
24
- type RepositoryFixture,
25
- type RepositorySource,
26
- } from "./repository-fixture";
27
- import {
28
- EvaluationConfigurationError,
29
- type HostResult,
30
- type ResolvedCase,
31
- } from "./evaluation-types";
32
-
33
- export { sha256 } from "./identity";
34
-
35
- export async function createFixture(
36
- fixture: ResolvedCase["fixture"],
37
- artifacts: InlineArtifact[],
38
- sources: PreparationSources | undefined,
39
- repository: RepositorySource | null,
40
- repositoryFixture: RepositoryFixture | null,
41
- generated: GeneratedFixture | null,
42
- setup: FixtureSetup | null,
43
- projectRoot: string,
44
- signal?: AbortSignal,
45
- reservedWorkspace?: string,
46
- ): Promise<string> {
47
- const paths = inlineFixturePaths(fixture, generated, repository);
48
- const workspace = reservedWorkspace ?? (await allocateWorkspace());
49
- try {
50
- await initializeFixtureHistory(
51
- fixture,
52
- sources,
53
- repository,
54
- repositoryFixture,
55
- generated,
56
- workspace,
57
- );
58
- await installDeclaredFixtureTools(
59
- generated ?? repositoryFixture,
60
- workspace,
61
- );
62
- await writeInlineFixtureFiles(paths, workspace);
63
- await setupFixture(setup, workspace, projectRoot, signal);
64
- await mountPreparationArtifacts(artifacts, workspace);
65
- await excludePreparationArtifacts(artifacts, workspace);
66
- await fixtureBinDirectory(workspace);
67
- return workspace;
68
- } catch (error) {
69
- if (!reservedWorkspace) await removeWorkspace(workspace);
70
- throw error;
71
- }
72
- }
73
-
74
- function inlineFixturePaths(
75
- fixture: ResolvedCase["fixture"],
76
- generated: GeneratedFixture | null,
77
- repository: RepositorySource | null,
78
- ) {
79
- return Object.entries(
80
- generated || repository ? {} : (fixture.files ?? {}),
81
- ).map(([path, content]) => ({ path, parts: fixtureParts(path), content }));
82
- }
83
-
84
- async function cloneFixtureRepository(
85
- fixture: ResolvedCase["fixture"],
86
- sources: PreparationSources | undefined,
87
- repository: RepositorySource,
88
- workspace: string,
89
- ): Promise<void> {
90
- if (!fixture.sourceRef || !sources)
91
- throw new EvaluationConfigurationError(
92
- "repository source root is required and must be absolute",
93
- );
94
- await cloneRepositorySource(
95
- fixture.sourceRef,
96
- sources,
97
- repository,
98
- workspace,
99
- );
100
- }
101
-
102
- async function initializeFixtureHistory(
103
- fixture: ResolvedCase["fixture"],
104
- sources: PreparationSources | undefined,
105
- repository: RepositorySource | null,
106
- repositoryFixture: RepositoryFixture | null,
107
- generated: GeneratedFixture | null,
108
- workspace: string,
109
- ): Promise<void> {
110
- if (repository)
111
- await cloneFixtureRepository(fixture, sources, repository, workspace);
112
- if (generated) await materializeGeneratedFixture(generated, workspace);
113
- if (repositoryFixture)
114
- await applyRepositoryOverlay(repositoryFixture, workspace);
115
- }
116
-
117
- async function installDeclaredFixtureTools(
118
- fixture: GeneratedFixture | RepositoryFixture | null,
119
- workspace: string,
120
- ): Promise<void> {
121
- await installFixtureBin(fixture?.bin, workspace);
122
- await installGitHooks(fixture?.hooks, workspace);
123
- }
124
-
125
- async function writeInlineFixtureFiles(
126
- files: ReturnType<typeof inlineFixturePaths>,
127
- workspace: string,
128
- ): Promise<void> {
129
- for (const file of files) {
130
- const target = join(workspace, ...file.parts);
131
- await mkdir(dirname(target), { recursive: true, mode: 0o700 });
132
- await writeFile(target, file.content, { flag: "wx", mode: 0o600 });
133
- }
134
- }
135
-
136
- async function setupFixture(
137
- setup: FixtureSetup | null,
138
- workspace: string,
139
- projectRoot: string,
140
- signal?: AbortSignal,
141
- ): Promise<void> {
142
- if (!setup) return;
143
- await runFixtureSetup(setup, {
144
- workspace,
145
- projectRoot,
146
- fixtureBinDir: (await fixtureBinDirectory(workspace)) ?? undefined,
147
- signal,
148
- });
149
- }
150
-
151
- async function mountPreparationArtifacts(
152
- artifacts: InlineArtifact[],
153
- workspace: string,
154
- ): Promise<void> {
155
- for (const artifact of artifacts) {
156
- const target = await safePreparationTarget(
157
- workspace,
158
- artifact.relativePath,
159
- );
160
- await writeFile(target, artifact.bytes, {
161
- flag: "wx",
162
- mode: artifact.executable ? 0o700 : 0o600,
163
- });
164
- }
165
- }
166
-
167
- async function excludePreparationArtifacts(
168
- artifacts: InlineArtifact[],
169
- workspace: string,
170
- ): Promise<void> {
171
- const gitExcluded = artifacts.filter((artifact) => artifact.gitExclude);
172
- if (!gitExcluded.length) return;
173
- const special = new Set(["\\", "*", "?", "[", "]", "#", "!", " "]);
174
- const patterns = gitExcluded.map((artifact) =>
175
- Array.from(artifact.relativePath)
176
- .map((character) =>
177
- special.has(character) ? `\\${character}` : character,
178
- )
179
- .join(""),
180
- );
181
- await appendFile(
182
- join(workspace, ".git", "info", "exclude"),
183
- `\n${patterns.map((path) => `/${path}`).join("\n")}\n`,
184
- );
185
- }
186
-
187
- export function hostObservations(
188
- result: HostResult,
189
- hostId: string,
190
- existingIds: Set<string>,
191
- ) {
192
- const items = result.observations ?? [];
193
- if (items.length > 128) throw new Error("too many host observations");
194
- const ids = new Set([...existingIds, "sevro.observation.final-message"]);
195
- let totalBytes = 0;
196
- return items.map((value: unknown) => {
197
- const item = validatedHostObservation(value, ids);
198
- ids.add(item.id);
199
- const data = canonicalJson(item.data);
200
- totalBytes += Buffer.byteLength(data, "utf8");
201
- if (totalBytes > 8 * 1024 * 1024)
202
- throw new Error("host observations exceed 8 MiB");
203
- return {
204
- id: item.id,
205
- source: hostId,
206
- completeness: item.completeness,
207
- data: JSON.parse(data) as Record<string, unknown>,
208
- };
209
- });
210
- }
211
-
212
- export function hostArtifacts(result: HostResult, existingIds: Set<string>) {
213
- const items = result.artifacts ?? [];
214
- if (!Array.isArray(items) || items.length > 32)
215
- throw new Error("too many host artifacts");
216
- const ids = new Set(existingIds);
217
- let totalBytes = 0;
218
- return items.map((value: unknown) => {
219
- const item = validatedHostArtifact(value, ids);
220
- ids.add(item.id);
221
- totalBytes += item.bytes.byteLength;
222
- if (
223
- item.bytes.byteLength > 8 * 1024 * 1024 ||
224
- totalBytes > 32 * 1024 * 1024
225
- )
226
- throw new Error("host artifacts exceed the size limit");
227
- return { id: item.id, bytes: Buffer.from(item.bytes) };
228
- });
229
- }
230
-
231
- function validEvidenceId(value: unknown): value is string {
232
- return (
233
- typeof value === "string" &&
234
- /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/.test(value)
235
- );
236
- }
237
-
238
- function validObservationId(value: unknown, ids: Set<string>): value is string {
239
- return (
240
- validEvidenceId(value) &&
241
- !value.startsWith("sevro.observation.") &&
242
- !ids.has(value)
243
- );
244
- }
245
-
246
- function validCompleteness(
247
- value: unknown,
248
- ): value is "complete" | "partial" | "unavailable" {
249
- return value === "complete" || value === "partial" || value === "unavailable";
250
- }
251
-
252
- function validatedHostObservation(value: unknown, ids: Set<string>) {
253
- if (!isRecord(value)) throw new Error("invalid host observation");
254
- if (
255
- !validObservationId(value.id, ids) ||
256
- !validCompleteness(value.completeness) ||
257
- !isRecord(value.data)
258
- )
259
- throw new Error("invalid host observation");
260
- return { id: value.id, completeness: value.completeness, data: value.data };
261
- }
262
-
263
- function validArtifactId(value: unknown, ids: Set<string>): value is string {
264
- return validEvidenceId(value) && !ids.has(value);
265
- }
266
-
267
- function validatedHostArtifact(value: unknown, ids: Set<string>) {
268
- if (!isRecord(value)) throw new Error("invalid host artifact");
269
- if (!validArtifactId(value.id, ids) || !(value.bytes instanceof Uint8Array))
270
- throw new Error("invalid host artifact");
271
- return { id: value.id, bytes: value.bytes };
272
- }
273
-
274
- export async function verifyRetainedArtifacts(
275
- artifacts: { path: string; sha256: string }[],
276
- ): Promise<void> {
277
- for (const artifact of artifacts) {
278
- let actual: string;
279
- try {
280
- actual = sha256(await readFile(fileURLToPath(artifact.path)));
281
- } catch {
282
- throw new Error("retained preparation artifact is unreadable");
283
- }
284
- if (actual !== artifact.sha256)
285
- throw new Error("retained preparation artifact changed");
286
- }
287
- }
288
-
289
- export function usage(result: HostResult | null) {
290
- return result === null
291
- ? { inputTokens: null, outputTokens: null, costUsd: null, complete: false }
292
- : availableUsage(result);
293
- }
294
-
295
- function availableUsage(result: HostResult) {
296
- return {
297
- inputTokens: result.inputTokens ?? null,
298
- outputTokens: result.outputTokens ?? null,
299
- costUsd: result.costUsd ?? null,
300
- complete: result.usageComplete ?? false,
301
- };
302
- }
@@ -1,200 +0,0 @@
1
- import { prepareEvaluation } from "./evaluation-plan";
2
- import {
3
- type CaseSummary,
4
- type CliResult,
5
- type EvaluationOptions,
6
- } from "./evaluation-types";
7
- import { canonicalJson, hashJson } from "./identity";
8
- import {
9
- exitCodeFor,
10
- summarizeAssessments,
11
- summarizeCases,
12
- type Assessment,
13
- } from "./results";
14
- import { runtimePolicySnapshot } from "./runtime-config";
15
- import { cleanupExpiredWorkspaces } from "./workspace-state";
16
- import { assertCliResult, assertRunEvidence } from "./schema";
17
- import { atomicWriteJson } from "./storage";
18
- import {
19
- allocateEvaluationRun,
20
- checkpointEvaluation,
21
- cleanupEvaluationWorkspaces,
22
- diagnoseEvaluationFailure,
23
- reserveEvaluationWorkspaces,
24
- retainPreparationArtifacts,
25
- type EvaluationContext,
26
- } from "./evaluation-context";
27
- import { executeEvaluationTrials } from "./evaluation-trial";
28
-
29
- function evaluationCaseSummary(
30
- options: EvaluationOptions,
31
- context: EvaluationContext,
32
- ): CaseSummary {
33
- const assessment: Assessment =
34
- !context.trialSummaries.length && options.signal?.aborted
35
- ? {
36
- execution: { status: "cancelled" },
37
- grading: { status: "not_requested" },
38
- task: { verdict: "not_assessed" },
39
- }
40
- : summarizeAssessments(context.trialSummaries, options.passThreshold);
41
- return {
42
- caseId: options.case.id,
43
- ...assessment,
44
- trials: context.trialSummaries,
45
- };
46
- }
47
-
48
- function evaluationSignal(
49
- options: EvaluationOptions,
50
- ): "SIGINT" | "SIGTERM" | undefined {
51
- const reason: unknown = options.signal?.aborted
52
- ? options.signal.reason
53
- : null;
54
- return reason === "SIGINT" || reason === "SIGTERM" ? reason : undefined;
55
- }
56
-
57
- function finalCliResult(
58
- options: EvaluationOptions,
59
- context: EvaluationContext,
60
- ): CliResult {
61
- const caseResult = evaluationCaseSummary(options, context);
62
- const assessment = summarizeCases([caseResult]);
63
- const diagnostic = context.diagnostics.sort(
64
- (left, right) => left.trial - right.trial,
65
- )[0]?.diagnostic;
66
- return {
67
- format: "sevro.cli-result.v1",
68
- runId: context.runId,
69
- ...assessment,
70
- exitCode: exitCodeFor(assessment, {
71
- dryRun: options.dry,
72
- signal: evaluationSignal(options),
73
- }),
74
- evidencePath: context.evidencePath,
75
- cases: [caseResult],
76
- ...(diagnostic ? { diagnostic } : {}),
77
- };
78
- }
79
-
80
- function finalConditionEvidence(
81
- options: EvaluationOptions,
82
- context: EvaluationContext,
83
- ) {
84
- const actualConditions = [
85
- ...new Set(context.trialEvidence.map((entry) => entry.condition.actual)),
86
- ];
87
- const commonAppliedInstrumentation =
88
- context.trialEvidence.length === options.trialCount &&
89
- context.trialEvidence.every(
90
- (entry) =>
91
- canonicalJson(entry.condition.appliedInstrumentation) ===
92
- canonicalJson(context.requestedInstrumentation),
93
- )
94
- ? context.requestedInstrumentation
95
- : [];
96
- return {
97
- requested: options.condition,
98
- actual: actualConditions.length === 1 ? actualConditions[0] : "unknown",
99
- requestedInstrumentation: context.requestedInstrumentation,
100
- appliedInstrumentation: commonAppliedInstrumentation,
101
- };
102
- }
103
-
104
- function finalExtensionEvidence(
105
- options: EvaluationOptions,
106
- replacedBuiltinGraders: string[],
107
- ) {
108
- const extension = options.extension;
109
- if (!extension) return null;
110
- const identity = extension.session.identity;
111
- return {
112
- id: identity.id,
113
- version: identity.version,
114
- sourceDigest: identity.sourceDigest,
115
- configurationDigest: identity.configurationDigest,
116
- protocol: identity.protocol,
117
- capabilities: identity.capabilities,
118
- replacements: {
119
- graders: replacedBuiltinGraders,
120
- taskVerdictPolicy: identity.selectedTaskVerdictPolicy,
121
- },
122
- };
123
- }
124
-
125
- function finalRunEvidence(
126
- options: EvaluationOptions,
127
- context: EvaluationContext,
128
- result: CliResult,
129
- diagnostic: { code: string; message: string } | undefined,
130
- ) {
131
- const condition = finalConditionEvidence(options, context);
132
- return {
133
- format: "sevro.run-evidence.v1",
134
- runId: context.runId,
135
- evaluationIdentity: context.evaluationIdentity,
136
- configuration: {
137
- digest: hashJson(context.redactedConfig),
138
- redacted: context.redactedConfig,
139
- },
140
- runner: context.runner,
141
- project: context.project,
142
- extension: finalExtensionEvidence(options, context.replacedBuiltinGraders),
143
- condition,
144
- graders: {
145
- active: context.activeGraders,
146
- replacedDefaults: context.replacedBuiltinGraders,
147
- },
148
- routes: context.routes,
149
- result,
150
- trials: context.trialEvidence,
151
- ...(diagnostic ? { diagnostic } : {}),
152
- };
153
- }
154
-
155
- async function finishEvaluationRun(
156
- options: EvaluationOptions,
157
- context: EvaluationContext,
158
- ) {
159
- const diagnostic = context.diagnostics.sort(
160
- (left, right) => left.trial - right.trial,
161
- )[0]?.diagnostic;
162
- const result = finalCliResult(options, context);
163
- const evidence = finalRunEvidence(options, context, result, diagnostic);
164
- assertCliResult(result);
165
- assertRunEvidence(evidence);
166
- await atomicWriteJson(context.evidencePath, evidence);
167
- checkpointEvaluation(
168
- context,
169
- options.signal?.aborted ? "interrupted" : "complete",
170
- );
171
- return { result };
172
- }
173
-
174
- /** Execute resolved evaluator data through an injected host and retain every trial. */
175
- export async function runEvaluation(
176
- options: EvaluationOptions,
177
- ): Promise<{ result: CliResult }> {
178
- options = {
179
- ...options,
180
- runtimePolicy: runtimePolicySnapshot(options.runtimePolicy),
181
- };
182
- await cleanupExpiredWorkspaces();
183
- const plan = await prepareEvaluation(options);
184
- const runtime = await allocateEvaluationRun(options, plan);
185
- try {
186
- const artifacts = await retainPreparationArtifacts({ ...plan, ...runtime });
187
- const records = await reserveEvaluationWorkspaces(options, {
188
- ...plan,
189
- ...runtime,
190
- });
191
- const context = { ...plan, ...runtime, ...artifacts, ...records };
192
- await executeEvaluationTrials(options, context);
193
- return await finishEvaluationRun(options, context);
194
- } catch (error) {
195
- diagnoseEvaluationFailure(runtime);
196
- throw error;
197
- } finally {
198
- await cleanupEvaluationWorkspaces(runtime);
199
- }
200
- }