@tangle-network/agent-interface 0.27.2 → 0.29.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/dist/agent-candidate-artifact-schema.d.ts +0 -3
- package/dist/agent-candidate-artifact-schema.js +0 -2
- package/dist/agent-candidate-code-schema.d.ts +0 -10
- package/dist/agent-candidate-code-schema.js +0 -1
- package/dist/agent-candidate-execution-plan-schema.d.ts +173 -382
- package/dist/agent-candidate-execution-plan-schema.js +100 -138
- package/dist/agent-candidate-lineage-schema.d.ts +1 -31
- package/dist/agent-candidate-lineage-schema.js +3 -34
- package/dist/agent-candidate-outcome-schema.d.ts +48 -36
- package/dist/agent-candidate-outcome-schema.js +23 -32
- package/dist/agent-candidate-promotion-schema.d.ts +11021 -2411
- package/dist/agent-candidate-promotion-schema.js +495 -109
- package/dist/agent-candidate-receipt-schema.d.ts +260 -344
- package/dist/agent-candidate-receipt-schema.js +82 -11
- package/dist/agent-candidate-schema-common.d.ts +8 -0
- package/dist/agent-candidate-schema-common.js +35 -1
- package/dist/agent-candidate-schema.d.ts +2 -44
- package/dist/agent-candidate-schema.js +3 -29
- package/dist/agent-candidate-task-schema.d.ts +643 -0
- package/dist/agent-candidate-task-schema.js +191 -0
- package/dist/agent-candidate.d.ts +173 -94
- package/dist/agent-candidate.test-fixture.d.ts +0 -32
- package/dist/agent-candidate.test-fixture.js +0 -32
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/profile-diff.d.ts +0 -1
- package/dist/profile-schema.d.ts +9 -0
- package/dist/profile-schema.js +14 -6
- package/package.json +2 -2
- package/dist/agent-candidate-compat.d.ts +0 -1918
- package/dist/agent-candidate-compat.js +0 -340
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { agentCandidateBundleSchema } from "./agent-candidate-schema.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { agentCandidateLineageSchema } from "./agent-candidate-lineage-schema.js";
|
|
4
|
+
import { agentCandidateBenchmarkSuiteInputsSchema } from "./agent-candidate-task-schema.js";
|
|
5
|
+
import { canonicalCandidateDigest, isCanonicalJsonValue, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
|
|
5
6
|
import { agentCandidateMaterializationReceiptSchema, agentCandidateRunReceiptSchema, } from "./agent-candidate-receipt-schema.js";
|
|
6
|
-
import { agentProfileSchema } from "./profile-schema.js";
|
|
7
7
|
const canonicalJsonSchema = z.custom(isCanonicalJsonValue, "value must be finite, acyclic RFC 8785 JSON");
|
|
8
8
|
const canonicalJsonObjectSchema = z
|
|
9
9
|
.record(z.string(), canonicalJsonSchema)
|
|
@@ -74,9 +74,7 @@ const measuredObjectiveSchema = z.union([
|
|
|
74
74
|
measuredObjectiveVariant(qualityDimensionFields),
|
|
75
75
|
unavailableObjectiveVariant(qualityDimensionFields),
|
|
76
76
|
measuredObjectiveVariant(costObjectiveFields),
|
|
77
|
-
unavailableObjectiveVariant(costObjectiveFields),
|
|
78
77
|
measuredObjectiveVariant(latencyObjectiveFields),
|
|
79
|
-
unavailableObjectiveVariant(latencyObjectiveFields),
|
|
80
78
|
]);
|
|
81
79
|
const improvementSurfaceSchema = z.enum([
|
|
82
80
|
"prompt",
|
|
@@ -90,19 +88,139 @@ const improvementSurfaceSchema = z.enum([
|
|
|
90
88
|
"code",
|
|
91
89
|
"knowledge",
|
|
92
90
|
]);
|
|
91
|
+
export const agentCandidateEvaluationPolicySchema = z
|
|
92
|
+
.object({
|
|
93
|
+
confidenceLevel: z.number().finite().gt(0).lt(1),
|
|
94
|
+
resamples: z.number().int().min(100),
|
|
95
|
+
bootstrapSeed: z.number().int().safe(),
|
|
96
|
+
deltaThreshold: z.number().finite().nonnegative(),
|
|
97
|
+
minProductiveRuns: z.number().int().min(3),
|
|
98
|
+
budgetUsd: z.number().finite().nonnegative().optional(),
|
|
99
|
+
criticalDimensions: z.array(z.string().min(1)),
|
|
100
|
+
regressionTolerance: z.number().finite().nonnegative(),
|
|
101
|
+
})
|
|
102
|
+
.strict()
|
|
103
|
+
.superRefine((policy, ctx) => {
|
|
104
|
+
if (new Set(policy.criticalDimensions).size !== policy.criticalDimensions.length ||
|
|
105
|
+
policy.criticalDimensions.some((name, index) => index > 0 && policy.criticalDimensions[index - 1] >= name)) {
|
|
106
|
+
ctx.addIssue({
|
|
107
|
+
code: "custom",
|
|
108
|
+
path: ["criticalDimensions"],
|
|
109
|
+
message: "critical dimensions must be sorted and unique",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
export const agentCandidateExperimentSchema = z
|
|
114
|
+
.object({
|
|
115
|
+
kind: z.literal("agent-candidate-experiment"),
|
|
116
|
+
digestAlgorithm: z.literal("rfc8785-sha256"),
|
|
117
|
+
baseline: agentCandidateBundleSchema,
|
|
118
|
+
candidate: agentCandidateBundleSchema,
|
|
119
|
+
candidateLineage: agentCandidateLineageSchema,
|
|
120
|
+
benchmark: agentCandidateBenchmarkSuiteInputsSchema,
|
|
121
|
+
policy: agentCandidateEvaluationPolicySchema,
|
|
122
|
+
digest: sha256DigestSchema,
|
|
123
|
+
})
|
|
124
|
+
.strict()
|
|
125
|
+
.superRefine((experiment, ctx) => {
|
|
126
|
+
const source = experiment.candidateLineage.source;
|
|
127
|
+
if ((source === "optimizer" || source === "compound") &&
|
|
128
|
+
!experiment.candidateLineage.parentDigests?.includes(experiment.baseline.digest)) {
|
|
129
|
+
ctx.addIssue({
|
|
130
|
+
code: "custom",
|
|
131
|
+
path: ["candidateLineage", "parentDigests"],
|
|
132
|
+
message: "generated candidate lineage must include the experiment baseline",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (experiment.candidateLineage.parentDigests?.includes(experiment.candidate.digest)) {
|
|
136
|
+
ctx.addIssue({
|
|
137
|
+
code: "custom",
|
|
138
|
+
path: ["candidateLineage", "parentDigests"],
|
|
139
|
+
message: "candidate lineage cannot name the candidate itself as a parent",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (experiment.candidateLineage.developmentSplitDigest !== undefined &&
|
|
143
|
+
experiment.benchmark.tasks.some((task) => task.benchmark.splitDigest ===
|
|
144
|
+
experiment.candidateLineage.developmentSplitDigest)) {
|
|
145
|
+
ctx.addIssue({
|
|
146
|
+
code: "custom",
|
|
147
|
+
path: ["candidateLineage", "developmentSplitDigest"],
|
|
148
|
+
message: "candidate development and held-out splits must be disjoint",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (!isCanonicalJsonValue(experiment)) {
|
|
152
|
+
ctx.addIssue({
|
|
153
|
+
code: "custom",
|
|
154
|
+
message: "candidate experiment must contain only RFC 8785 JSON values",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
export const candidateExecutionEvidenceSchema = z
|
|
159
|
+
.object({
|
|
160
|
+
kind: z.literal("agent-candidate-execution-evidence"),
|
|
161
|
+
materializationReceipt: agentCandidateMaterializationReceiptSchema,
|
|
162
|
+
receipt: agentCandidateRunReceiptSchema,
|
|
163
|
+
digest: sha256DigestSchema,
|
|
164
|
+
})
|
|
165
|
+
.strict()
|
|
166
|
+
.superRefine((evidence, ctx) => {
|
|
167
|
+
const materialization = evidence.materializationReceipt;
|
|
168
|
+
const plan = materialization.executionPlan;
|
|
169
|
+
const checks = [
|
|
170
|
+
[
|
|
171
|
+
evidence.receipt.materializationReceiptDigest === materialization.digest,
|
|
172
|
+
["receipt", "materializationReceiptDigest"],
|
|
173
|
+
"run receipt must bind the included materialization receipt",
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
evidence.receipt.executionPlanDigest === plan.digest,
|
|
177
|
+
["receipt", "executionPlanDigest"],
|
|
178
|
+
"run receipt must bind the included execution plan",
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
evidence.receipt.bundleDigest === materialization.bundleDigest,
|
|
182
|
+
["receipt", "bundleDigest"],
|
|
183
|
+
"run receipt and materialization must bind one bundle",
|
|
184
|
+
],
|
|
185
|
+
[
|
|
186
|
+
evidence.receipt.runCellDigest === plan.material.runCell.digest,
|
|
187
|
+
["receipt", "runCellDigest"],
|
|
188
|
+
"run receipt must bind the materialized run cell",
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
materialization.profileActivation.profilePlan.digest ===
|
|
192
|
+
plan.material.profile.planDigest,
|
|
193
|
+
["materializationReceipt", "profileActivation", "profilePlan", "digest"],
|
|
194
|
+
"profile activation must bind the materialized execution plan",
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
evidence.receipt.modelSettlement.material.grantDigest ===
|
|
198
|
+
plan.material.model.access.grantDigest,
|
|
199
|
+
["receipt", "modelSettlement", "material", "grantDigest"],
|
|
200
|
+
"model settlement must bind the execution plan grant",
|
|
201
|
+
],
|
|
202
|
+
];
|
|
203
|
+
for (const [valid, path, message] of checks) {
|
|
204
|
+
if (!valid)
|
|
205
|
+
ctx.addIssue({ code: "custom", path, message });
|
|
206
|
+
}
|
|
207
|
+
if (!isCanonicalJsonValue(evidence)) {
|
|
208
|
+
ctx.addIssue({
|
|
209
|
+
code: "custom",
|
|
210
|
+
message: "candidate execution evidence must contain only RFC 8785 JSON values",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
});
|
|
93
214
|
export const agentImprovementMeasuredComparisonSchema = z
|
|
94
215
|
.object({
|
|
95
|
-
schemaVersion: z.literal(1),
|
|
96
216
|
kind: z.literal("agent-improvement-measured-comparison"),
|
|
97
|
-
|
|
217
|
+
experiment: agentCandidateExperimentSchema,
|
|
218
|
+
measurements: z.array(z
|
|
98
219
|
.object({
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
splitDigest: sha256DigestSchema,
|
|
220
|
+
baseline: candidateExecutionEvidenceSchema,
|
|
221
|
+
candidate: candidateExecutionEvidenceSchema,
|
|
102
222
|
})
|
|
103
|
-
.strict(),
|
|
104
|
-
baselineProfileDigest: sha256DigestSchema,
|
|
105
|
-
candidateBundleDigest: sha256DigestSchema,
|
|
223
|
+
.strict()),
|
|
106
224
|
overall: z
|
|
107
225
|
.object({
|
|
108
226
|
name: z.literal("composite"),
|
|
@@ -158,7 +276,11 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
158
276
|
evaluation: z
|
|
159
277
|
.object({
|
|
160
278
|
generationsExplored: z.number().int().nonnegative(),
|
|
279
|
+
searchDurationMs: z.number().finite().nonnegative(),
|
|
280
|
+
executionDurationMs: z.number().finite().nonnegative(),
|
|
161
281
|
durationMs: z.number().finite().nonnegative(),
|
|
282
|
+
searchCostUsd: z.number().finite().nonnegative(),
|
|
283
|
+
executionCostUsd: z.number().finite().nonnegative(),
|
|
162
284
|
totalCostUsd: z.number().finite().nonnegative(),
|
|
163
285
|
})
|
|
164
286
|
.strict(),
|
|
@@ -167,6 +289,257 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
167
289
|
.strict()
|
|
168
290
|
.superRefine((comparison, ctx) => {
|
|
169
291
|
refineEstimate(comparison.overall, ["overall"], ctx);
|
|
292
|
+
if (!approximatelyEqual(comparison.evaluation.durationMs, comparison.evaluation.searchDurationMs + comparison.evaluation.executionDurationMs) ||
|
|
293
|
+
!approximatelyEqual(comparison.evaluation.totalCostUsd, comparison.evaluation.searchCostUsd + comparison.evaluation.executionCostUsd)) {
|
|
294
|
+
ctx.addIssue({
|
|
295
|
+
code: "custom",
|
|
296
|
+
path: ["evaluation"],
|
|
297
|
+
message: "evaluation totals must equal their search and execution components",
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
const { suite, tasks } = comparison.experiment.benchmark;
|
|
301
|
+
const expectedN = suite.taskDigests.length * suite.reps;
|
|
302
|
+
if (comparison.measurements.length !== expectedN) {
|
|
303
|
+
ctx.addIssue({
|
|
304
|
+
code: "custom",
|
|
305
|
+
path: ["measurements"],
|
|
306
|
+
message: "measured comparison must contain every signed benchmark cell",
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
const executionIdentities = {
|
|
310
|
+
execution: new Set(),
|
|
311
|
+
runCell: new Set(),
|
|
312
|
+
materialization: new Set(),
|
|
313
|
+
receipt: new Set(),
|
|
314
|
+
evidence: new Set(),
|
|
315
|
+
};
|
|
316
|
+
for (let taskIndex = 0; taskIndex < suite.taskDigests.length; taskIndex += 1) {
|
|
317
|
+
const task = tasks[taskIndex];
|
|
318
|
+
if (!task)
|
|
319
|
+
continue;
|
|
320
|
+
for (let repetition = 0; repetition < suite.reps; repetition += 1) {
|
|
321
|
+
const index = taskIndex * suite.reps + repetition;
|
|
322
|
+
const measurement = comparison.measurements[index];
|
|
323
|
+
if (!measurement)
|
|
324
|
+
continue;
|
|
325
|
+
const seed = suite.seeds[index];
|
|
326
|
+
for (const arm of ["baseline", "candidate"]) {
|
|
327
|
+
const evidence = measurement[arm];
|
|
328
|
+
const bundle = comparison.experiment[arm];
|
|
329
|
+
const materialization = evidence.materializationReceipt;
|
|
330
|
+
const plan = materialization.executionPlan;
|
|
331
|
+
const runCell = plan.material.runCell;
|
|
332
|
+
const result = evidence.receipt.benchmarkResult.material;
|
|
333
|
+
const outcome = evidence.receipt.taskOutcome.material.outcome;
|
|
334
|
+
const armPath = ["measurements", index, arm];
|
|
335
|
+
const expectedTree = bundle.code.kind === "disabled"
|
|
336
|
+
? undefined
|
|
337
|
+
: bundle.code.kind === "no-op"
|
|
338
|
+
? bundle.code.baseTree
|
|
339
|
+
: bundle.code.candidateTree;
|
|
340
|
+
const containerMatches = bundle.execution.environment.kind === "evaluator-task-container"
|
|
341
|
+
? task.evaluatorTaskContainer !== undefined &&
|
|
342
|
+
plan.material.container.source === "evaluator-task-container" &&
|
|
343
|
+
JSON.stringify(plan.material.container) ===
|
|
344
|
+
JSON.stringify(task.evaluatorTaskContainer)
|
|
345
|
+
: plan.material.container.source === "pinned-container" &&
|
|
346
|
+
plan.material.container.image ===
|
|
347
|
+
bundle.execution.environment.container.image &&
|
|
348
|
+
plan.material.container.indexDigest ===
|
|
349
|
+
bundle.execution.environment.container.indexDigest;
|
|
350
|
+
const checks = [
|
|
351
|
+
[
|
|
352
|
+
runCell.experimentDigest === comparison.experiment.digest,
|
|
353
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "runCell", "experimentDigest"],
|
|
354
|
+
"execution evidence must bind the measured experiment",
|
|
355
|
+
],
|
|
356
|
+
[
|
|
357
|
+
runCell.arm === arm,
|
|
358
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "runCell", "arm"],
|
|
359
|
+
"execution evidence must bind its measured arm",
|
|
360
|
+
],
|
|
361
|
+
[
|
|
362
|
+
runCell.bundleDigest === bundle.digest && materialization.bundleDigest === bundle.digest,
|
|
363
|
+
[...armPath, "materializationReceipt", "bundleDigest"],
|
|
364
|
+
"execution evidence must bind the experiment arm bundle",
|
|
365
|
+
],
|
|
366
|
+
[
|
|
367
|
+
runCell.suiteDigest === suite.digest &&
|
|
368
|
+
runCell.taskDigest === task.digest &&
|
|
369
|
+
runCell.taskIndex === taskIndex &&
|
|
370
|
+
runCell.repetition === repetition &&
|
|
371
|
+
runCell.seed === seed &&
|
|
372
|
+
runCell.attempt === 1,
|
|
373
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "runCell"],
|
|
374
|
+
"publishable execution evidence must use the first signed task attempt",
|
|
375
|
+
],
|
|
376
|
+
[
|
|
377
|
+
materialization.codeKind === bundle.code.kind,
|
|
378
|
+
[...armPath, "materializationReceipt", "codeKind"],
|
|
379
|
+
"materialized code must match the experiment arm bundle",
|
|
380
|
+
],
|
|
381
|
+
[
|
|
382
|
+
materialization.benchmark.suite.digest === suite.digest &&
|
|
383
|
+
materialization.benchmark.task.digest === task.digest,
|
|
384
|
+
[...armPath, "materializationReceipt", "benchmark"],
|
|
385
|
+
"execution evidence must capture the signed suite and selected task",
|
|
386
|
+
],
|
|
387
|
+
[
|
|
388
|
+
materialization.harness === bundle.execution.harness &&
|
|
389
|
+
materialization.harnessVersion === bundle.execution.harnessVersion &&
|
|
390
|
+
plan.material.harness === bundle.execution.harness &&
|
|
391
|
+
plan.material.harnessVersion === bundle.execution.harnessVersion,
|
|
392
|
+
[...armPath, "materializationReceipt", "harness"],
|
|
393
|
+
"execution evidence must bind the candidate harness and version",
|
|
394
|
+
],
|
|
395
|
+
[
|
|
396
|
+
JSON.stringify(plan.material.instructionDelivery) ===
|
|
397
|
+
JSON.stringify(bundle.execution.instructionDelivery),
|
|
398
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "instructionDelivery"],
|
|
399
|
+
"execution plan must bind the candidate instruction delivery",
|
|
400
|
+
],
|
|
401
|
+
[
|
|
402
|
+
JSON.stringify(plan.material.limits) === JSON.stringify(task.limits),
|
|
403
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "limits"],
|
|
404
|
+
"execution plan must bind every signed task limit",
|
|
405
|
+
],
|
|
406
|
+
[
|
|
407
|
+
containerMatches,
|
|
408
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "container"],
|
|
409
|
+
"execution plan must bind the candidate or evaluator task container",
|
|
410
|
+
],
|
|
411
|
+
[
|
|
412
|
+
JSON.stringify(plan.material.candidateWorkspace) ===
|
|
413
|
+
JSON.stringify(bundle.execution.workspace) &&
|
|
414
|
+
JSON.stringify(materialization.candidateWorkspace) ===
|
|
415
|
+
JSON.stringify(bundle.execution.workspace),
|
|
416
|
+
[...armPath, "materializationReceipt", "candidateWorkspace"],
|
|
417
|
+
"execution evidence must bind the candidate workspace",
|
|
418
|
+
],
|
|
419
|
+
[
|
|
420
|
+
materialization.materializedTree === expectedTree,
|
|
421
|
+
[...armPath, "materializationReceipt", "materializedTree"],
|
|
422
|
+
"materialized tree must match the candidate code",
|
|
423
|
+
],
|
|
424
|
+
[
|
|
425
|
+
plan.material.launch.cwd.workspace === bundle.execution.cwd.workspace &&
|
|
426
|
+
plan.material.launch.cwd.path === bundle.execution.cwd.path,
|
|
427
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "launch", "cwd"],
|
|
428
|
+
"execution plan must bind the candidate working directory",
|
|
429
|
+
],
|
|
430
|
+
[
|
|
431
|
+
plan.material.knowledgeManifestDigest === bundle.knowledge?.snapshot.digest &&
|
|
432
|
+
materialization.knowledgeManifestDigest === bundle.knowledge?.snapshot.digest,
|
|
433
|
+
[...armPath, "materializationReceipt", "knowledgeManifestDigest"],
|
|
434
|
+
"execution evidence must bind the candidate knowledge snapshot",
|
|
435
|
+
],
|
|
436
|
+
[
|
|
437
|
+
(bundle.memory.mode === "disabled" && plan.material.memory.mode === "disabled") ||
|
|
438
|
+
(bundle.memory.mode === "isolated" &&
|
|
439
|
+
plan.material.memory.mode === "isolated" &&
|
|
440
|
+
plan.material.memory.seedDigest === bundle.memory.seed?.sha256),
|
|
441
|
+
[...armPath, "materializationReceipt", "executionPlan", "material", "memory"],
|
|
442
|
+
"execution plan must bind the candidate memory policy",
|
|
443
|
+
],
|
|
444
|
+
[
|
|
445
|
+
result.evidence.sha256 !== task.grader.artifact.sha256,
|
|
446
|
+
[...armPath, "receipt", "benchmarkResult", "material", "evidence", "sha256"],
|
|
447
|
+
"grading evidence must be distinct from the signed grader implementation",
|
|
448
|
+
],
|
|
449
|
+
[
|
|
450
|
+
JSON.stringify(result.grader) === JSON.stringify(task.grader),
|
|
451
|
+
[...armPath, "receipt", "benchmarkResult", "material", "grader"],
|
|
452
|
+
"benchmark result must bind the signed grader",
|
|
453
|
+
],
|
|
454
|
+
[
|
|
455
|
+
materialization.profileActivation.profilePlan.material.sourceProfileDigest ===
|
|
456
|
+
canonicalCandidateDigest(bundle.profile),
|
|
457
|
+
[...armPath, "materializationReceipt", "profileActivation", "profilePlan", "material", "sourceProfileDigest"],
|
|
458
|
+
"materialized profile files must bind the experiment arm profile",
|
|
459
|
+
],
|
|
460
|
+
[
|
|
461
|
+
JSON.stringify(materialization.resolvedModel) === JSON.stringify(task.model),
|
|
462
|
+
[...armPath, "materializationReceipt", "resolvedModel"],
|
|
463
|
+
"execution must use the selected task model",
|
|
464
|
+
],
|
|
465
|
+
[
|
|
466
|
+
(task.limits.maxModelCalls === 0 &&
|
|
467
|
+
materialization.executionPlan.material.model.access.network.mode ===
|
|
468
|
+
"disabled") ||
|
|
469
|
+
(task.limits.maxModelCalls > 0 &&
|
|
470
|
+
materialization.executionPlan.material.model.access.network.mode ===
|
|
471
|
+
"gateway-only"),
|
|
472
|
+
[
|
|
473
|
+
...armPath,
|
|
474
|
+
"materializationReceipt",
|
|
475
|
+
"executionPlan",
|
|
476
|
+
"material",
|
|
477
|
+
"model",
|
|
478
|
+
"access",
|
|
479
|
+
"network",
|
|
480
|
+
],
|
|
481
|
+
"model gateway access must match the signed model-call limit",
|
|
482
|
+
],
|
|
483
|
+
[
|
|
484
|
+
outcome.kind === task.outcome.kind,
|
|
485
|
+
[...armPath, "receipt", "taskOutcome", "material", "outcome", "kind"],
|
|
486
|
+
"captured outcome must match the selected task contract",
|
|
487
|
+
],
|
|
488
|
+
[
|
|
489
|
+
task.outcome.kind !== "output" ||
|
|
490
|
+
(outcome.kind === "output" &&
|
|
491
|
+
outcome.spec.mediaType === task.outcome.mediaType &&
|
|
492
|
+
outcome.spec.maxBytes === task.outcome.maxBytes),
|
|
493
|
+
[...armPath, "receipt", "taskOutcome", "material", "outcome", "spec"],
|
|
494
|
+
"captured output must match the selected task specification",
|
|
495
|
+
],
|
|
496
|
+
[
|
|
497
|
+
task.outcome.kind !== "workspace" ||
|
|
498
|
+
(outcome.kind === "workspace" &&
|
|
499
|
+
task.repository !== undefined &&
|
|
500
|
+
outcome.baseRepository.identity === task.repository.identity &&
|
|
501
|
+
outcome.baseRepository.rootIdentity === task.repository.rootIdentity &&
|
|
502
|
+
outcome.baseRepository.commit === task.repository.baseCommit &&
|
|
503
|
+
outcome.baseRepository.tree === task.repository.baseTree),
|
|
504
|
+
[...armPath, "receipt", "taskOutcome", "material", "outcome", "baseRepository"],
|
|
505
|
+
"captured workspace must start from the selected task repository",
|
|
506
|
+
],
|
|
507
|
+
];
|
|
508
|
+
for (const [valid, path, message] of checks) {
|
|
509
|
+
if (!valid)
|
|
510
|
+
ctx.addIssue({ code: "custom", path, message });
|
|
511
|
+
}
|
|
512
|
+
const identitiesForRun = {
|
|
513
|
+
execution: plan.material.executionId,
|
|
514
|
+
runCell: runCell.digest,
|
|
515
|
+
materialization: materialization.digest,
|
|
516
|
+
receipt: evidence.receipt.digest,
|
|
517
|
+
evidence: evidence.digest,
|
|
518
|
+
};
|
|
519
|
+
for (const [kind, identity] of Object.entries(identitiesForRun)) {
|
|
520
|
+
if (executionIdentities[kind].has(identity)) {
|
|
521
|
+
ctx.addIssue({
|
|
522
|
+
code: "custom",
|
|
523
|
+
path: armPath,
|
|
524
|
+
message: `measured executions must not reuse ${kind} identity`,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
executionIdentities[kind].add(identity);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (comparison.overall.n !== expectedN) {
|
|
533
|
+
ctx.addIssue({
|
|
534
|
+
code: "custom",
|
|
535
|
+
path: ["overall", "n"],
|
|
536
|
+
message: "measured sample count must equal the complete benchmark suite",
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
if (comparison.measurements.length > 0) {
|
|
540
|
+
refineMeasuredMean(comparison.overall.baseline, comparison.measurements.map((row) => row.baseline.receipt.benchmarkResult.material.score), ["overall", "baseline"], ctx);
|
|
541
|
+
refineMeasuredMean(comparison.overall.candidate, comparison.measurements.map((row) => row.candidate.receipt.benchmarkResult.material.score), ["overall", "candidate"], ctx);
|
|
542
|
+
}
|
|
170
543
|
const identities = new Set();
|
|
171
544
|
const qualityObjectives = new Set();
|
|
172
545
|
const dimensionParents = [];
|
|
@@ -175,6 +548,21 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
175
548
|
for (const [index, objective] of comparison.objectives.entries()) {
|
|
176
549
|
if (objective.availability === "measured") {
|
|
177
550
|
refineEstimate(objective, ["objectives", index], ctx);
|
|
551
|
+
if (objective.n !== expectedN) {
|
|
552
|
+
ctx.addIssue({
|
|
553
|
+
code: "custom",
|
|
554
|
+
path: ["objectives", index, "n"],
|
|
555
|
+
message: "measured objective count must equal the complete benchmark suite",
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (comparison.measurements.length > 0 && objective.kind === "cost") {
|
|
559
|
+
refineMeasuredMean(objective.baseline, comparison.measurements.map((row) => executionCostUsd(row.baseline)), ["objectives", index, "baseline"], ctx);
|
|
560
|
+
refineMeasuredMean(objective.candidate, comparison.measurements.map((row) => executionCostUsd(row.candidate)), ["objectives", index, "candidate"], ctx);
|
|
561
|
+
}
|
|
562
|
+
if (comparison.measurements.length > 0 && objective.kind === "latency") {
|
|
563
|
+
refineMeasuredMean(objective.baseline, comparison.measurements.map((row) => executionLatencyMs(row.baseline)), ["objectives", index, "baseline"], ctx);
|
|
564
|
+
refineMeasuredMean(objective.candidate, comparison.measurements.map((row) => executionLatencyMs(row.candidate)), ["objectives", index, "candidate"], ctx);
|
|
565
|
+
}
|
|
178
566
|
}
|
|
179
567
|
const identity = objective.kind === "dimension"
|
|
180
568
|
? `${objective.kind}:${objective.objective}:${objective.name}`
|
|
@@ -230,6 +618,29 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
230
618
|
message: "power analysis must use the paired held-out sample",
|
|
231
619
|
});
|
|
232
620
|
}
|
|
621
|
+
if (comparison.overall.confidenceInterval.level !==
|
|
622
|
+
comparison.experiment.policy.confidenceLevel ||
|
|
623
|
+
comparison.overall.confidenceInterval.resamples !==
|
|
624
|
+
comparison.experiment.policy.resamples ||
|
|
625
|
+
comparison.power.confidenceLevel !== comparison.experiment.policy.confidenceLevel) {
|
|
626
|
+
ctx.addIssue({
|
|
627
|
+
code: "custom",
|
|
628
|
+
path: ["experiment", "policy"],
|
|
629
|
+
message: "reported uncertainty must use the frozen evaluation policy",
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
for (const [index, objective] of comparison.objectives.entries()) {
|
|
633
|
+
if (objective.availability === "measured" &&
|
|
634
|
+
(objective.confidenceInterval.level !==
|
|
635
|
+
comparison.experiment.policy.confidenceLevel ||
|
|
636
|
+
objective.confidenceInterval.resamples !== comparison.experiment.policy.resamples)) {
|
|
637
|
+
ctx.addIssue({
|
|
638
|
+
code: "custom",
|
|
639
|
+
path: ["objectives", index, "confidenceInterval"],
|
|
640
|
+
message: "objective uncertainty must use the frozen evaluation policy",
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
233
644
|
if (!isCanonicalJsonValue(comparison)) {
|
|
234
645
|
ctx.addIssue({
|
|
235
646
|
code: "custom",
|
|
@@ -239,7 +650,6 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
239
650
|
});
|
|
240
651
|
export const agentImprovementProposalSchema = z
|
|
241
652
|
.object({
|
|
242
|
-
schemaVersion: z.literal(1),
|
|
243
653
|
kind: z.literal("agent-improvement-proposal"),
|
|
244
654
|
runId: z.string().min(1).max(200),
|
|
245
655
|
changedSurfaces: z
|
|
@@ -247,31 +657,34 @@ export const agentImprovementProposalSchema = z
|
|
|
247
657
|
.rest(improvementSurfaceSchema)
|
|
248
658
|
.refine((surfaces) => new Set(surfaces).size === surfaces.length, "changed surfaces must be unique"),
|
|
249
659
|
proposedAt: z.iso.datetime(),
|
|
250
|
-
baselineProfile: agentProfileSchema,
|
|
251
660
|
findings: z.array(canonicalJsonObjectSchema),
|
|
252
661
|
evaluation: agentImprovementMeasuredComparisonSchema,
|
|
253
|
-
candidateBundle: agentCandidateBundleSchema,
|
|
254
662
|
digest: sha256DigestSchema,
|
|
255
663
|
})
|
|
256
664
|
.strict()
|
|
257
665
|
.superRefine((proposal, ctx) => {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
666
|
+
if (proposal.evaluation.decision.outcome !== "ship") {
|
|
667
|
+
ctx.addIssue({
|
|
668
|
+
code: "custom",
|
|
669
|
+
path: ["evaluation", "decision", "outcome"],
|
|
670
|
+
message: "an improvement proposal requires a passing measured comparison",
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (!proposal.evaluation.power.sufficient ||
|
|
674
|
+
proposal.evaluation.overall.n <
|
|
675
|
+
proposal.evaluation.experiment.policy.minProductiveRuns) {
|
|
264
676
|
ctx.addIssue({
|
|
265
677
|
code: "custom",
|
|
266
|
-
path: ["evaluation", "
|
|
267
|
-
message: "
|
|
678
|
+
path: ["evaluation", "power"],
|
|
679
|
+
message: "an improvement proposal requires sufficient pre-registered power",
|
|
268
680
|
});
|
|
269
681
|
}
|
|
270
|
-
if (proposal.evaluation.
|
|
682
|
+
if (proposal.evaluation.experiment.baseline.digest ===
|
|
683
|
+
proposal.evaluation.experiment.candidate.digest) {
|
|
271
684
|
ctx.addIssue({
|
|
272
685
|
code: "custom",
|
|
273
|
-
path: ["evaluation", "
|
|
274
|
-
message: "
|
|
686
|
+
path: ["evaluation", "experiment", "candidate", "digest"],
|
|
687
|
+
message: "an improvement proposal requires a changed candidate bundle",
|
|
275
688
|
});
|
|
276
689
|
}
|
|
277
690
|
if (!isCanonicalJsonValue(proposal)) {
|
|
@@ -301,12 +714,33 @@ function refineEstimate(estimate, path, ctx) {
|
|
|
301
714
|
});
|
|
302
715
|
}
|
|
303
716
|
}
|
|
717
|
+
function refineMeasuredMean(reported, values, path, ctx) {
|
|
718
|
+
const measured = values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
719
|
+
const tolerance = Number.EPSILON * Math.max(1, Math.abs(measured)) * values.length * 8;
|
|
720
|
+
if (Math.abs(reported - measured) > tolerance) {
|
|
721
|
+
ctx.addIssue({
|
|
722
|
+
code: "custom",
|
|
723
|
+
path,
|
|
724
|
+
message: "reported mean must equal the signed per-cell results",
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function approximatelyEqual(left, right) {
|
|
729
|
+
const tolerance = Number.EPSILON * Math.max(1, Math.abs(left), Math.abs(right)) * 16;
|
|
730
|
+
return Math.abs(left - right) <= tolerance;
|
|
731
|
+
}
|
|
732
|
+
function executionCostUsd(evidence) {
|
|
733
|
+
return (evidence.receipt.modelSettlement.material.usage.costUsdNanos +
|
|
734
|
+
evidence.receipt.benchmarkResult.material.grading.usage.costUsdNanos) / 1_000_000_000;
|
|
735
|
+
}
|
|
736
|
+
function executionLatencyMs(evidence) {
|
|
737
|
+
return (evidence.receipt.timing.durationMs +
|
|
738
|
+
evidence.receipt.benchmarkResult.material.grading.timing.durationMs);
|
|
739
|
+
}
|
|
304
740
|
export const agentImprovementReviewSchema = z
|
|
305
741
|
.object({
|
|
306
|
-
schemaVersion: z.literal(1),
|
|
307
742
|
kind: z.literal("agent-improvement-review"),
|
|
308
743
|
proposalDigest: sha256DigestSchema,
|
|
309
|
-
candidateBundleDigest: sha256DigestSchema,
|
|
310
744
|
decision: z.enum(["approve", "reject", "request-changes"]),
|
|
311
745
|
reviewedBy: z.string().min(1),
|
|
312
746
|
reviewedAt: z.iso.datetime(),
|
|
@@ -316,94 +750,46 @@ export const agentImprovementReviewSchema = z
|
|
|
316
750
|
})
|
|
317
751
|
.strict()
|
|
318
752
|
.refine(isCanonicalJsonValue, "review must contain only RFC 8785 JSON values");
|
|
319
|
-
export const
|
|
753
|
+
export const agentImprovementActivationSchema = z
|
|
320
754
|
.object({
|
|
321
|
-
|
|
322
|
-
kind: z.literal("agent-candidate-execution-evidence"),
|
|
755
|
+
kind: z.literal("agent-improvement-activation"),
|
|
323
756
|
proposalDigest: sha256DigestSchema,
|
|
324
757
|
reviewDigest: sha256DigestSchema,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
758
|
+
experimentDigest: sha256DigestSchema,
|
|
759
|
+
candidateBundleDigest: sha256DigestSchema,
|
|
760
|
+
targets: z
|
|
761
|
+
.tuple([
|
|
762
|
+
z
|
|
763
|
+
.object({
|
|
764
|
+
surface: improvementSurfaceSchema,
|
|
765
|
+
identity: z.string().min(1).max(500),
|
|
766
|
+
expectedBaseDigest: sha256DigestSchema,
|
|
767
|
+
})
|
|
768
|
+
.strict(),
|
|
769
|
+
])
|
|
770
|
+
.rest(z
|
|
771
|
+
.object({
|
|
772
|
+
surface: improvementSurfaceSchema,
|
|
773
|
+
identity: z.string().min(1).max(500),
|
|
774
|
+
expectedBaseDigest: sha256DigestSchema,
|
|
775
|
+
})
|
|
776
|
+
.strict()),
|
|
777
|
+
fundingOwner: z.string().min(1).max(500),
|
|
778
|
+
authorizedBy: z.string().min(1).max(500),
|
|
779
|
+
authorizedAt: z.iso.datetime(),
|
|
330
780
|
digest: sha256DigestSchema,
|
|
331
781
|
})
|
|
332
782
|
.strict()
|
|
333
|
-
.superRefine((
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
const plannedOutcome = plan.material.task.outcome;
|
|
337
|
-
const capturedOutcome = evidence.receipt.taskOutcome.material.outcome;
|
|
338
|
-
const checks = [
|
|
339
|
-
[
|
|
340
|
-
evidence.receipt.materializationReceiptDigest === materialization.digest,
|
|
341
|
-
["receipt", "materializationReceiptDigest"],
|
|
342
|
-
"run receipt must bind the included materialization receipt",
|
|
343
|
-
],
|
|
344
|
-
[
|
|
345
|
-
evidence.receipt.executionPlanDigest === plan.digest,
|
|
346
|
-
["receipt", "executionPlanDigest"],
|
|
347
|
-
"run receipt must bind the included execution plan",
|
|
348
|
-
],
|
|
349
|
-
[
|
|
350
|
-
evidence.receipt.bundleDigest === materialization.bundleDigest,
|
|
351
|
-
["receipt", "bundleDigest"],
|
|
352
|
-
"run receipt and materialization must bind one bundle",
|
|
353
|
-
],
|
|
354
|
-
[
|
|
355
|
-
evidence.executionId === plan.material.executionId,
|
|
356
|
-
["executionId"],
|
|
357
|
-
"execution evidence must bind the materialized execution id",
|
|
358
|
-
],
|
|
359
|
-
[
|
|
360
|
-
evidence.profileActivation.profilePlan.digest ===
|
|
361
|
-
materialization.profilePlan.digest,
|
|
362
|
-
["profileActivation", "profilePlan", "digest"],
|
|
363
|
-
"profile activation must bind the materialized profile plan",
|
|
364
|
-
],
|
|
365
|
-
[
|
|
366
|
-
capturedOutcome.kind === plannedOutcome.kind,
|
|
367
|
-
["receipt", "taskOutcome", "material", "outcome", "kind"],
|
|
368
|
-
"task outcome kind must match the signed execution plan",
|
|
369
|
-
],
|
|
370
|
-
[
|
|
371
|
-
evidence.receipt.termination.kind === "exit" &&
|
|
372
|
-
evidence.receipt.termination.exitCode === 0,
|
|
373
|
-
["receipt", "termination"],
|
|
374
|
-
"successful execution evidence requires a zero exit status",
|
|
375
|
-
],
|
|
376
|
-
];
|
|
377
|
-
if (plannedOutcome.kind === "output" && capturedOutcome.kind === "output") {
|
|
378
|
-
checks.push([
|
|
379
|
-
capturedOutcome.spec.mediaType === plannedOutcome.mediaType &&
|
|
380
|
-
capturedOutcome.spec.maxBytes === plannedOutcome.maxBytes,
|
|
381
|
-
["receipt", "taskOutcome", "material", "outcome", "spec"],
|
|
382
|
-
"task output constraints must match the signed execution plan",
|
|
383
|
-
]);
|
|
384
|
-
}
|
|
385
|
-
if (plannedOutcome.kind === "workspace" &&
|
|
386
|
-
capturedOutcome.kind === "workspace" &&
|
|
387
|
-
plan.material.task.repository) {
|
|
388
|
-
const repository = plan.material.task.repository;
|
|
389
|
-
const base = capturedOutcome.baseRepository;
|
|
390
|
-
checks.push([
|
|
391
|
-
base.identity === repository.identity &&
|
|
392
|
-
base.rootIdentity === repository.rootIdentity &&
|
|
393
|
-
base.commit === repository.baseCommit &&
|
|
394
|
-
base.tree === repository.baseTree,
|
|
395
|
-
["receipt", "taskOutcome", "material", "outcome", "baseRepository"],
|
|
396
|
-
"workspace outcome must bind the signed repository base",
|
|
397
|
-
]);
|
|
398
|
-
}
|
|
399
|
-
for (const [valid, path, message] of checks) {
|
|
400
|
-
if (!valid)
|
|
401
|
-
ctx.addIssue({ code: "custom", path, message });
|
|
402
|
-
}
|
|
403
|
-
if (!isCanonicalJsonValue(evidence)) {
|
|
783
|
+
.superRefine((activation, ctx) => {
|
|
784
|
+
const identities = activation.targets.map((target) => `${target.surface}\u0000${target.identity}`);
|
|
785
|
+
if (new Set(identities).size !== identities.length) {
|
|
404
786
|
ctx.addIssue({
|
|
405
787
|
code: "custom",
|
|
406
|
-
|
|
788
|
+
path: ["targets"],
|
|
789
|
+
message: "activation targets must be unique by surface and identity",
|
|
407
790
|
});
|
|
408
791
|
}
|
|
792
|
+
if (!isCanonicalJsonValue(activation)) {
|
|
793
|
+
ctx.addIssue({ code: "custom", message: "activation must contain only RFC 8785 JSON values" });
|
|
794
|
+
}
|
|
409
795
|
});
|