@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 +200 -6
- package/dist/index.cjs +95 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -3
- package/dist/index.d.ts +117 -3
- package/dist/index.mjs +89 -2
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +18 -2
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.d.cts +10 -1
- package/dist/langchain.d.ts +10 -1
- package/dist/langchain.mjs +18 -2
- package/dist/langchain.mjs.map +1 -1
- package/dist/providers/anthropic.cjs +11 -0
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.mjs +11 -0
- package/dist/providers/anthropic.mjs.map +1 -1
- package/dist/providers/openai.cjs +11 -0
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.mjs +11 -0
- package/dist/providers/openai.mjs.map +1 -1
- package/dist/providers/wrap.cjs +11 -0
- package/dist/providers/wrap.cjs.map +1 -1
- package/dist/providers/wrap.d.cts +1 -1
- package/dist/providers/wrap.d.ts +1 -1
- package/dist/providers/wrap.mjs +11 -0
- package/dist/providers/wrap.mjs.map +1 -1
- package/dist/{types-C5t12OK8.d.cts → types-C_M2xSWK.d.cts} +49 -1
- package/dist/{types-C5t12OK8.d.ts → types-C_M2xSWK.d.ts} +49 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @nexart/ai-execution v0.
|
|
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
|
+
| 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`
|
|
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
|
-
|
|
|
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, {
|
|
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,
|