loadout-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/MASTER_PLAN.md +1711 -0
- package/README.md +453 -0
- package/SECURITY.md +18 -0
- package/SIMPLE_PLAN.md +44 -0
- package/catalog/discovered.json +10469 -0
- package/catalog/packages.json +1150 -0
- package/dashboard/app.js +607 -0
- package/dashboard/index.html +249 -0
- package/dashboard/styles.css +384 -0
- package/dist/src/cli.js +2735 -0
- package/dist/src/core/active-policy.js +233 -0
- package/dist/src/core/active-set.js +382 -0
- package/dist/src/core/adapters.js +254 -0
- package/dist/src/core/adopt.js +92 -0
- package/dist/src/core/agent-health-score.js +475 -0
- package/dist/src/core/agent-inspection.js +152 -0
- package/dist/src/core/agent-versions.js +131 -0
- package/dist/src/core/api.js +90 -0
- package/dist/src/core/atomic-file.js +22 -0
- package/dist/src/core/audit.js +174 -0
- package/dist/src/core/benchmark-campaign.js +496 -0
- package/dist/src/core/benchmark-evidence.js +458 -0
- package/dist/src/core/benchmark-fixtures.js +1157 -0
- package/dist/src/core/benchmark-runner.js +301 -0
- package/dist/src/core/benchmark-trust.js +795 -0
- package/dist/src/core/canary.js +77 -0
- package/dist/src/core/candidate-intelligence.js +661 -0
- package/dist/src/core/catalog-coverage.js +85 -0
- package/dist/src/core/catalog-install.js +186 -0
- package/dist/src/core/catalog-release.js +286 -0
- package/dist/src/core/catalog.js +327 -0
- package/dist/src/core/codex-mcp.js +122 -0
- package/dist/src/core/community.js +86 -0
- package/dist/src/core/compatibility-intelligence.js +465 -0
- package/dist/src/core/completion.js +160 -0
- package/dist/src/core/components.js +138 -0
- package/dist/src/core/conversion.js +56 -0
- package/dist/src/core/credentials.js +377 -0
- package/dist/src/core/demo.js +136 -0
- package/dist/src/core/diff.js +83 -0
- package/dist/src/core/discovery-connector.js +165 -0
- package/dist/src/core/doctor.js +95 -0
- package/dist/src/core/ecosystem-import.js +1072 -0
- package/dist/src/core/evaluate.js +72 -0
- package/dist/src/core/file-lock.js +140 -0
- package/dist/src/core/freshness-alerts.js +230 -0
- package/dist/src/core/github-discovery.js +97 -0
- package/dist/src/core/github.js +148 -0
- package/dist/src/core/head-to-head.js +437 -0
- package/dist/src/core/health-score-evidence.js +169 -0
- package/dist/src/core/health.js +129 -0
- package/dist/src/core/improve.js +223 -0
- package/dist/src/core/install.js +233 -0
- package/dist/src/core/intelligence-feed-build.js +59 -0
- package/dist/src/core/intelligence-feed.js +353 -0
- package/dist/src/core/loadout-badge.js +55 -0
- package/dist/src/core/loadout-card.js +87 -0
- package/dist/src/core/manifest.js +307 -0
- package/dist/src/core/mcp-recipes.js +419 -0
- package/dist/src/core/mcp-registry-discovery.js +455 -0
- package/dist/src/core/mcp.js +287 -0
- package/dist/src/core/model-config.js +102 -0
- package/dist/src/core/observations.js +87 -0
- package/dist/src/core/outcomes.js +164 -0
- package/dist/src/core/package.js +225 -0
- package/dist/src/core/paths.js +214 -0
- package/dist/src/core/portable.js +130 -0
- package/dist/src/core/private-discovery.js +45 -0
- package/dist/src/core/profiles.js +255 -0
- package/dist/src/core/provenance.js +278 -0
- package/dist/src/core/ranking.js +104 -0
- package/dist/src/core/recommend.js +181 -0
- package/dist/src/core/registry-api.js +97 -0
- package/dist/src/core/registry.js +316 -0
- package/dist/src/core/release-claims.js +175 -0
- package/dist/src/core/remove.js +122 -0
- package/dist/src/core/review-queue.js +200 -0
- package/dist/src/core/runtime-tool-recipe.js +315 -0
- package/dist/src/core/runtime-tools.js +453 -0
- package/dist/src/core/safety.js +234 -0
- package/dist/src/core/sandbox.js +78 -0
- package/dist/src/core/scheduler.js +264 -0
- package/dist/src/core/share-report.js +85 -0
- package/dist/src/core/signing.js +115 -0
- package/dist/src/core/skill-compare.js +239 -0
- package/dist/src/core/skill-inventory.js +193 -0
- package/dist/src/core/skill-security.js +340 -0
- package/dist/src/core/skills-sh-discovery.js +336 -0
- package/dist/src/core/skills.js +208 -0
- package/dist/src/core/snapshot.js +210 -0
- package/dist/src/core/source.js +305 -0
- package/dist/src/core/state.js +300 -0
- package/dist/src/core/sync.js +348 -0
- package/dist/src/core/terminal.js +53 -0
- package/dist/src/core/transaction.js +211 -0
- package/dist/src/core/update-watch.js +39 -0
- package/dist/src/core/update.js +216 -0
- package/dist/src/core/upgrade.js +207 -0
- package/dist/src/dashboard.js +418 -0
- package/dist/src/shared/schemas.js +336 -0
- package/dist/src/shared/types.js +1 -0
- package/docs/ACTIVE_SET.md +53 -0
- package/docs/ACTIVE_SET_POLICY.md +45 -0
- package/docs/CANDIDATE_INTELLIGENCE.md +139 -0
- package/docs/CATALOG.md +78 -0
- package/docs/CATALOG_POLICY.md +41 -0
- package/docs/COMMUNITY_DISCOVERY.md +14 -0
- package/docs/COMPATIBILITY_POLICY.md +22 -0
- package/docs/CONVERSION_AND_SANDBOX.md +27 -0
- package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +31 -0
- package/docs/DISCOVERED.md +280 -0
- package/docs/EVALUATION_PROTOCOL.md +40 -0
- package/docs/EVALUATION_PROTOCOL_V1.md +300 -0
- package/docs/FEATURE_TEST_MATRIX.md +802 -0
- package/docs/GITHUB_AUTHORIZATION.md +38 -0
- package/docs/HEAD_TO_HEAD_EVALUATION.md +79 -0
- package/docs/PROVENANCE_AND_COMPARISON.md +72 -0
- package/docs/PROVIDER_CONFIGURATION.md +45 -0
- package/docs/RELEASE_REVIEW.md +117 -0
- package/docs/SAFE_UPDATE_DEMO.md +25 -0
- package/docs/SCHEMA_DECISIONS.md +25 -0
- package/docs/TEAM_POLICY.md +18 -0
- package/docs/TESTING.md +190 -0
- package/package.json +82 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, open, readFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { benchmarkCampaignSha256, benchmarkScheduleSha256, buildBenchmarkSchedule, parseBenchmarkCampaign, } from "./benchmark-campaign.js";
|
|
5
|
+
import { withFileLock } from "./file-lock.js";
|
|
6
|
+
export const BENCHMARK_EVIDENCE_VERSION = "loadout-benchmark-evidence-v1";
|
|
7
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
|
|
8
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
9
|
+
const PAUSE_REASONS = new Set([
|
|
10
|
+
"caller-aborted",
|
|
11
|
+
"overall-time-ceiling",
|
|
12
|
+
"teardown-failed",
|
|
13
|
+
"interrupted-attempt",
|
|
14
|
+
"budget-ceiling",
|
|
15
|
+
]);
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function exactKeys(value, expected, context) {
|
|
20
|
+
const expectedSet = new Set(expected);
|
|
21
|
+
const unknown = Object.keys(value).filter((key) => !expectedSet.has(key));
|
|
22
|
+
if (unknown.length)
|
|
23
|
+
throw new Error(`${context} has unknown field(s): ${unknown.join(", ")}`);
|
|
24
|
+
const missing = expected.filter((key) => !(key in value));
|
|
25
|
+
if (missing.length)
|
|
26
|
+
throw new Error(`${context} is missing field(s): ${missing.join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
function canonical(value) {
|
|
29
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
30
|
+
return JSON.stringify(value);
|
|
31
|
+
if (typeof value === "number") {
|
|
32
|
+
if (!Number.isFinite(value))
|
|
33
|
+
throw new Error("Cannot hash a non-finite value");
|
|
34
|
+
return JSON.stringify(value);
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
38
|
+
if (isRecord(value))
|
|
39
|
+
return `{${Object.entries(value)
|
|
40
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
41
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${canonical(entry)}`)
|
|
42
|
+
.join(",")}}`;
|
|
43
|
+
throw new Error(`Cannot hash value of type ${typeof value}`);
|
|
44
|
+
}
|
|
45
|
+
function sha256(value) {
|
|
46
|
+
return createHash("sha256").update(canonical(value)).digest("hex");
|
|
47
|
+
}
|
|
48
|
+
function assertIdentifier(value, context) {
|
|
49
|
+
if (typeof value !== "string" ||
|
|
50
|
+
!ID.test(value) ||
|
|
51
|
+
value.includes("..") ||
|
|
52
|
+
value.includes("//") ||
|
|
53
|
+
/(?:sk-|bearer\s|api[_-]?key|token=|password=)/i.test(value))
|
|
54
|
+
throw new Error(`${context} is invalid or contains sensitive material`);
|
|
55
|
+
}
|
|
56
|
+
function assertTimestamp(value, context) {
|
|
57
|
+
if (typeof value !== "string" || new Date(value).toISOString() !== value)
|
|
58
|
+
throw new Error(`${context} must be an ISO-8601 UTC timestamp`);
|
|
59
|
+
}
|
|
60
|
+
function assertInteger(value, context, minimum, maximum) {
|
|
61
|
+
if (typeof value !== "number" ||
|
|
62
|
+
!Number.isInteger(value) ||
|
|
63
|
+
value < minimum ||
|
|
64
|
+
value > maximum)
|
|
65
|
+
throw new Error(`${context} must be an integer from ${minimum} to ${maximum}`);
|
|
66
|
+
}
|
|
67
|
+
function assertFinite(value, context, maximum) {
|
|
68
|
+
if (typeof value !== "number" ||
|
|
69
|
+
!Number.isFinite(value) ||
|
|
70
|
+
value < 0 ||
|
|
71
|
+
value > maximum)
|
|
72
|
+
throw new Error(`${context} must be a finite number from 0 to ${maximum}`);
|
|
73
|
+
}
|
|
74
|
+
function parseCompletion(value, campaign, scheduleIds) {
|
|
75
|
+
if (!isRecord(value))
|
|
76
|
+
throw new Error("Benchmark completion must be an object");
|
|
77
|
+
const common = [
|
|
78
|
+
"requestId",
|
|
79
|
+
"outcome",
|
|
80
|
+
"attempts",
|
|
81
|
+
"inputTokens",
|
|
82
|
+
"outputTokens",
|
|
83
|
+
"durationMs",
|
|
84
|
+
"reportedCostUsd",
|
|
85
|
+
];
|
|
86
|
+
exactKeys(value, value.outcome === "succeeded"
|
|
87
|
+
? [...common, "outputSha256"]
|
|
88
|
+
: value.outcome === "exhausted"
|
|
89
|
+
? [...common, "failureCode"]
|
|
90
|
+
: common, "Benchmark completion");
|
|
91
|
+
if (typeof value.requestId !== "string" || !scheduleIds.has(value.requestId))
|
|
92
|
+
throw new Error("Benchmark completion request is outside the schedule");
|
|
93
|
+
if (value.outcome !== "succeeded" && value.outcome !== "exhausted")
|
|
94
|
+
throw new Error("Benchmark completion outcome is invalid");
|
|
95
|
+
assertInteger(value.attempts, "Benchmark completion attempts", 1, campaign.trials.maxRetriesPerRequest + 1);
|
|
96
|
+
assertInteger(value.inputTokens, "Benchmark completion input tokens", 0, value.attempts * campaign.sampling.maxInputTokensPerRequest);
|
|
97
|
+
assertInteger(value.outputTokens, "Benchmark completion output tokens", 0, value.attempts * campaign.sampling.maxOutputTokensPerRequest);
|
|
98
|
+
assertFinite(value.durationMs, "Benchmark completion duration", value.attempts * campaign.trials.timeoutMsPerRequest);
|
|
99
|
+
assertFinite(value.reportedCostUsd, "Benchmark completion cost", campaign.budget.maxCostUsd);
|
|
100
|
+
if (value.outcome === "succeeded") {
|
|
101
|
+
if (typeof value.outputSha256 !== "string" ||
|
|
102
|
+
!SHA256.test(value.outputSha256))
|
|
103
|
+
throw new Error("Benchmark completion output hash is invalid");
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
assertIdentifier(value.failureCode, "Benchmark completion failure code");
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
function parsePayload(value, campaign, schedule) {
|
|
111
|
+
if (!isRecord(value) || typeof value.type !== "string")
|
|
112
|
+
throw new Error("Benchmark evidence payload is invalid");
|
|
113
|
+
const scheduleIds = new Set(schedule.map((request) => request.requestId));
|
|
114
|
+
switch (value.type) {
|
|
115
|
+
case "run-started":
|
|
116
|
+
exactKeys(value, ["type", "providerId", "sandboxBackend", "spendApproved"], "Run-started payload");
|
|
117
|
+
assertIdentifier(value.providerId, "Benchmark provider id");
|
|
118
|
+
if (!new Set(["docker", "podman", "injected"]).has(String(value.sandboxBackend)))
|
|
119
|
+
throw new Error("Benchmark sandbox backend is invalid");
|
|
120
|
+
if (value.spendApproved !== true)
|
|
121
|
+
throw new Error("Benchmark evidence requires explicit spend approval");
|
|
122
|
+
break;
|
|
123
|
+
case "request-started": {
|
|
124
|
+
exactKeys(value, ["type", "requestId", "pairIndex", "position", "attempt"], "Request-started payload");
|
|
125
|
+
const request = schedule.find((item) => item.requestId === value.requestId);
|
|
126
|
+
if (!request)
|
|
127
|
+
throw new Error("Started request is outside the schedule");
|
|
128
|
+
if (value.pairIndex !== request.pairIndex ||
|
|
129
|
+
value.position !== request.position)
|
|
130
|
+
throw new Error("Started request metadata does not match the schedule");
|
|
131
|
+
assertInteger(value.attempt, "Benchmark request attempt", 1, campaign.trials.maxRetriesPerRequest + 1);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "request-attempt-failed":
|
|
135
|
+
exactKeys(value, [
|
|
136
|
+
"type",
|
|
137
|
+
"requestId",
|
|
138
|
+
"attempt",
|
|
139
|
+
"inputTokens",
|
|
140
|
+
"outputTokens",
|
|
141
|
+
"durationMs",
|
|
142
|
+
"reportedCostUsd",
|
|
143
|
+
"failureCode",
|
|
144
|
+
], "Request-attempt-failed payload");
|
|
145
|
+
if (typeof value.requestId !== "string" ||
|
|
146
|
+
!scheduleIds.has(value.requestId))
|
|
147
|
+
throw new Error("Failed benchmark attempt is outside the schedule");
|
|
148
|
+
assertInteger(value.attempt, "Failed benchmark attempt number", 1, campaign.trials.maxRetriesPerRequest + 1);
|
|
149
|
+
assertInteger(value.inputTokens, "Failed benchmark attempt input tokens", 0, campaign.sampling.maxInputTokensPerRequest);
|
|
150
|
+
assertInteger(value.outputTokens, "Failed benchmark attempt output tokens", 0, campaign.sampling.maxOutputTokensPerRequest);
|
|
151
|
+
assertFinite(value.durationMs, "Failed benchmark attempt duration", campaign.trials.timeoutMsPerRequest);
|
|
152
|
+
assertFinite(value.reportedCostUsd, "Failed benchmark attempt cost", campaign.budget.maxCostUsd);
|
|
153
|
+
assertIdentifier(value.failureCode, "Failed benchmark attempt code");
|
|
154
|
+
break;
|
|
155
|
+
case "request-completed":
|
|
156
|
+
exactKeys(value, ["type", "completion"], "Request-completed payload");
|
|
157
|
+
return {
|
|
158
|
+
type: "request-completed",
|
|
159
|
+
completion: parseCompletion(value.completion, campaign, scheduleIds),
|
|
160
|
+
};
|
|
161
|
+
case "request-recovered":
|
|
162
|
+
exactKeys(value, ["type", "completion", "resolution"], "Request-recovered payload");
|
|
163
|
+
if (value.resolution !== "abandoned-unknown-provider-state")
|
|
164
|
+
throw new Error("Benchmark recovery resolution is invalid");
|
|
165
|
+
return {
|
|
166
|
+
type: "request-recovered",
|
|
167
|
+
completion: parseCompletion(value.completion, campaign, scheduleIds),
|
|
168
|
+
resolution: value.resolution,
|
|
169
|
+
};
|
|
170
|
+
case "run-paused":
|
|
171
|
+
exactKeys(value, ["type", "reasonCode"], "Run-paused payload");
|
|
172
|
+
if (!PAUSE_REASONS.has(value.reasonCode))
|
|
173
|
+
throw new Error("Benchmark pause reason is invalid");
|
|
174
|
+
break;
|
|
175
|
+
case "run-completed":
|
|
176
|
+
exactKeys(value, ["type"], "Run-completed payload");
|
|
177
|
+
break;
|
|
178
|
+
case "run-cancelled":
|
|
179
|
+
exactKeys(value, ["type", "reasonCode"], "Run-cancelled payload");
|
|
180
|
+
assertIdentifier(value.reasonCode, "Benchmark cancellation reason");
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
throw new Error(`Unsupported benchmark evidence event: ${value.type}`);
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
function unsignedEvent(event) {
|
|
188
|
+
const unsigned = { ...event };
|
|
189
|
+
delete unsigned.eventSha256;
|
|
190
|
+
return unsigned;
|
|
191
|
+
}
|
|
192
|
+
export function benchmarkEvidenceEventSha256(event) {
|
|
193
|
+
return sha256(event);
|
|
194
|
+
}
|
|
195
|
+
export function parseBenchmarkEvidenceEvent(value, campaignValue) {
|
|
196
|
+
const campaign = parseBenchmarkCampaign(campaignValue);
|
|
197
|
+
if (!isRecord(value))
|
|
198
|
+
throw new Error("Benchmark evidence event must be an object");
|
|
199
|
+
exactKeys(value, [
|
|
200
|
+
"schemaVersion",
|
|
201
|
+
"evidenceVersion",
|
|
202
|
+
"runId",
|
|
203
|
+
"campaignId",
|
|
204
|
+
"campaignSha256",
|
|
205
|
+
"scheduleSha256",
|
|
206
|
+
"sequence",
|
|
207
|
+
"recordedAt",
|
|
208
|
+
"previousEventSha256",
|
|
209
|
+
"payload",
|
|
210
|
+
"eventSha256",
|
|
211
|
+
], "Benchmark evidence event");
|
|
212
|
+
if (value.schemaVersion !== 1 ||
|
|
213
|
+
value.evidenceVersion !== BENCHMARK_EVIDENCE_VERSION)
|
|
214
|
+
throw new Error("Unsupported benchmark evidence version");
|
|
215
|
+
assertIdentifier(value.runId, "Benchmark run id");
|
|
216
|
+
if (value.campaignId !== campaign.campaignId)
|
|
217
|
+
throw new Error("Benchmark evidence names a different campaign");
|
|
218
|
+
if (value.campaignSha256 !== benchmarkCampaignSha256(campaign))
|
|
219
|
+
throw new Error("Benchmark evidence campaign hash is invalid");
|
|
220
|
+
if (value.scheduleSha256 !== benchmarkScheduleSha256(campaign))
|
|
221
|
+
throw new Error("Benchmark evidence schedule hash is invalid");
|
|
222
|
+
assertInteger(value.sequence, "Benchmark evidence sequence", 0, 100_000);
|
|
223
|
+
assertTimestamp(value.recordedAt, "Benchmark evidence timestamp");
|
|
224
|
+
if (value.previousEventSha256 !== null &&
|
|
225
|
+
!SHA256.test(String(value.previousEventSha256)))
|
|
226
|
+
throw new Error("Benchmark previous-event hash is invalid");
|
|
227
|
+
const payload = parsePayload(value.payload, campaign, buildBenchmarkSchedule(campaign));
|
|
228
|
+
if (typeof value.eventSha256 !== "string" || !SHA256.test(value.eventSha256))
|
|
229
|
+
throw new Error("Benchmark event hash is invalid");
|
|
230
|
+
const event = { ...value, payload };
|
|
231
|
+
if (benchmarkEvidenceEventSha256(unsignedEvent(event)) !== event.eventSha256)
|
|
232
|
+
throw new Error("Benchmark event hash is invalid");
|
|
233
|
+
return event;
|
|
234
|
+
}
|
|
235
|
+
export function reduceBenchmarkEvidence(values, campaignValue) {
|
|
236
|
+
const campaign = parseBenchmarkCampaign(campaignValue);
|
|
237
|
+
const schedule = buildBenchmarkSchedule(campaign);
|
|
238
|
+
const state = {
|
|
239
|
+
status: "empty",
|
|
240
|
+
events: [],
|
|
241
|
+
completions: [],
|
|
242
|
+
pending: [...schedule],
|
|
243
|
+
currentRequestUsage: {
|
|
244
|
+
inputTokens: 0,
|
|
245
|
+
outputTokens: 0,
|
|
246
|
+
durationMs: 0,
|
|
247
|
+
reportedCostUsd: 0,
|
|
248
|
+
},
|
|
249
|
+
attempts: 0,
|
|
250
|
+
inputTokens: 0,
|
|
251
|
+
outputTokens: 0,
|
|
252
|
+
reportedCostUsd: 0,
|
|
253
|
+
durationMs: 0,
|
|
254
|
+
};
|
|
255
|
+
for (const [index, value] of values.entries()) {
|
|
256
|
+
const event = parseBenchmarkEvidenceEvent(value, campaign);
|
|
257
|
+
if (event.sequence !== index)
|
|
258
|
+
throw new Error(`Benchmark evidence sequence is non-contiguous at ${index}`);
|
|
259
|
+
if (index === 0) {
|
|
260
|
+
if (event.previousEventSha256 !== null)
|
|
261
|
+
throw new Error("First benchmark event must not name a predecessor");
|
|
262
|
+
if (event.payload.type !== "run-started")
|
|
263
|
+
throw new Error("First benchmark event must start the run");
|
|
264
|
+
state.runId = event.runId;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
if (event.runId !== state.runId)
|
|
268
|
+
throw new Error("Benchmark evidence mixes run ids");
|
|
269
|
+
if (event.previousEventSha256 !== state.lastEventSha256)
|
|
270
|
+
throw new Error("Benchmark evidence hash chain is invalid");
|
|
271
|
+
const previous = state.events.at(-1);
|
|
272
|
+
if (Date.parse(event.recordedAt) < Date.parse(previous.recordedAt))
|
|
273
|
+
throw new Error("Benchmark evidence timestamps are non-monotonic");
|
|
274
|
+
}
|
|
275
|
+
if (state.status === "completed" || state.status === "cancelled")
|
|
276
|
+
throw new Error("Benchmark evidence continues after a terminal event");
|
|
277
|
+
if (state.status === "empty")
|
|
278
|
+
state.status = "running";
|
|
279
|
+
const payload = event.payload;
|
|
280
|
+
if (payload.type === "run-started" && index !== 0)
|
|
281
|
+
throw new Error("Benchmark run can only be started once");
|
|
282
|
+
if (payload.type === "request-started") {
|
|
283
|
+
const expected = state.pending[0];
|
|
284
|
+
if (!expected || expected.requestId !== payload.requestId)
|
|
285
|
+
throw new Error("Benchmark request order differs from deterministic schedule");
|
|
286
|
+
const priorAttempts = state.events.filter((item) => item.payload.type === "request-started" &&
|
|
287
|
+
item.payload.requestId === payload.requestId).length;
|
|
288
|
+
if (payload.attempt !== priorAttempts + 1)
|
|
289
|
+
throw new Error("Benchmark attempt number is non-contiguous");
|
|
290
|
+
if (state.inFlight)
|
|
291
|
+
throw new Error("Benchmark evidence starts a request while one is in flight");
|
|
292
|
+
state.inFlight = { request: expected, attempt: payload.attempt };
|
|
293
|
+
state.attempts += 1;
|
|
294
|
+
}
|
|
295
|
+
if (payload.type === "request-attempt-failed") {
|
|
296
|
+
if (!state.inFlight ||
|
|
297
|
+
state.inFlight.request.requestId !== payload.requestId ||
|
|
298
|
+
state.inFlight.attempt !== payload.attempt)
|
|
299
|
+
throw new Error("Failed benchmark attempt has no matching in-flight request");
|
|
300
|
+
state.inFlight = undefined;
|
|
301
|
+
state.currentRequestUsage.inputTokens += payload.inputTokens;
|
|
302
|
+
state.currentRequestUsage.outputTokens += payload.outputTokens;
|
|
303
|
+
state.currentRequestUsage.durationMs += payload.durationMs;
|
|
304
|
+
state.currentRequestUsage.reportedCostUsd += payload.reportedCostUsd;
|
|
305
|
+
state.inputTokens += payload.inputTokens;
|
|
306
|
+
state.outputTokens += payload.outputTokens;
|
|
307
|
+
state.durationMs += payload.durationMs;
|
|
308
|
+
state.reportedCostUsd += payload.reportedCostUsd;
|
|
309
|
+
}
|
|
310
|
+
if (payload.type === "request-completed" ||
|
|
311
|
+
payload.type === "request-recovered") {
|
|
312
|
+
if (!state.inFlight ||
|
|
313
|
+
state.inFlight.request.requestId !== payload.completion.requestId)
|
|
314
|
+
throw new Error("Benchmark completion has no matching in-flight request");
|
|
315
|
+
if (payload.completion.attempts !== state.inFlight.attempt)
|
|
316
|
+
throw new Error("Benchmark completion attempt count does not match evidence");
|
|
317
|
+
const used = state.currentRequestUsage;
|
|
318
|
+
if (payload.completion.inputTokens < used.inputTokens ||
|
|
319
|
+
payload.completion.outputTokens < used.outputTokens ||
|
|
320
|
+
payload.completion.durationMs < used.durationMs ||
|
|
321
|
+
payload.completion.reportedCostUsd + Number.EPSILON <
|
|
322
|
+
used.reportedCostUsd)
|
|
323
|
+
throw new Error("Benchmark completion omits prior failed-attempt usage");
|
|
324
|
+
state.completions.push(payload.completion);
|
|
325
|
+
state.pending.shift();
|
|
326
|
+
state.inFlight = undefined;
|
|
327
|
+
state.inputTokens += payload.completion.inputTokens - used.inputTokens;
|
|
328
|
+
state.outputTokens += payload.completion.outputTokens - used.outputTokens;
|
|
329
|
+
state.reportedCostUsd +=
|
|
330
|
+
payload.completion.reportedCostUsd - used.reportedCostUsd;
|
|
331
|
+
state.durationMs += payload.completion.durationMs - used.durationMs;
|
|
332
|
+
state.currentRequestUsage = {
|
|
333
|
+
inputTokens: 0,
|
|
334
|
+
outputTokens: 0,
|
|
335
|
+
durationMs: 0,
|
|
336
|
+
reportedCostUsd: 0,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (payload.type === "run-paused")
|
|
340
|
+
state.status = "paused";
|
|
341
|
+
if (payload.type === "run-cancelled")
|
|
342
|
+
state.status = "cancelled";
|
|
343
|
+
if (payload.type === "run-completed") {
|
|
344
|
+
if (state.pending.length || state.inFlight)
|
|
345
|
+
throw new Error("Benchmark run completed before its schedule was exhausted");
|
|
346
|
+
state.status = "completed";
|
|
347
|
+
}
|
|
348
|
+
else if (state.status === "paused" &&
|
|
349
|
+
(payload.type === "request-started" ||
|
|
350
|
+
payload.type === "request-attempt-failed" ||
|
|
351
|
+
payload.type === "request-completed" ||
|
|
352
|
+
payload.type === "request-recovered")) {
|
|
353
|
+
state.status = "running";
|
|
354
|
+
}
|
|
355
|
+
if (state.attempts > campaign.budget.maxRequests)
|
|
356
|
+
throw new Error("Benchmark evidence exceeds its request ceiling");
|
|
357
|
+
if (state.inputTokens > campaign.budget.maxInputTokens)
|
|
358
|
+
throw new Error("Benchmark evidence exceeds its input-token ceiling");
|
|
359
|
+
if (state.outputTokens > campaign.budget.maxOutputTokens)
|
|
360
|
+
throw new Error("Benchmark evidence exceeds its output-token ceiling");
|
|
361
|
+
if (state.reportedCostUsd > campaign.budget.maxCostUsd + Number.EPSILON)
|
|
362
|
+
throw new Error("Benchmark evidence exceeds its cost ceiling");
|
|
363
|
+
if (state.durationMs >
|
|
364
|
+
schedule.length *
|
|
365
|
+
campaign.trials.timeoutMsPerRequest *
|
|
366
|
+
(campaign.trials.maxRetriesPerRequest + 1))
|
|
367
|
+
throw new Error("Benchmark evidence exceeds its time ceiling");
|
|
368
|
+
state.events.push(event);
|
|
369
|
+
state.lastEventSha256 = event.eventSha256;
|
|
370
|
+
}
|
|
371
|
+
return state;
|
|
372
|
+
}
|
|
373
|
+
export function createBenchmarkEvidenceEvent(campaignValue, runId, existingEvents, payload, recordedAt = new Date().toISOString()) {
|
|
374
|
+
const campaign = parseBenchmarkCampaign(campaignValue);
|
|
375
|
+
assertIdentifier(runId, "Benchmark run id");
|
|
376
|
+
assertTimestamp(recordedAt, "Benchmark evidence timestamp");
|
|
377
|
+
const state = reduceBenchmarkEvidence(existingEvents, campaign);
|
|
378
|
+
if (state.runId && state.runId !== runId)
|
|
379
|
+
throw new Error("Benchmark evidence mixes run ids");
|
|
380
|
+
const unsigned = {
|
|
381
|
+
schemaVersion: 1,
|
|
382
|
+
evidenceVersion: BENCHMARK_EVIDENCE_VERSION,
|
|
383
|
+
runId,
|
|
384
|
+
campaignId: campaign.campaignId,
|
|
385
|
+
campaignSha256: benchmarkCampaignSha256(campaign),
|
|
386
|
+
scheduleSha256: benchmarkScheduleSha256(campaign),
|
|
387
|
+
sequence: existingEvents.length,
|
|
388
|
+
recordedAt,
|
|
389
|
+
previousEventSha256: state.lastEventSha256 ?? null,
|
|
390
|
+
payload,
|
|
391
|
+
};
|
|
392
|
+
const event = {
|
|
393
|
+
...unsigned,
|
|
394
|
+
eventSha256: benchmarkEvidenceEventSha256(unsigned),
|
|
395
|
+
};
|
|
396
|
+
reduceBenchmarkEvidence([...existingEvents, event], campaign);
|
|
397
|
+
return event;
|
|
398
|
+
}
|
|
399
|
+
export async function readBenchmarkEvidenceLog(path, campaignValue) {
|
|
400
|
+
let content;
|
|
401
|
+
try {
|
|
402
|
+
content = await readFile(path, "utf8");
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
if (error.code === "ENOENT")
|
|
406
|
+
return reduceBenchmarkEvidence([], campaignValue);
|
|
407
|
+
throw error;
|
|
408
|
+
}
|
|
409
|
+
const lines = content.split("\n");
|
|
410
|
+
if (lines.at(-1) !== "")
|
|
411
|
+
throw new Error("Benchmark evidence log ends with an incomplete record");
|
|
412
|
+
const values = lines
|
|
413
|
+
.slice(0, -1)
|
|
414
|
+
.filter((line) => line.length)
|
|
415
|
+
.map((line, index) => {
|
|
416
|
+
try {
|
|
417
|
+
return JSON.parse(line);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
throw new Error(`Benchmark evidence record ${index} is invalid JSON`);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
return reduceBenchmarkEvidence(values, campaignValue);
|
|
424
|
+
}
|
|
425
|
+
export async function appendBenchmarkEvidenceEvent(path, campaignValue, runId, payload, recordedAt = new Date().toISOString()) {
|
|
426
|
+
return withFileLock(`${path}.lock`, async () => {
|
|
427
|
+
const current = await readBenchmarkEvidenceLog(path, campaignValue);
|
|
428
|
+
const event = createBenchmarkEvidenceEvent(campaignValue, runId, current.events, payload, recordedAt);
|
|
429
|
+
await mkdir(dirname(path), { recursive: true });
|
|
430
|
+
const handle = await open(path, "a", 0o600);
|
|
431
|
+
try {
|
|
432
|
+
await handle.writeFile(`${JSON.stringify(event)}\n`);
|
|
433
|
+
await handle.sync();
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
await handle.close();
|
|
437
|
+
}
|
|
438
|
+
return reduceBenchmarkEvidence([...current.events, event], campaignValue);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
export function buildInterruptedRecovery(state, usage) {
|
|
442
|
+
if (!state.inFlight)
|
|
443
|
+
throw new Error("Benchmark recovery requires an interrupted in-flight request");
|
|
444
|
+
return {
|
|
445
|
+
type: "request-recovered",
|
|
446
|
+
resolution: "abandoned-unknown-provider-state",
|
|
447
|
+
completion: {
|
|
448
|
+
requestId: state.inFlight.request.requestId,
|
|
449
|
+
outcome: "exhausted",
|
|
450
|
+
attempts: state.inFlight.attempt,
|
|
451
|
+
inputTokens: state.currentRequestUsage.inputTokens + usage.inputTokens,
|
|
452
|
+
outputTokens: state.currentRequestUsage.outputTokens + usage.outputTokens,
|
|
453
|
+
durationMs: state.currentRequestUsage.durationMs + usage.durationMs,
|
|
454
|
+
reportedCostUsd: state.currentRequestUsage.reportedCostUsd + usage.reportedCostUsd,
|
|
455
|
+
failureCode: "interrupted-provider-state-unknown",
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
}
|