@tangle-network/agent-interface 0.28.0 → 0.30.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-code-schema.d.ts +0 -8
- package/dist/agent-candidate-code-schema.js +0 -1
- package/dist/agent-candidate-execution-plan-schema.d.ts +193 -350
- package/dist/agent-candidate-execution-plan-schema.js +98 -131
- package/dist/agent-candidate-lineage-schema.d.ts +1 -28
- package/dist/agent-candidate-lineage-schema.js +3 -33
- package/dist/agent-candidate-outcome-schema.d.ts +48 -18
- package/dist/agent-candidate-outcome-schema.js +19 -24
- package/dist/agent-candidate-profile-schema.d.ts +3 -28
- package/dist/agent-candidate-profile-schema.js +15 -27
- package/dist/agent-candidate-promotion-schema.d.ts +10981 -2356
- package/dist/agent-candidate-promotion-schema.js +495 -105
- package/dist/agent-candidate-receipt-schema.d.ts +268 -303
- package/dist/agent-candidate-receipt-schema.js +82 -8
- 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 +3 -52
- package/dist/agent-candidate-schema.js +3 -28
- 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 +191 -75
- package/dist/agent-candidate.test-fixture.d.ts +0 -26
- package/dist/agent-candidate.test-fixture.js +0 -26
- package/dist/agent-profile.d.ts +33 -9
- package/dist/harness-capabilities.d.ts +2 -2
- package/dist/harness-capabilities.js +23 -14
- package/dist/profile-schema.d.ts +65 -84
- package/dist/profile-schema.js +82 -32
- package/dist/profile-security.js +2 -0
- package/package.json +2 -2
|
@@ -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,18 +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
216
|
kind: z.literal("agent-improvement-measured-comparison"),
|
|
96
|
-
|
|
217
|
+
experiment: agentCandidateExperimentSchema,
|
|
218
|
+
measurements: z.array(z
|
|
97
219
|
.object({
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
splitDigest: sha256DigestSchema,
|
|
220
|
+
baseline: candidateExecutionEvidenceSchema,
|
|
221
|
+
candidate: candidateExecutionEvidenceSchema,
|
|
101
222
|
})
|
|
102
|
-
.strict(),
|
|
103
|
-
baselineProfileDigest: sha256DigestSchema,
|
|
104
|
-
candidateBundleDigest: sha256DigestSchema,
|
|
223
|
+
.strict()),
|
|
105
224
|
overall: z
|
|
106
225
|
.object({
|
|
107
226
|
name: z.literal("composite"),
|
|
@@ -157,7 +276,11 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
157
276
|
evaluation: z
|
|
158
277
|
.object({
|
|
159
278
|
generationsExplored: z.number().int().nonnegative(),
|
|
279
|
+
searchDurationMs: z.number().finite().nonnegative(),
|
|
280
|
+
executionDurationMs: z.number().finite().nonnegative(),
|
|
160
281
|
durationMs: z.number().finite().nonnegative(),
|
|
282
|
+
searchCostUsd: z.number().finite().nonnegative(),
|
|
283
|
+
executionCostUsd: z.number().finite().nonnegative(),
|
|
161
284
|
totalCostUsd: z.number().finite().nonnegative(),
|
|
162
285
|
})
|
|
163
286
|
.strict(),
|
|
@@ -166,6 +289,257 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
166
289
|
.strict()
|
|
167
290
|
.superRefine((comparison, ctx) => {
|
|
168
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
|
+
}
|
|
169
543
|
const identities = new Set();
|
|
170
544
|
const qualityObjectives = new Set();
|
|
171
545
|
const dimensionParents = [];
|
|
@@ -174,6 +548,21 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
174
548
|
for (const [index, objective] of comparison.objectives.entries()) {
|
|
175
549
|
if (objective.availability === "measured") {
|
|
176
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
|
+
}
|
|
177
566
|
}
|
|
178
567
|
const identity = objective.kind === "dimension"
|
|
179
568
|
? `${objective.kind}:${objective.objective}:${objective.name}`
|
|
@@ -229,6 +618,29 @@ export const agentImprovementMeasuredComparisonSchema = z
|
|
|
229
618
|
message: "power analysis must use the paired held-out sample",
|
|
230
619
|
});
|
|
231
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
|
+
}
|
|
232
644
|
if (!isCanonicalJsonValue(comparison)) {
|
|
233
645
|
ctx.addIssue({
|
|
234
646
|
code: "custom",
|
|
@@ -245,31 +657,34 @@ export const agentImprovementProposalSchema = z
|
|
|
245
657
|
.rest(improvementSurfaceSchema)
|
|
246
658
|
.refine((surfaces) => new Set(surfaces).size === surfaces.length, "changed surfaces must be unique"),
|
|
247
659
|
proposedAt: z.iso.datetime(),
|
|
248
|
-
baselineProfile: agentProfileSchema,
|
|
249
660
|
findings: z.array(canonicalJsonObjectSchema),
|
|
250
661
|
evaluation: agentImprovementMeasuredComparisonSchema,
|
|
251
|
-
candidateBundle: agentCandidateBundleSchema,
|
|
252
662
|
digest: sha256DigestSchema,
|
|
253
663
|
})
|
|
254
664
|
.strict()
|
|
255
665
|
.superRefine((proposal, ctx) => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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) {
|
|
262
676
|
ctx.addIssue({
|
|
263
677
|
code: "custom",
|
|
264
|
-
path: ["evaluation", "
|
|
265
|
-
message: "
|
|
678
|
+
path: ["evaluation", "power"],
|
|
679
|
+
message: "an improvement proposal requires sufficient pre-registered power",
|
|
266
680
|
});
|
|
267
681
|
}
|
|
268
|
-
if (proposal.evaluation.
|
|
682
|
+
if (proposal.evaluation.experiment.baseline.digest ===
|
|
683
|
+
proposal.evaluation.experiment.candidate.digest) {
|
|
269
684
|
ctx.addIssue({
|
|
270
685
|
code: "custom",
|
|
271
|
-
path: ["evaluation", "
|
|
272
|
-
message: "
|
|
686
|
+
path: ["evaluation", "experiment", "candidate", "digest"],
|
|
687
|
+
message: "an improvement proposal requires a changed candidate bundle",
|
|
273
688
|
});
|
|
274
689
|
}
|
|
275
690
|
if (!isCanonicalJsonValue(proposal)) {
|
|
@@ -299,11 +714,33 @@ function refineEstimate(estimate, path, ctx) {
|
|
|
299
714
|
});
|
|
300
715
|
}
|
|
301
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
|
+
}
|
|
302
740
|
export const agentImprovementReviewSchema = z
|
|
303
741
|
.object({
|
|
304
742
|
kind: z.literal("agent-improvement-review"),
|
|
305
743
|
proposalDigest: sha256DigestSchema,
|
|
306
|
-
candidateBundleDigest: sha256DigestSchema,
|
|
307
744
|
decision: z.enum(["approve", "reject", "request-changes"]),
|
|
308
745
|
reviewedBy: z.string().min(1),
|
|
309
746
|
reviewedAt: z.iso.datetime(),
|
|
@@ -313,93 +750,46 @@ export const agentImprovementReviewSchema = z
|
|
|
313
750
|
})
|
|
314
751
|
.strict()
|
|
315
752
|
.refine(isCanonicalJsonValue, "review must contain only RFC 8785 JSON values");
|
|
316
|
-
export const
|
|
753
|
+
export const agentImprovementActivationSchema = z
|
|
317
754
|
.object({
|
|
318
|
-
kind: z.literal("agent-
|
|
755
|
+
kind: z.literal("agent-improvement-activation"),
|
|
319
756
|
proposalDigest: sha256DigestSchema,
|
|
320
757
|
reviewDigest: sha256DigestSchema,
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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(),
|
|
326
780
|
digest: sha256DigestSchema,
|
|
327
781
|
})
|
|
328
782
|
.strict()
|
|
329
|
-
.superRefine((
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
const plannedOutcome = plan.material.task.outcome;
|
|
333
|
-
const capturedOutcome = evidence.receipt.taskOutcome.material.outcome;
|
|
334
|
-
const checks = [
|
|
335
|
-
[
|
|
336
|
-
evidence.receipt.materializationReceiptDigest === materialization.digest,
|
|
337
|
-
["receipt", "materializationReceiptDigest"],
|
|
338
|
-
"run receipt must bind the included materialization receipt",
|
|
339
|
-
],
|
|
340
|
-
[
|
|
341
|
-
evidence.receipt.executionPlanDigest === plan.digest,
|
|
342
|
-
["receipt", "executionPlanDigest"],
|
|
343
|
-
"run receipt must bind the included execution plan",
|
|
344
|
-
],
|
|
345
|
-
[
|
|
346
|
-
evidence.receipt.bundleDigest === materialization.bundleDigest,
|
|
347
|
-
["receipt", "bundleDigest"],
|
|
348
|
-
"run receipt and materialization must bind one bundle",
|
|
349
|
-
],
|
|
350
|
-
[
|
|
351
|
-
evidence.executionId === plan.material.executionId,
|
|
352
|
-
["executionId"],
|
|
353
|
-
"execution evidence must bind the materialized execution id",
|
|
354
|
-
],
|
|
355
|
-
[
|
|
356
|
-
evidence.profileActivation.profilePlan.digest ===
|
|
357
|
-
materialization.profilePlan.digest,
|
|
358
|
-
["profileActivation", "profilePlan", "digest"],
|
|
359
|
-
"profile activation must bind the materialized profile plan",
|
|
360
|
-
],
|
|
361
|
-
[
|
|
362
|
-
capturedOutcome.kind === plannedOutcome.kind,
|
|
363
|
-
["receipt", "taskOutcome", "material", "outcome", "kind"],
|
|
364
|
-
"task outcome kind must match the signed execution plan",
|
|
365
|
-
],
|
|
366
|
-
[
|
|
367
|
-
evidence.receipt.termination.kind === "exit" &&
|
|
368
|
-
evidence.receipt.termination.exitCode === 0,
|
|
369
|
-
["receipt", "termination"],
|
|
370
|
-
"successful execution evidence requires a zero exit status",
|
|
371
|
-
],
|
|
372
|
-
];
|
|
373
|
-
if (plannedOutcome.kind === "output" && capturedOutcome.kind === "output") {
|
|
374
|
-
checks.push([
|
|
375
|
-
capturedOutcome.spec.mediaType === plannedOutcome.mediaType &&
|
|
376
|
-
capturedOutcome.spec.maxBytes === plannedOutcome.maxBytes,
|
|
377
|
-
["receipt", "taskOutcome", "material", "outcome", "spec"],
|
|
378
|
-
"task output constraints must match the signed execution plan",
|
|
379
|
-
]);
|
|
380
|
-
}
|
|
381
|
-
if (plannedOutcome.kind === "workspace" &&
|
|
382
|
-
capturedOutcome.kind === "workspace" &&
|
|
383
|
-
plan.material.task.repository) {
|
|
384
|
-
const repository = plan.material.task.repository;
|
|
385
|
-
const base = capturedOutcome.baseRepository;
|
|
386
|
-
checks.push([
|
|
387
|
-
base.identity === repository.identity &&
|
|
388
|
-
base.rootIdentity === repository.rootIdentity &&
|
|
389
|
-
base.commit === repository.baseCommit &&
|
|
390
|
-
base.tree === repository.baseTree,
|
|
391
|
-
["receipt", "taskOutcome", "material", "outcome", "baseRepository"],
|
|
392
|
-
"workspace outcome must bind the signed repository base",
|
|
393
|
-
]);
|
|
394
|
-
}
|
|
395
|
-
for (const [valid, path, message] of checks) {
|
|
396
|
-
if (!valid)
|
|
397
|
-
ctx.addIssue({ code: "custom", path, message });
|
|
398
|
-
}
|
|
399
|
-
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) {
|
|
400
786
|
ctx.addIssue({
|
|
401
787
|
code: "custom",
|
|
402
|
-
|
|
788
|
+
path: ["targets"],
|
|
789
|
+
message: "activation targets must be unique by surface and identity",
|
|
403
790
|
});
|
|
404
791
|
}
|
|
792
|
+
if (!isCanonicalJsonValue(activation)) {
|
|
793
|
+
ctx.addIssue({ code: "custom", message: "activation must contain only RFC 8785 JSON values" });
|
|
794
|
+
}
|
|
405
795
|
});
|