@nexart/ai-execution 0.10.0 → 0.11.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 +114 -6
- package/dist/index.cjs +22 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +22 -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.11.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.11.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
|
|
|
@@ -582,7 +594,7 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
|
|
|
582
594
|
| `verifySnapshot(snapshot)` | Verify snapshot hashes and structure |
|
|
583
595
|
| `sealCer(snapshot, options?)` | Seal snapshot into CER bundle |
|
|
584
596
|
| `verify(bundle)` / `verifyCer(bundle)` | Verify CER bundle |
|
|
585
|
-
| `certifyDecision(params)` | One-call: createSnapshot + sealCer |
|
|
597
|
+
| `certifyDecision(params)` | One-call: createSnapshot + sealCer. Accepts optional `signals?: CerContextSignal[]` — included in `certificateHash` when present (v0.10.0+) |
|
|
586
598
|
|
|
587
599
|
### Workflow
|
|
588
600
|
|
|
@@ -674,7 +686,7 @@ Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR`
|
|
|
674
686
|
|
|
675
687
|
## LangChain Integration
|
|
676
688
|
|
|
677
|
-
`@nexart/ai-execution`
|
|
689
|
+
`@nexart/ai-execution` includes a minimal LangChain helper surface (introduced v0.10.0, current v0.11.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
690
|
|
|
679
691
|
`certifyLangChainRun` operates in two modes depending on whether `nodeUrl` and `apiKey` are supplied:
|
|
680
692
|
|
|
@@ -747,6 +759,8 @@ console.log(result.receipt);
|
|
|
747
759
|
|
|
748
760
|
### Three helpers
|
|
749
761
|
|
|
762
|
+
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.
|
|
763
|
+
|
|
750
764
|
| Helper | Returns | Network |
|
|
751
765
|
|---|---|---|
|
|
752
766
|
| `createLangChainCer(input)` | `LangChainCerResult` (sync) | Never |
|
|
@@ -834,6 +848,99 @@ const result = await certifyLangChainRun(
|
|
|
834
848
|
|
|
835
849
|
---
|
|
836
850
|
|
|
851
|
+
## Context Signals (v0.10.0+)
|
|
852
|
+
|
|
853
|
+
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.
|
|
854
|
+
|
|
855
|
+
**What signals are:**
|
|
856
|
+
- Records of things that happened before or alongside the AI call (approvals, deploys, audits, tool invocations, review outcomes, etc.)
|
|
857
|
+
- Protocol-agnostic — `type`, `source`, `actor`, and `payload` are all free-form strings and objects
|
|
858
|
+
- Included in `certificateHash` only when present — omitting signals produces a hash identical to pre-v0.10.0
|
|
859
|
+
|
|
860
|
+
**What signals are not:**
|
|
861
|
+
- Governance enforcement — NexArt does not interpret signal content or enforce policy based on it
|
|
862
|
+
- Execution dependencies — signals do not gate or modify the AI call in any way
|
|
863
|
+
- A replacement for `toolCalls` — `toolCalls` records tool invocations *within* a run; signals record upstream context *around* a run
|
|
864
|
+
|
|
865
|
+
### Quick example
|
|
866
|
+
|
|
867
|
+
```ts
|
|
868
|
+
import { createSignalCollector } from '@nexart/signals';
|
|
869
|
+
import { certifyDecision, verifyCer } from '@nexart/ai-execution';
|
|
870
|
+
|
|
871
|
+
// 1. Collect upstream signals during your pipeline run
|
|
872
|
+
const collector = createSignalCollector({ defaultSource: 'github-actions' });
|
|
873
|
+
collector.add({ type: 'approval', actor: 'alice', status: 'ok', payload: { pr: 42 } });
|
|
874
|
+
collector.add({ type: 'deploy', actor: 'ci-bot', status: 'ok', payload: { env: 'prod' } });
|
|
875
|
+
const { signals } = collector.export();
|
|
876
|
+
|
|
877
|
+
// 2. Certify the AI execution with signals bound as context evidence
|
|
878
|
+
const bundle = certifyDecision({
|
|
879
|
+
provider: 'openai',
|
|
880
|
+
model: 'gpt-4o-mini',
|
|
881
|
+
prompt: 'Summarise.',
|
|
882
|
+
input: userQuery,
|
|
883
|
+
output: llmResponse,
|
|
884
|
+
parameters: { temperature: 0, maxTokens: 512, topP: null, seed: null },
|
|
885
|
+
signals, // ← included in certificateHash; omit to get identical hash as before
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
verifyCer(bundle).ok; // true
|
|
889
|
+
console.log(bundle.context?.signals.length); // 2 — signals are sealed in the bundle
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
`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.
|
|
893
|
+
|
|
894
|
+
### Signals also work on the LangChain path
|
|
895
|
+
|
|
896
|
+
```ts
|
|
897
|
+
import { certifyLangChainRun } from '@nexart/ai-execution';
|
|
898
|
+
|
|
899
|
+
const { bundle } = certifyLangChainRun({
|
|
900
|
+
provider: 'openai',
|
|
901
|
+
model: 'gpt-4o-mini',
|
|
902
|
+
input: { messages: [...] },
|
|
903
|
+
output: { text: '...' },
|
|
904
|
+
signals, // ← same field, same semantics
|
|
905
|
+
});
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
### CerContextSignal shape
|
|
909
|
+
|
|
910
|
+
Every `CerContextSignal` has the following fields. All are always present in the stored bundle — no undefined values:
|
|
911
|
+
|
|
912
|
+
| Field | Type | Default | Description |
|
|
913
|
+
|---|---|---|---|
|
|
914
|
+
| `type` | `string` | required | Signal category — free-form (e.g. `"approval"`, `"deploy"`, `"audit"`) |
|
|
915
|
+
| `source` | `string` | required | Upstream system — free-form (e.g. `"github-actions"`, `"linear"`) |
|
|
916
|
+
| `step` | `number` | `0` | Position in sequence — used for ordering within the `context.signals` array |
|
|
917
|
+
| `timestamp` | `string` | current time | ISO 8601 |
|
|
918
|
+
| `actor` | `string` | `"unknown"` | Who produced the signal — free-form |
|
|
919
|
+
| `status` | `string` | `"ok"` | Outcome — free-form (e.g. `"ok"`, `"error"`, `"pending"`) |
|
|
920
|
+
| `payload` | `Record<string, unknown>` | `{}` | Opaque upstream data — NexArt does not interpret this |
|
|
921
|
+
|
|
922
|
+
Signal ordering within the array is part of the sealed hash. Reordering signals produces a different `certificateHash`.
|
|
923
|
+
|
|
924
|
+
### Tamper detection
|
|
925
|
+
|
|
926
|
+
`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:
|
|
927
|
+
|
|
928
|
+
```ts
|
|
929
|
+
const tampered = { ...bundle, context: { signals: [...altered] } };
|
|
930
|
+
verifyCer(tampered).ok; // false
|
|
931
|
+
verifyCer(tampered).code; // 'CERTIFICATE_HASH_MISMATCH'
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Exported types
|
|
935
|
+
|
|
936
|
+
```ts
|
|
937
|
+
import type { CerContextSignal, CerContext } from '@nexart/ai-execution';
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
`CerContext` is the shape of `bundle.context` — `{ signals: CerContextSignal[] }`. Both are exported from the package root.
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
837
944
|
## Version History
|
|
838
945
|
|
|
839
946
|
| Version | Description |
|
|
@@ -849,7 +956,8 @@ const result = await certifyLangChainRun(
|
|
|
849
956
|
| 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
957
|
| 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
958
|
| v0.9.0 | CER Protocol types: `CerVerificationResult`, `ReasonCode`, `CheckStatus`; `verifyAiCerBundleDetailed()`; `CertifyDecisionParams.createdAt` wired through; determinism bug fix |
|
|
852
|
-
|
|
|
959
|
+
| 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 |
|
|
960
|
+
| **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 |
|
|
853
961
|
| v1.0.0 | Planned: API stabilization, freeze public API surface |
|
|
854
962
|
|
|
855
963
|
## Releasing
|
package/dist/index.cjs
CHANGED
|
@@ -339,14 +339,22 @@ function computeCertificateHash(payload) {
|
|
|
339
339
|
const canonical = toCanonicalJson(payload);
|
|
340
340
|
return `sha256:${sha256Hex(canonical)}`;
|
|
341
341
|
}
|
|
342
|
+
function buildContext(signals) {
|
|
343
|
+
if (!signals || signals.length === 0) return void 0;
|
|
344
|
+
return { signals };
|
|
345
|
+
}
|
|
342
346
|
function sealCer(snapshot, options) {
|
|
343
347
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
348
|
+
const context = buildContext(options?.signals);
|
|
344
349
|
const payload = {
|
|
345
350
|
bundleType: "cer.ai.execution.v1",
|
|
346
351
|
createdAt,
|
|
347
352
|
snapshot,
|
|
348
353
|
version: "0.1"
|
|
349
354
|
};
|
|
355
|
+
if (context) {
|
|
356
|
+
payload.context = context;
|
|
357
|
+
}
|
|
350
358
|
const certificateHash = computeCertificateHash(payload);
|
|
351
359
|
const bundle = {
|
|
352
360
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -355,6 +363,9 @@ function sealCer(snapshot, options) {
|
|
|
355
363
|
version: "0.1",
|
|
356
364
|
snapshot
|
|
357
365
|
};
|
|
366
|
+
if (context) {
|
|
367
|
+
bundle.context = context;
|
|
368
|
+
}
|
|
358
369
|
if (options?.meta) {
|
|
359
370
|
bundle.meta = options.meta;
|
|
360
371
|
}
|
|
@@ -403,6 +414,10 @@ function verifyCer(bundle) {
|
|
|
403
414
|
snapshot: bundle.snapshot,
|
|
404
415
|
version: "0.1"
|
|
405
416
|
};
|
|
417
|
+
const verifyContext = buildContext(bundle.context?.signals);
|
|
418
|
+
if (verifyContext) {
|
|
419
|
+
payload.context = verifyContext;
|
|
420
|
+
}
|
|
406
421
|
const expectedHash = computeCertificateHash(payload);
|
|
407
422
|
if (bundle.certificateHash !== expectedHash) {
|
|
408
423
|
certHashErrors.push(`certificateHash mismatch: expected ${expectedHash}, got ${bundle.certificateHash}`);
|
|
@@ -463,7 +478,11 @@ function certifyDecision(params) {
|
|
|
463
478
|
conversationId: params.conversationId,
|
|
464
479
|
prevStepHash: params.prevStepHash
|
|
465
480
|
});
|
|
466
|
-
return sealCer(snapshot, {
|
|
481
|
+
return sealCer(snapshot, {
|
|
482
|
+
createdAt: params.createdAt,
|
|
483
|
+
meta: params.meta,
|
|
484
|
+
signals: params.signals
|
|
485
|
+
});
|
|
467
486
|
}
|
|
468
487
|
|
|
469
488
|
// src/run.ts
|
|
@@ -2688,7 +2707,8 @@ function buildCertifyParams(input, executionId) {
|
|
|
2688
2707
|
runId: typeof input.metadata?.runId === "string" ? input.metadata.runId : void 0,
|
|
2689
2708
|
workflowId: typeof input.metadata?.workflowId === "string" ? input.metadata.workflowId : void 0,
|
|
2690
2709
|
conversationId: typeof input.metadata?.conversationId === "string" ? input.metadata.conversationId : void 0,
|
|
2691
|
-
meta: buildMeta(input.metadata)
|
|
2710
|
+
meta: buildMeta(input.metadata),
|
|
2711
|
+
signals: input.signals
|
|
2692
2712
|
};
|
|
2693
2713
|
}
|
|
2694
2714
|
function createLangChainCer(input) {
|