agentcert 0.5.4 → 0.6.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/README.md +23 -0
- package/dist/cli.js +26 -0
- package/dist/command-help.js +3 -0
- package/dist/control-plane.js +1 -0
- package/dist/schema-validator.js +68 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,6 +57,29 @@ npx agentcert connect --server https://agentcert.app --project your-project-id
|
|
|
57
57
|
npx agentcert push --evidence .agentcert/latest/agentcert-evidence.json
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
Bind a release, pull-request, or nightly run to an issued continuous assurance
|
|
61
|
+
case by declaring the exact reviewed scope:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx agentcert run --config agentcert.config.json --push \
|
|
65
|
+
--assurance-case "$AGENTCERT_ASSURANCE_CASE_ID" \
|
|
66
|
+
--assurance-scope agentcert.assurance-scope.json \
|
|
67
|
+
--assurance-trigger auto
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Validate the scope before CI uses it:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx agentcert schema validate \
|
|
74
|
+
--schema assurance-scope \
|
|
75
|
+
--file agentcert.assurance-scope.json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`auto` treats pull requests as prospective, scheduled workflows as nightly,
|
|
79
|
+
and other GitHub runs as release checks. Authoritative failure or scope drift
|
|
80
|
+
sets the Hosted contract to `REVALIDATION_REQUIRED`; only an independently
|
|
81
|
+
issued successor case establishes a new `CURRENT` baseline.
|
|
82
|
+
|
|
60
83
|
Add `--push` to `agentcert run` to run locally and upload the resulting bundle
|
|
61
84
|
in one command. By default, both commands also upload local files referenced by
|
|
62
85
|
the bundle. Reads are confined to `--artifact-root` (the current directory by
|
package/dist/cli.js
CHANGED
|
@@ -611,6 +611,7 @@ async function pushHostedEvidence(bundle, bytes, fileName) {
|
|
|
611
611
|
externalId: readFlag("--external-id"),
|
|
612
612
|
companionArtifacts: companions?.artifacts,
|
|
613
613
|
skippedCompanionArtifacts: companions?.skipped,
|
|
614
|
+
assurance: await readContinuousAssuranceBinding(),
|
|
614
615
|
});
|
|
615
616
|
process.stdout.write(`Hosted run: ${result.runId}\nHosted evidence: ${result.evidenceId}\n`);
|
|
616
617
|
if (companions) {
|
|
@@ -623,6 +624,31 @@ async function pushHostedEvidence(bundle, bytes, fileName) {
|
|
|
623
624
|
}
|
|
624
625
|
}
|
|
625
626
|
}
|
|
627
|
+
async function readContinuousAssuranceBinding() {
|
|
628
|
+
const caseId = readFlag("--assurance-case");
|
|
629
|
+
const scopePath = readFlag("--assurance-scope");
|
|
630
|
+
if (!caseId && !scopePath)
|
|
631
|
+
return undefined;
|
|
632
|
+
if (!caseId || !scopePath)
|
|
633
|
+
throw new Error("--assurance-case and --assurance-scope must be provided together.");
|
|
634
|
+
const scope = await readJson(scopePath);
|
|
635
|
+
const validation = validateAgentCertSchema("assurance-scope", scope);
|
|
636
|
+
if (!validation.valid)
|
|
637
|
+
throw new Error(`Assurance scope is invalid:\n${validation.errors.map((error) => `- ${error}`).join("\n")}`);
|
|
638
|
+
return { caseId, trigger: assuranceTriggerFromEnvironment(readFlag("--assurance-trigger") ?? "auto"), scope: scope };
|
|
639
|
+
}
|
|
640
|
+
function assuranceTriggerFromEnvironment(value) {
|
|
641
|
+
if (value === "pull_request" || value === "release" || value === "nightly")
|
|
642
|
+
return value;
|
|
643
|
+
if (value !== "auto")
|
|
644
|
+
throw new Error("--assurance-trigger must be auto, pull_request, release, or nightly.");
|
|
645
|
+
const event = process.env.GITHUB_EVENT_NAME;
|
|
646
|
+
if (event === "pull_request" || event === "pull_request_target")
|
|
647
|
+
return "pull_request";
|
|
648
|
+
if (event === "schedule")
|
|
649
|
+
return "nightly";
|
|
650
|
+
return "release";
|
|
651
|
+
}
|
|
626
652
|
async function promptValue(label) {
|
|
627
653
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
628
654
|
throw new Error(`Missing ${label.trim().replace(/:$/, "").toLowerCase()}. Pass it as a flag or environment variable.`);
|
package/dist/command-help.js
CHANGED
|
@@ -34,6 +34,9 @@ Options:
|
|
|
34
34
|
--project <id> Hosted project ID
|
|
35
35
|
--api-key <key> Project API key (prefer AGENTCERT_API_KEY in CI)
|
|
36
36
|
--external-id <id> Idempotent hosted run ID
|
|
37
|
+
--assurance-case <id> Issued assurance case to reconcile
|
|
38
|
+
--assurance-scope <p> Declared agent/model/prompt/tools/policy/suite scope JSON
|
|
39
|
+
--assurance-trigger <t> auto, pull_request, release, or nightly (default: auto)
|
|
37
40
|
--artifact-root <dir> Allowed root for companion artifacts (default: current directory)
|
|
38
41
|
--no-artifacts Upload only the evidence bundle
|
|
39
42
|
--help, -h Show this help
|
package/dist/control-plane.js
CHANGED
|
@@ -70,6 +70,7 @@ export async function pushEvidenceToControlPlane(options) {
|
|
|
70
70
|
subject: bundle.subject,
|
|
71
71
|
products: bundle.summary.products,
|
|
72
72
|
},
|
|
73
|
+
assurance: options.assurance,
|
|
73
74
|
}),
|
|
74
75
|
});
|
|
75
76
|
await requestJson(request, `${projectUrl}/runs/${encodeURIComponent(run.id)}/events`, {
|
package/dist/schema-validator.js
CHANGED
|
@@ -13,6 +13,7 @@ export function parseSchemaId(input) {
|
|
|
13
13
|
value === "release-gate" ||
|
|
14
14
|
value === "assurance-report" ||
|
|
15
15
|
value === "assurance-delivery" ||
|
|
16
|
+
value === "assurance-scope" ||
|
|
16
17
|
value === "evidence-signature" ||
|
|
17
18
|
value === "evidence-strength" ||
|
|
18
19
|
value === "action-mandate" ||
|
|
@@ -20,7 +21,7 @@ export function parseSchemaId(input) {
|
|
|
20
21
|
value === "trusted-run-receipt") {
|
|
21
22
|
return value;
|
|
22
23
|
}
|
|
23
|
-
throw new Error(`Unsupported schema "${value}". Use evidence-bundle, result, corpus-record, failure-review, classifier-eval, monitor-snapshot, robustness-lab, release-gate, assurance-report, assurance-delivery, evidence-signature, evidence-strength, action-mandate, trusted-action-record, or trusted-run-receipt.`);
|
|
24
|
+
throw new Error(`Unsupported schema "${value}". Use evidence-bundle, result, corpus-record, failure-review, classifier-eval, monitor-snapshot, robustness-lab, release-gate, assurance-report, assurance-delivery, assurance-scope, evidence-signature, evidence-strength, action-mandate, trusted-action-record, or trusted-run-receipt.`);
|
|
24
25
|
}
|
|
25
26
|
export function validateAgentCertSchema(schema, input) {
|
|
26
27
|
const errors = [];
|
|
@@ -52,6 +53,8 @@ export function validateAgentCertSchema(schema, input) {
|
|
|
52
53
|
validateAssuranceReport(value, errors);
|
|
53
54
|
if (schema === "assurance-delivery")
|
|
54
55
|
validateAssuranceDelivery(value, errors);
|
|
56
|
+
if (schema === "assurance-scope")
|
|
57
|
+
validateAssuranceScope(value, errors);
|
|
55
58
|
if (schema === "evidence-strength")
|
|
56
59
|
validateEvidenceStrength(value, errors);
|
|
57
60
|
if (schema === "action-mandate")
|
|
@@ -293,6 +296,7 @@ function validateAssuranceReport(value, errors) {
|
|
|
293
296
|
errors.push("evaluationPlanSha256 must be a lowercase SHA-256 digest.");
|
|
294
297
|
validateTimestamp(value.issuedAt, "issuedAt", errors);
|
|
295
298
|
validateTimestamp(value.expiresAt, "expiresAt", errors);
|
|
299
|
+
validateAssuranceContinuity(value.continuousAssurance, errors);
|
|
296
300
|
}
|
|
297
301
|
function validateAssuranceDelivery(value, errors) {
|
|
298
302
|
requiredConst(value, "schemaVersion", "agentcert.assurance_delivery.v0.1", errors);
|
|
@@ -320,6 +324,69 @@ function validateAssuranceDelivery(value, errors) {
|
|
|
320
324
|
const decision = recordValue(value.decision);
|
|
321
325
|
if (decision)
|
|
322
326
|
requiredEnum(decision, "verdict", ["RELEASE", "RELEASE_WITH_CONTROLS", "BLOCK"], errors);
|
|
327
|
+
validateAssuranceContinuity(value.continuousAssurance, errors);
|
|
328
|
+
}
|
|
329
|
+
function validateAssuranceContinuity(input, errors) {
|
|
330
|
+
if (input === undefined)
|
|
331
|
+
return;
|
|
332
|
+
const value = recordValue(input);
|
|
333
|
+
if (!value) {
|
|
334
|
+
errors.push("continuousAssurance must be an object.");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
requiredConst(value, "schemaVersion", "agentcert.assurance_continuity.v0.1", errors);
|
|
338
|
+
requiredObject(value, "scope", errors);
|
|
339
|
+
requiredSha256At(value, "scopeFingerprintSha256", "continuousAssurance.scopeFingerprintSha256", errors);
|
|
340
|
+
requiredConst(value, "freshnessAtIssuance", "CURRENT", errors);
|
|
341
|
+
requiredArray(value, "revalidationRequiredWhen", errors);
|
|
342
|
+
stringArray(value.revalidationRequiredWhen, "continuousAssurance.revalidationRequiredWhen", errors);
|
|
343
|
+
const scope = recordValue(value.scope);
|
|
344
|
+
if (scope) {
|
|
345
|
+
const nested = [];
|
|
346
|
+
validateAssuranceScope(scope, nested);
|
|
347
|
+
errors.push(...nested.map((error) => `continuousAssurance.scope.${error}`));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function validateAssuranceScope(value, errors) {
|
|
351
|
+
requiredConst(value, "schemaVersion", "agentcert.assurance_scope.v0.1", errors);
|
|
352
|
+
for (const key of ["agent", "model", "prompt", "tools", "policy", "scenarioSuite"])
|
|
353
|
+
requiredObject(value, key, errors);
|
|
354
|
+
const agent = recordValue(value.agent);
|
|
355
|
+
const model = recordValue(value.model);
|
|
356
|
+
const prompt = recordValue(value.prompt);
|
|
357
|
+
const tools = recordValue(value.tools);
|
|
358
|
+
const policy = recordValue(value.policy);
|
|
359
|
+
const suite = recordValue(value.scenarioSuite);
|
|
360
|
+
if (agent) {
|
|
361
|
+
requiredStringAt(agent, "id", "agent.id", errors);
|
|
362
|
+
requiredStringAt(agent, "version", "agent.version", errors);
|
|
363
|
+
optionalSha256At(agent, "artifactSha256", "agent.artifactSha256", errors);
|
|
364
|
+
}
|
|
365
|
+
if (model)
|
|
366
|
+
for (const key of ["provider", "name", "version"])
|
|
367
|
+
requiredStringAt(model, key, `model.${key}`, errors);
|
|
368
|
+
if (prompt)
|
|
369
|
+
requiredSha256At(prompt, "sha256", "prompt.sha256", errors);
|
|
370
|
+
if (tools)
|
|
371
|
+
requiredSha256At(tools, "manifestSha256", "tools.manifestSha256", errors);
|
|
372
|
+
if (policy) {
|
|
373
|
+
requiredStringAt(policy, "id", "policy.id", errors);
|
|
374
|
+
requiredStringAt(policy, "version", "policy.version", errors);
|
|
375
|
+
optionalSha256At(policy, "sha256", "policy.sha256", errors);
|
|
376
|
+
}
|
|
377
|
+
if (suite) {
|
|
378
|
+
requiredStringAt(suite, "id", "scenarioSuite.id", errors);
|
|
379
|
+
requiredStringAt(suite, "version", "scenarioSuite.version", errors);
|
|
380
|
+
requiredSha256At(suite, "sha256", "scenarioSuite.sha256", errors);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function requiredSha256At(value, key, path, errors) {
|
|
384
|
+
if (typeof value[key] !== "string" || !/^[0-9a-f]{64}$/.test(value[key]))
|
|
385
|
+
errors.push(`${path} must be a lowercase SHA-256 digest.`);
|
|
386
|
+
}
|
|
387
|
+
function optionalSha256At(value, key, path, errors) {
|
|
388
|
+
if (value[key] !== undefined && (typeof value[key] !== "string" || !/^[0-9a-f]{64}$/.test(value[key])))
|
|
389
|
+
errors.push(`${path} must be a lowercase SHA-256 digest.`);
|
|
323
390
|
}
|
|
324
391
|
function validateFailureReview(value, errors) {
|
|
325
392
|
requiredConst(value, "schemaVersion", "1", errors);
|