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 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.`);
@@ -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
@@ -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`, {
@@ -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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcert",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "Assurance release gates, runtime evidence, and regression CI for AI agents.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",