@nexart/ai-execution 0.10.0 → 0.12.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
@@ -1,4 +1,4 @@
1
- # @nexart/ai-execution v0.10.0
1
+ # @nexart/ai-execution v0.12.0
2
2
 
3
3
  Tamper-evident records and Certified Execution Records (CER) for AI operations.
4
4
 
@@ -7,7 +7,7 @@ Tamper-evident records and Certified Execution Records (CER) for AI operations.
7
7
  | Component | Version |
8
8
  |---|---|
9
9
  | Service | — |
10
- | SDK | 0.10.0 |
10
+ | SDK | 0.12.0 |
11
11
  | Protocol | 1.2.0 |
12
12
 
13
13
  ## Why Not Just Store Logs?
@@ -22,6 +22,7 @@ This package creates integrity records for AI executions. Every time you call an
22
22
  - What you got back (output)
23
23
  - The exact parameters used (temperature, model, etc.)
24
24
  - SHA-256 hashes of everything for tamper detection
25
+ - Optionally: upstream context signals from other systems, sealed into the same certificate hash (v0.10.0+)
25
26
 
26
27
  These records can be verified offline to detect any post-hoc modification and prove integrity of the recorded execution.
27
28
 
@@ -168,6 +169,13 @@ Auto-generated fields (set by `createSnapshot`, do not set manually): `type`, `p
168
169
  "createdAt": "2026-02-12T00:00:00.000Z",
169
170
  "version": "0.1",
170
171
  "snapshot": { "..." : "..." },
172
+ "context": {
173
+ "signals": [
174
+ { "type": "approval", "source": "github-actions", "step": 0,
175
+ "timestamp": "2026-02-12T00:00:00.000Z", "actor": "alice",
176
+ "status": "ok", "payload": { "pr": 42 } }
177
+ ]
178
+ },
171
179
  "meta": { "source": "my-app", "tags": ["production"] },
172
180
  "declaration": {
173
181
  "stabilitySchemeId": "nexart-cer-v1",
@@ -178,9 +186,13 @@ Auto-generated fields (set by `createSnapshot`, do not set manually): `type`, `p
178
186
  }
179
187
  ```
180
188
 
189
+ `context` is **optional** and only appears when signals are supplied. Bundles without `context` are structurally identical to all prior versions.
190
+
181
191
  ### Certificate Hash Computation
182
192
 
183
- The `certificateHash` is SHA-256 of the UTF-8 bytes of the canonical JSON of exactly: `{ bundleType, version, createdAt, snapshot }`. `meta` and `declaration` are excluded. Key-ordering is recursive. This computation is identical across all SDK versions.
193
+ The `certificateHash` is SHA-256 of the UTF-8 bytes of the canonical JSON of exactly: `{ bundleType, version, createdAt, snapshot }` — or, when context signals are present: `{ bundleType, version, createdAt, snapshot, context }`. `meta` and `declaration` are always excluded. Key-ordering is recursive. This computation is identical across all SDK versions.
194
+
195
+ **Context and backward compatibility:** when no signals are provided (or an empty array is passed), `context` is omitted from the hash payload entirely. The resulting `certificateHash` is byte-for-byte identical to a pre-v0.10.0 bundle with the same snapshot. Passing signals is strictly additive — it can only extend the hash, never break it for existing callers.
184
196
 
185
197
  ### Declaration Block (v0.7.0+)
186
198
 
@@ -201,6 +213,78 @@ const bundle = sealCer(snapshot, {
201
213
  // verifyCer(bundle).ok === true — declaration does not affect the result
202
214
  ```
203
215
 
216
+ ## CER Packages (v0.12.0+)
217
+
218
+ A **CER package** is a transport/export envelope that wraps a sealed `cer.ai.execution.v1` bundle with optional receipt, signature, and attestation metadata.
219
+
220
+ ```
221
+ {
222
+ "cer": { ...sealed cer.ai.execution.v1 bundle... },
223
+ "receipt": { ...optional attestation receipt... },
224
+ "signature": "base64url...",
225
+ "attestation": { ...optional attestation summary... },
226
+ "verificationEnvelope": { ...optional envelope metadata... },
227
+ "verificationEnvelopeSignature": "base64url..."
228
+ }
229
+ ```
230
+
231
+ **When to use package helpers vs raw bundle helpers**
232
+
233
+ | Use case | Recommended API |
234
+ |---|---|
235
+ | Creating, verifying, or archiving a CER locally | `certifyDecision`, `verifyCer`, `exportCer` / `importCer` (raw bundle) |
236
+ | Wrapping a CER for transport or external storage with optional metadata | `createCerPackage`, `exportCerPackage`, `importCerPackage` |
237
+ | Detecting whether an object is a package or a raw bundle | `isCerPackage` |
238
+ | Extracting the CER from a received package | `getCerFromPackage` |
239
+ | Verifying integrity of a received package's inner CER | `verifyCerPackage` |
240
+
241
+ The `cer` field inside a package is authoritative. Package helpers never mutate it, never re-hash it, and never inject package-level fields into the CER.
242
+
243
+ ### Creating and exporting a package
244
+
245
+ ```typescript
246
+ import { certifyDecision, createCerPackage, exportCerPackage } from '@nexart/ai-execution';
247
+
248
+ const cer = certifyDecision({
249
+ provider: 'openai',
250
+ model: 'gpt-4o',
251
+ prompt: 'Summarize.',
252
+ input: userQuery,
253
+ output: llmResponse,
254
+ parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
255
+ });
256
+
257
+ const pkg = createCerPackage({
258
+ cer,
259
+ receipt: myAttestationReceipt, // optional
260
+ signature: 'base64url...', // optional
261
+ });
262
+
263
+ const json = exportCerPackage(pkg); // stable canonical JSON
264
+ ```
265
+
266
+ ### Importing and verifying a package
267
+
268
+ ```typescript
269
+ import { importCerPackage, verifyCerPackage, getCerFromPackage } from '@nexart/ai-execution';
270
+
271
+ // importCerPackage parses + validates shape + verifies inner CER hash
272
+ const pkg = importCerPackage(receivedJsonString); // throws CerVerificationError on failure
273
+
274
+ // Alternatively: parse yourself then verify
275
+ const result = verifyCerPackage(parsedObj);
276
+ if (!result.ok) throw new Error(result.errors.join('; '));
277
+
278
+ const cer = getCerFromPackage(parsedObj); // throws if not a valid package
279
+ ```
280
+
281
+ **Design constraints of package helpers:**
282
+ - Additive only — no changes to CER hashing, canonicalization, or `verifyCer()` semantics
283
+ - `verifyCerPackage` only verifies the inner `cer` bundle; receipt/signature/envelope fields are opaque transport metadata not verified here
284
+ - All existing raw bundle flows (`importCer`, `exportCer`, `verifyCer`) are unchanged
285
+
286
+ ---
287
+
204
288
  ## Attestation
205
289
 
206
290
  Endpoint: `POST {nodeUrl}/api/attest`
@@ -582,7 +666,7 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
582
666
  | `verifySnapshot(snapshot)` | Verify snapshot hashes and structure |
583
667
  | `sealCer(snapshot, options?)` | Seal snapshot into CER bundle |
584
668
  | `verify(bundle)` / `verifyCer(bundle)` | Verify CER bundle |
585
- | `certifyDecision(params)` | One-call: createSnapshot + sealCer |
669
+ | `certifyDecision(params)` | One-call: createSnapshot + sealCer. Accepts optional `signals?: CerContextSignal[]` — included in `certificateHash` when present (v0.10.0+) |
586
670
 
587
671
  ### Workflow
588
672
 
@@ -623,6 +707,19 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
623
707
  | `exportCer(bundle)` | Serialize to canonical JSON string |
624
708
  | `importCer(json)` | Parse + verify from JSON string |
625
709
 
710
+ ### CER Package Helpers (v0.12.0+)
711
+
712
+ | Function | Description |
713
+ |---|---|
714
+ | `isCerPackage(value)` | Type guard: returns `true` if `value` is a CER package shape (`{ cer: { bundleType: 'cer.ai.execution.v1', ... }, ... }`). Structural check only — does not verify the inner CER hash. |
715
+ | `createCerPackage(params)` | Assemble a CER package from a sealed CER and optional transport fields (`receipt`, `signature`, `attestation`, `verificationEnvelope`, `verificationEnvelopeSignature`). Assembly only — does not re-hash or re-sign. |
716
+ | `getCerFromPackage(pkg)` | Extract and return the inner CER bundle. Throws `CerVerificationError` if `pkg` is not a valid package shape. |
717
+ | `exportCerPackage(pkg)` | Serialize a CER package to a stable canonical JSON string. |
718
+ | `importCerPackage(json)` | Parse a CER package JSON string, validate its shape, and verify the inner CER with `verifyCer()`. Throws `CerVerificationError` on any failure. |
719
+ | `verifyCerPackage(pkg)` | Verify the inner CER of a package using `verifyCer()`. Returns a `VerificationResult`. Conservative: only verifies the inner `cer` — does not verify receipt/signature/envelope. |
720
+
721
+ **Exported types:** `AiCerPackage`, `CreateCerPackageParams`
722
+
626
723
  ### Provider Drop-in (v0.6.0+)
627
724
 
628
725
  | Function | Description |
@@ -674,7 +771,7 @@ Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR`
674
771
 
675
772
  ## LangChain Integration
676
773
 
677
- `@nexart/ai-execution` v0.10.0 includes a minimal LangChain helper surface. Certify the final input and output of any LangChain chain, agent, or runnable as a tamper-evident CER — no LangChain package dependency required.
774
+ `@nexart/ai-execution` includes a minimal LangChain helper surface (introduced v0.10.0, current v0.12.0). Certify the final input and output of any LangChain chain, agent, or runnable as a tamper-evident CER — no LangChain package dependency required.
678
775
 
679
776
  `certifyLangChainRun` operates in two modes depending on whether `nodeUrl` and `apiKey` are supplied:
680
777
 
@@ -747,6 +844,8 @@ console.log(result.receipt);
747
844
 
748
845
  ### Three helpers
749
846
 
847
+ All three helpers accept an optional `signals?: CerContextSignal[]` field on their input. When provided, signals are sealed into `bundle.context` and included in `certificateHash`. Omitting signals produces a hash identical to a bundle without signals.
848
+
750
849
  | Helper | Returns | Network |
751
850
  |---|---|---|
752
851
  | `createLangChainCer(input)` | `LangChainCerResult` (sync) | Never |
@@ -834,6 +933,99 @@ const result = await certifyLangChainRun(
834
933
 
835
934
  ---
836
935
 
936
+ ## Context Signals (v0.10.0+)
937
+
938
+ CERs can optionally include **context signals** — structured, protocol-agnostic records of upstream events that were present when the AI execution ran. Signals are evidence-only: they have no effect on how the AI call is made, what parameters are used, or how the output is processed. Their only role is to be bound into the `certificateHash` alongside the execution record so that any post-hoc modification to either the signals or the snapshot is detectable.
939
+
940
+ **What signals are:**
941
+ - Records of things that happened before or alongside the AI call (approvals, deploys, audits, tool invocations, review outcomes, etc.)
942
+ - Protocol-agnostic — `type`, `source`, `actor`, and `payload` are all free-form strings and objects
943
+ - Included in `certificateHash` only when present — omitting signals produces a hash identical to pre-v0.10.0
944
+
945
+ **What signals are not:**
946
+ - Governance enforcement — NexArt does not interpret signal content or enforce policy based on it
947
+ - Execution dependencies — signals do not gate or modify the AI call in any way
948
+ - A replacement for `toolCalls` — `toolCalls` records tool invocations *within* a run; signals record upstream context *around* a run
949
+
950
+ ### Quick example
951
+
952
+ ```ts
953
+ import { createSignalCollector } from '@nexart/signals';
954
+ import { certifyDecision, verifyCer } from '@nexart/ai-execution';
955
+
956
+ // 1. Collect upstream signals during your pipeline run
957
+ const collector = createSignalCollector({ defaultSource: 'github-actions' });
958
+ collector.add({ type: 'approval', actor: 'alice', status: 'ok', payload: { pr: 42 } });
959
+ collector.add({ type: 'deploy', actor: 'ci-bot', status: 'ok', payload: { env: 'prod' } });
960
+ const { signals } = collector.export();
961
+
962
+ // 2. Certify the AI execution with signals bound as context evidence
963
+ const bundle = certifyDecision({
964
+ provider: 'openai',
965
+ model: 'gpt-4o-mini',
966
+ prompt: 'Summarise.',
967
+ input: userQuery,
968
+ output: llmResponse,
969
+ parameters: { temperature: 0, maxTokens: 512, topP: null, seed: null },
970
+ signals, // ← included in certificateHash; omit to get identical hash as before
971
+ });
972
+
973
+ verifyCer(bundle).ok; // true
974
+ console.log(bundle.context?.signals.length); // 2 — signals are sealed in the bundle
975
+ ```
976
+
977
+ `NexArtSignal[]` from `@nexart/signals` is structurally identical to `CerContextSignal[]` — no casting or conversion needed. `@nexart/ai-execution` has no hard dependency on `@nexart/signals`; any object matching the `CerContextSignal` shape works.
978
+
979
+ ### Signals also work on the LangChain path
980
+
981
+ ```ts
982
+ import { certifyLangChainRun } from '@nexart/ai-execution';
983
+
984
+ const { bundle } = certifyLangChainRun({
985
+ provider: 'openai',
986
+ model: 'gpt-4o-mini',
987
+ input: { messages: [...] },
988
+ output: { text: '...' },
989
+ signals, // ← same field, same semantics
990
+ });
991
+ ```
992
+
993
+ ### CerContextSignal shape
994
+
995
+ Every `CerContextSignal` has the following fields. All are always present in the stored bundle — no undefined values:
996
+
997
+ | Field | Type | Default | Description |
998
+ |---|---|---|---|
999
+ | `type` | `string` | required | Signal category — free-form (e.g. `"approval"`, `"deploy"`, `"audit"`) |
1000
+ | `source` | `string` | required | Upstream system — free-form (e.g. `"github-actions"`, `"linear"`) |
1001
+ | `step` | `number` | `0` | Position in sequence — used for ordering within the `context.signals` array |
1002
+ | `timestamp` | `string` | current time | ISO 8601 |
1003
+ | `actor` | `string` | `"unknown"` | Who produced the signal — free-form |
1004
+ | `status` | `string` | `"ok"` | Outcome — free-form (e.g. `"ok"`, `"error"`, `"pending"`) |
1005
+ | `payload` | `Record<string, unknown>` | `{}` | Opaque upstream data — NexArt does not interpret this |
1006
+
1007
+ Signal ordering within the array is part of the sealed hash. Reordering signals produces a different `certificateHash`.
1008
+
1009
+ ### Tamper detection
1010
+
1011
+ `verifyCer()` always reconstructs `context` from the stored `bundle.context.signals` when recomputing the hash. Any mutation — changing a signal field, adding or removing signals, or injecting a `context` block into a no-signals bundle — produces a `CERTIFICATE_HASH_MISMATCH` result:
1012
+
1013
+ ```ts
1014
+ const tampered = { ...bundle, context: { signals: [...altered] } };
1015
+ verifyCer(tampered).ok; // false
1016
+ verifyCer(tampered).code; // 'CERTIFICATE_HASH_MISMATCH'
1017
+ ```
1018
+
1019
+ ### Exported types
1020
+
1021
+ ```ts
1022
+ import type { CerContextSignal, CerContext } from '@nexart/ai-execution';
1023
+ ```
1024
+
1025
+ `CerContext` is the shape of `bundle.context` — `{ signals: CerContextSignal[] }`. Both are exported from the package root.
1026
+
1027
+ ---
1028
+
837
1029
  ## Version History
838
1030
 
839
1031
  | Version | Description |
@@ -849,7 +1041,9 @@ const result = await certifyLangChainRun(
849
1041
  | v0.7.0 | AIEF alignment: `verifyAief()` (AIEF §9.1 adapter); `verifyRunSummary()` chain verifier; `makeToolEvent()` + `hashToolOutput()` + `snapshot.toolCalls` (AIEF-06); `BundleDeclaration` block (`stabilitySchemeId`, `protectedSetId`, `protectedFields`) excluded from `certificateHash`; `redactBeforeSeal()` pre-seal verifiable redaction; `validateProfile()` (flexible/AIEF_L2/L3/L4); 5 new `CerVerifyCode` entries; backward-compatible, no hash changes |
850
1042
  | v0.8.0 | Helper APIs: `exportVerifiableRedacted()` (post-seal re-seal with redacted snapshot + provenance metadata); `certifyAndAttestRun()` (one-call multi-step certify + optional per-step attestation with injectable mock); test determinism fix; all v0.1–v0.7 bundles verify identically |
851
1043
  | v0.9.0 | CER Protocol types: `CerVerificationResult`, `ReasonCode`, `CheckStatus`; `verifyAiCerBundleDetailed()`; `CertifyDecisionParams.createdAt` wired through; determinism bug fix |
852
- | **v0.10.0** | LangChain integration: `createLangChainCer()` (sync/local); `certifyLangChainRun()` dual-mode — local (sync) or node-attested (`Promise<LangChainAttestedResult>` when `nodeUrl`+`apiKey` supplied); `LangChainAttestedResult`, `AttestDecisionFn`; injectable `_attestFn` for test mocking; `@nexart/ai-execution/langchain` subpath; 47 tests (was 34); all prior bundles verify identically |
1044
+ | v0.10.0 | LangChain integration: `createLangChainCer()` (sync/local); `certifyLangChainRun()` dual-mode — local (sync) or node-attested (`Promise<LangChainAttestedResult>` when `nodeUrl`+`apiKey` supplied); `LangChainAttestedResult`, `AttestDecisionFn`; injectable `_attestFn` for test mocking. **Context signals:** optional `signals?: CerContextSignal[]` on `certifyDecision`, `certifyLangChainRun`, and `createLangChainCer` — sealed into `bundle.context` and included in `certificateHash` when non-empty; absent or empty = hash unchanged from pre-v0.10.0. New types: `CerContextSignal`, `CerContext`. New example: `examples/signals-cer.ts`. 413 total tests; all prior bundles verify identically |
1045
+ | v0.11.0 | Version release. Ships all v0.10.0 features (LangChain integration + context signals) as the published package version. `cerSignals.test.js` added to the `npm test` script. No API, hash, or canonicalization changes |
1046
+ | **v0.12.0** | CER package helpers: `isCerPackage`, `createCerPackage`, `getCerFromPackage`, `exportCerPackage`, `importCerPackage`, `verifyCerPackage`. New types: `AiCerPackage`, `CreateCerPackageParams`. Additive only — no changes to CER hashing, canonicalization, or verification semantics. 466 total tests; all prior bundles verify identically |
853
1047
  | v1.0.0 | Planned: API stabilization, freeze public API surface |
854
1048
 
855
1049
  ## Releasing
package/dist/index.cjs CHANGED
@@ -44,18 +44,23 @@ __export(src_exports, {
44
44
  certifyLangChainRun: () => certifyLangChainRun,
45
45
  computeInputHash: () => computeInputHash,
46
46
  computeOutputHash: () => computeOutputHash,
47
+ createCerPackage: () => createCerPackage,
47
48
  createClient: () => createClient,
48
49
  createLangChainCer: () => createLangChainCer,
49
50
  createSnapshot: () => createSnapshot,
50
51
  exportCer: () => exportCer,
52
+ exportCerPackage: () => exportCerPackage,
51
53
  exportVerifiableRedacted: () => exportVerifiableRedacted,
52
54
  fetchNodeKeys: () => fetchNodeKeys,
53
55
  getAttestationReceipt: () => getAttestationReceipt,
56
+ getCerFromPackage: () => getCerFromPackage,
54
57
  hasAttestation: () => hasAttestation,
55
58
  hashCanonicalJson: () => hashCanonicalJson,
56
59
  hashToolOutput: () => hashToolOutput,
57
60
  hashUtf8: () => hashUtf8,
58
61
  importCer: () => importCer,
62
+ importCerPackage: () => importCerPackage,
63
+ isCerPackage: () => isCerPackage,
59
64
  makeToolEvent: () => makeToolEvent,
60
65
  mapToAiefReason: () => mapToAiefReason,
61
66
  redactBeforeSeal: () => redactBeforeSeal,
@@ -72,6 +77,7 @@ __export(src_exports, {
72
77
  verifyAief: () => verifyAief,
73
78
  verifyBundleAttestation: () => verifyBundleAttestation,
74
79
  verifyCer: () => verifyCer,
80
+ verifyCerPackage: () => verifyCerPackage,
75
81
  verifyNodeReceiptSignature: () => verifyNodeReceiptSignature,
76
82
  verifyRunSummary: () => verifyRunSummary,
77
83
  verifySnapshot: () => verifySnapshot,
@@ -339,14 +345,22 @@ function computeCertificateHash(payload) {
339
345
  const canonical = toCanonicalJson(payload);
340
346
  return `sha256:${sha256Hex(canonical)}`;
341
347
  }
348
+ function buildContext(signals) {
349
+ if (!signals || signals.length === 0) return void 0;
350
+ return { signals };
351
+ }
342
352
  function sealCer(snapshot, options) {
343
353
  const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
354
+ const context = buildContext(options?.signals);
344
355
  const payload = {
345
356
  bundleType: "cer.ai.execution.v1",
346
357
  createdAt,
347
358
  snapshot,
348
359
  version: "0.1"
349
360
  };
361
+ if (context) {
362
+ payload.context = context;
363
+ }
350
364
  const certificateHash = computeCertificateHash(payload);
351
365
  const bundle = {
352
366
  bundleType: "cer.ai.execution.v1",
@@ -355,6 +369,9 @@ function sealCer(snapshot, options) {
355
369
  version: "0.1",
356
370
  snapshot
357
371
  };
372
+ if (context) {
373
+ bundle.context = context;
374
+ }
358
375
  if (options?.meta) {
359
376
  bundle.meta = options.meta;
360
377
  }
@@ -403,6 +420,10 @@ function verifyCer(bundle) {
403
420
  snapshot: bundle.snapshot,
404
421
  version: "0.1"
405
422
  };
423
+ const verifyContext = buildContext(bundle.context?.signals);
424
+ if (verifyContext) {
425
+ payload.context = verifyContext;
426
+ }
406
427
  const expectedHash = computeCertificateHash(payload);
407
428
  if (bundle.certificateHash !== expectedHash) {
408
429
  certHashErrors.push(`certificateHash mismatch: expected ${expectedHash}, got ${bundle.certificateHash}`);
@@ -463,7 +484,11 @@ function certifyDecision(params) {
463
484
  conversationId: params.conversationId,
464
485
  prevStepHash: params.prevStepHash
465
486
  });
466
- return sealCer(snapshot, { createdAt: params.createdAt, meta: params.meta });
487
+ return sealCer(snapshot, {
488
+ createdAt: params.createdAt,
489
+ meta: params.meta,
490
+ signals: params.signals
491
+ });
467
492
  }
468
493
 
469
494
  // src/run.ts
@@ -2688,7 +2713,8 @@ function buildCertifyParams(input, executionId) {
2688
2713
  runId: typeof input.metadata?.runId === "string" ? input.metadata.runId : void 0,
2689
2714
  workflowId: typeof input.metadata?.workflowId === "string" ? input.metadata.workflowId : void 0,
2690
2715
  conversationId: typeof input.metadata?.conversationId === "string" ? input.metadata.conversationId : void 0,
2691
- meta: buildMeta(input.metadata)
2716
+ meta: buildMeta(input.metadata),
2717
+ signals: input.signals
2692
2718
  };
2693
2719
  }
2694
2720
  function createLangChainCer(input) {
@@ -2717,6 +2743,67 @@ function certifyLangChainRun(input, _options) {
2717
2743
  }
2718
2744
  return createLangChainCer(input);
2719
2745
  }
2746
+
2747
+ // src/package.ts
2748
+ function isCerPackage(value) {
2749
+ if (typeof value !== "object" || value === null) return false;
2750
+ const pkg = value;
2751
+ if (typeof pkg["cer"] !== "object" || pkg["cer"] === null) return false;
2752
+ const cer = pkg["cer"];
2753
+ return cer["bundleType"] === "cer.ai.execution.v1";
2754
+ }
2755
+ function createCerPackage(params) {
2756
+ const pkg = { cer: params.cer };
2757
+ if (params.receipt !== void 0) pkg.receipt = params.receipt;
2758
+ if (params.signature !== void 0) pkg.signature = params.signature;
2759
+ if (params.attestation !== void 0) pkg.attestation = params.attestation;
2760
+ if (params.verificationEnvelope !== void 0) pkg.verificationEnvelope = params.verificationEnvelope;
2761
+ if (params.verificationEnvelopeSignature !== void 0) pkg.verificationEnvelopeSignature = params.verificationEnvelopeSignature;
2762
+ return pkg;
2763
+ }
2764
+ function getCerFromPackage(pkg) {
2765
+ if (!isCerPackage(pkg)) {
2766
+ throw new CerVerificationError([
2767
+ "getCerFromPackage: value is not a valid CER package (missing or invalid cer field)"
2768
+ ]);
2769
+ }
2770
+ return pkg.cer;
2771
+ }
2772
+ function exportCerPackage(pkg) {
2773
+ return toCanonicalJson(pkg);
2774
+ }
2775
+ function importCerPackage(json) {
2776
+ let parsed;
2777
+ try {
2778
+ parsed = JSON.parse(json);
2779
+ } catch (err2) {
2780
+ throw new CerVerificationError([
2781
+ `importCerPackage: invalid JSON: ${err2.message}`
2782
+ ]);
2783
+ }
2784
+ if (!isCerPackage(parsed)) {
2785
+ throw new CerVerificationError([
2786
+ "importCerPackage: parsed value is not a CER package (missing or invalid cer field)"
2787
+ ]);
2788
+ }
2789
+ const result = verifyCer(parsed.cer);
2790
+ if (!result.ok) {
2791
+ throw new CerVerificationError([
2792
+ `importCerPackage: inner CER failed verification: ${result.errors.join("; ")}`
2793
+ ]);
2794
+ }
2795
+ return parsed;
2796
+ }
2797
+ function verifyCerPackage(pkg) {
2798
+ if (!isCerPackage(pkg)) {
2799
+ return {
2800
+ ok: false,
2801
+ errors: ["verifyCerPackage: value is not a CER package (missing or invalid cer field)"],
2802
+ code: CerVerifyCode.SCHEMA_ERROR
2803
+ };
2804
+ }
2805
+ return verifyCer(pkg.cer);
2806
+ }
2720
2807
  // Annotate the CommonJS export names for ESM import in node:
2721
2808
  0 && (module.exports = {
2722
2809
  CerAttestationError,
@@ -2733,18 +2820,23 @@ function certifyLangChainRun(input, _options) {
2733
2820
  certifyLangChainRun,
2734
2821
  computeInputHash,
2735
2822
  computeOutputHash,
2823
+ createCerPackage,
2736
2824
  createClient,
2737
2825
  createLangChainCer,
2738
2826
  createSnapshot,
2739
2827
  exportCer,
2828
+ exportCerPackage,
2740
2829
  exportVerifiableRedacted,
2741
2830
  fetchNodeKeys,
2742
2831
  getAttestationReceipt,
2832
+ getCerFromPackage,
2743
2833
  hasAttestation,
2744
2834
  hashCanonicalJson,
2745
2835
  hashToolOutput,
2746
2836
  hashUtf8,
2747
2837
  importCer,
2838
+ importCerPackage,
2839
+ isCerPackage,
2748
2840
  makeToolEvent,
2749
2841
  mapToAiefReason,
2750
2842
  redactBeforeSeal,
@@ -2761,6 +2853,7 @@ function certifyLangChainRun(input, _options) {
2761
2853
  verifyAief,
2762
2854
  verifyBundleAttestation,
2763
2855
  verifyCer,
2856
+ verifyCerPackage,
2764
2857
  verifyNodeReceiptSignature,
2765
2858
  verifyRunSummary,
2766
2859
  verifySnapshot,