@nexart/ai-execution 0.5.0 → 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 +25 -2
- package/dist/index.cjs +317 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -3
- package/dist/index.d.ts +115 -3
- package/dist/index.mjs +313 -9
- package/dist/index.mjs.map +1 -1
- package/dist/providers/anthropic.cjs +1 -1
- 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 +1 -1
- package/dist/providers/anthropic.mjs.map +1 -1
- package/dist/providers/openai.cjs +1 -1
- 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 +1 -1
- package/dist/providers/openai.mjs.map +1 -1
- package/dist/providers/wrap.cjs +1 -1
- 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 +1 -1
- package/dist/providers/wrap.mjs.map +1 -1
- package/dist/{types-BjEqrksn.d.cts → types-Cgb52dTx.d.cts} +52 -1
- package/dist/{types-BjEqrksn.d.ts → types-Cgb52dTx.d.ts} +52 -1
- package/fixtures/v060/legacy-attestation.json +32 -0
- package/fixtures/v060/original-meta-bundle.json +36 -0
- package/fixtures/v060/pre-v05-bundle.json +29 -0
- package/fixtures/v060/redacted-bundle.json +36 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
# @nexart/ai-execution v0.
|
|
1
|
+
# @nexart/ai-execution v0.6.0
|
|
2
2
|
|
|
3
3
|
Tamper-evident records and Certified Execution Records (CER) for AI operations.
|
|
4
4
|
|
|
5
|
+
## Version Information
|
|
6
|
+
|
|
7
|
+
| Component | Version |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Service | — |
|
|
10
|
+
| SDK | 0.6.0 |
|
|
11
|
+
| Protocol | 1.2.0 |
|
|
12
|
+
|
|
5
13
|
## Why Not Just Store Logs?
|
|
6
14
|
|
|
7
15
|
Logs tell you what happened. CERs prove integrity. A log entry can be edited, truncated, or fabricated after the fact with no way to detect it. A CER bundle is cryptographically sealed: any modification — to the input, output, parameters, or ordering — invalidates the certificate hash. If you need to demonstrate to an auditor, regulator, or downstream system that a recorded execution has not been modified post-hoc, logs are insufficient. CERs provide the tamper-evident chain of custody that logs cannot. **CERs certify records, not model determinism or provider execution.**
|
|
@@ -332,10 +340,24 @@ Fixtures at `fixtures/vectors/` and `fixtures/golden/`. Cross-language implement
|
|
|
332
340
|
| `selectNodeKey(doc, kid?)` | Select a key from a `NodeKeysDocument` by kid or activeKid (v0.5.0+) |
|
|
333
341
|
| `verifyBundleAttestation(bundle, options)` | One-call offline attestation verification (v0.5.0+) |
|
|
334
342
|
| `sanitizeForAttestation(bundle)` | Remove `undefined` keys, reject BigInt/functions/symbols |
|
|
343
|
+
| `sanitizeForStorage(bundle, options?)` | Sanitize for DB storage with optional path-based redaction; does not recompute hashes (v0.6.0+) |
|
|
344
|
+
| `sanitizeForStamp(bundle)` | Extract attestable core (no meta); does not recompute hashes (v0.6.0+) |
|
|
335
345
|
| `hasAttestation(bundle)` | Check if bundle already has attestation fields |
|
|
336
346
|
| `exportCer(bundle)` | Serialize to canonical JSON string |
|
|
337
347
|
| `importCer(json)` | Parse + verify from JSON string |
|
|
338
348
|
|
|
349
|
+
### Provider Drop-in (v0.6.0+)
|
|
350
|
+
|
|
351
|
+
| Function | Description |
|
|
352
|
+
|---|---|
|
|
353
|
+
| `certifyDecisionFromProviderCall(params)` | One-function wrapper: extracts prompt/input/output/params from raw provider request+response and returns `{ ok, bundle }` or `{ ok: false, code: 'SCHEMA_ERROR', reason }`. Supports OpenAI, Anthropic, Gemini, Mistral, Bedrock, and generic shapes. |
|
|
354
|
+
|
|
355
|
+
### Opinionated Client (v0.6.0+)
|
|
356
|
+
|
|
357
|
+
| Export | Description |
|
|
358
|
+
|---|---|
|
|
359
|
+
| `createClient(defaults)` | Returns a `NexArtClient` with bound defaults (`appId`, `workflowId`, `nodeUrl`, `apiKey`, `tags`, `source`). Methods: `certifyDecision`, `certifyAndAttestDecision`, `verify`, `verifyBundleAttestation`. Defaults do not affect bundle hashing. |
|
|
360
|
+
|
|
339
361
|
### Reason Codes
|
|
340
362
|
|
|
341
363
|
`CerVerifyCode` — stable string-union constant exported from the package root:
|
|
@@ -378,7 +400,8 @@ Priority when multiple failures exist: `CANONICALIZATION_ERROR` > `SCHEMA_ERROR`
|
|
|
378
400
|
| v0.4.0 | Dual ESM/CJS build, `sanitizeForAttestation`, `hasAttestation`, auto-sanitize in `attest()`, fixed `ERR_PACKAGE_PATH_NOT_EXPORTED` |
|
|
379
401
|
| v0.4.1 | Verification reason codes (`CerVerifyCode`), `code` + `details` on `VerificationResult`, README provenance wording tightened |
|
|
380
402
|
| v0.4.2 | `AttestationReceipt`, `getAttestationReceipt`, `certifyAndAttestDecision`, `attestIfNeeded` |
|
|
381
|
-
|
|
|
403
|
+
| v0.5.0 | Ed25519 signed receipt verification: `verifyNodeReceiptSignature`, `verifyBundleAttestation`, `fetchNodeKeys`, `selectNodeKey`; new attestation `CerVerifyCode` entries; `SPEC.md`; `NodeKeysDocument`, `SignedAttestationReceipt`, `NodeReceiptVerifyResult` types |
|
|
404
|
+
| **v0.6.0** | Frictionless integration: `certifyDecisionFromProviderCall` (OpenAI/Anthropic/Gemini/Mistral/Bedrock drop-in); `sanitizeForStorage` + `sanitizeForStamp` redaction helpers; `createClient(defaults)` factory; regression fixture suite; all backward-compatible, no hash changes |
|
|
382
405
|
| v1.0.0 | Planned: API stabilization, freeze public API surface |
|
|
383
406
|
|
|
384
407
|
## Releasing
|
package/dist/index.cjs
CHANGED
|
@@ -38,8 +38,10 @@ __export(src_exports, {
|
|
|
38
38
|
attestIfNeeded: () => attestIfNeeded,
|
|
39
39
|
certifyAndAttestDecision: () => certifyAndAttestDecision,
|
|
40
40
|
certifyDecision: () => certifyDecision,
|
|
41
|
+
certifyDecisionFromProviderCall: () => certifyDecisionFromProviderCall,
|
|
41
42
|
computeInputHash: () => computeInputHash,
|
|
42
43
|
computeOutputHash: () => computeOutputHash,
|
|
44
|
+
createClient: () => createClient,
|
|
43
45
|
createSnapshot: () => createSnapshot,
|
|
44
46
|
exportCer: () => exportCer,
|
|
45
47
|
fetchNodeKeys: () => fetchNodeKeys,
|
|
@@ -49,6 +51,8 @@ __export(src_exports, {
|
|
|
49
51
|
hashUtf8: () => hashUtf8,
|
|
50
52
|
importCer: () => importCer,
|
|
51
53
|
sanitizeForAttestation: () => sanitizeForAttestation,
|
|
54
|
+
sanitizeForStamp: () => sanitizeForStamp,
|
|
55
|
+
sanitizeForStorage: () => sanitizeForStorage,
|
|
52
56
|
sealCer: () => sealCer,
|
|
53
57
|
selectNodeKey: () => selectNodeKey,
|
|
54
58
|
sha256Hex: () => sha256Hex,
|
|
@@ -172,7 +176,7 @@ function computeOutputHash(output) {
|
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
// src/snapshot.ts
|
|
175
|
-
var PACKAGE_VERSION = "0.
|
|
179
|
+
var PACKAGE_VERSION = "0.6.0";
|
|
176
180
|
function validateParameters(params) {
|
|
177
181
|
const errors = [];
|
|
178
182
|
if (typeof params.temperature !== "number" || !Number.isFinite(params.temperature)) {
|
|
@@ -523,9 +527,45 @@ function deepRemoveUndefined(value) {
|
|
|
523
527
|
}
|
|
524
528
|
return value;
|
|
525
529
|
}
|
|
530
|
+
function redactPath(obj, path, replacement) {
|
|
531
|
+
const parts = path.split(".");
|
|
532
|
+
const clone = { ...obj };
|
|
533
|
+
let cursor = clone;
|
|
534
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
535
|
+
const key = parts[i];
|
|
536
|
+
if (typeof cursor[key] !== "object" || cursor[key] === null) return clone;
|
|
537
|
+
cursor[key] = { ...cursor[key] };
|
|
538
|
+
cursor = cursor[key];
|
|
539
|
+
}
|
|
540
|
+
const last = parts[parts.length - 1];
|
|
541
|
+
if (last in cursor) cursor[last] = replacement;
|
|
542
|
+
return clone;
|
|
543
|
+
}
|
|
526
544
|
function sanitizeForAttestation(bundle) {
|
|
527
545
|
return deepRemoveUndefined(bundle);
|
|
528
546
|
}
|
|
547
|
+
function sanitizeForStorage(bundle, options) {
|
|
548
|
+
let cleaned = deepRemoveUndefined(bundle);
|
|
549
|
+
if (options?.redactPaths && options.redactPaths.length > 0) {
|
|
550
|
+
const replacement = options.redactWith ?? "[REDACTED]";
|
|
551
|
+
let obj = cleaned;
|
|
552
|
+
for (const path of options.redactPaths) {
|
|
553
|
+
obj = redactPath(obj, path, replacement);
|
|
554
|
+
}
|
|
555
|
+
cleaned = obj;
|
|
556
|
+
}
|
|
557
|
+
return cleaned;
|
|
558
|
+
}
|
|
559
|
+
function sanitizeForStamp(bundle) {
|
|
560
|
+
const core = {
|
|
561
|
+
bundleType: bundle.bundleType,
|
|
562
|
+
certificateHash: bundle.certificateHash,
|
|
563
|
+
createdAt: bundle.createdAt,
|
|
564
|
+
version: bundle.version,
|
|
565
|
+
snapshot: bundle.snapshot
|
|
566
|
+
};
|
|
567
|
+
return deepRemoveUndefined(core);
|
|
568
|
+
}
|
|
529
569
|
function hasAttestation(bundle) {
|
|
530
570
|
if (typeof bundle !== "object" || bundle === null) return false;
|
|
531
571
|
const b = bundle;
|
|
@@ -844,10 +884,10 @@ var M = (a, b = P) => {
|
|
|
844
884
|
return r >= 0n ? r : b + r;
|
|
845
885
|
};
|
|
846
886
|
var modN = (a) => M(a, N);
|
|
847
|
-
var invert = (
|
|
848
|
-
if (
|
|
849
|
-
err("no inverse n=" +
|
|
850
|
-
let a = M(
|
|
887
|
+
var invert = (num2, md) => {
|
|
888
|
+
if (num2 === 0n || md <= 0n)
|
|
889
|
+
err("no inverse n=" + num2 + " mod=" + md);
|
|
890
|
+
let a = M(num2, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
|
|
851
891
|
while (a !== 0n) {
|
|
852
892
|
const q = b / a, r = b % a;
|
|
853
893
|
const m = x - u * q, n = y - v * q;
|
|
@@ -1064,7 +1104,7 @@ var G = new Point(Gx, Gy, 1n, M(Gx * Gy));
|
|
|
1064
1104
|
var I = new Point(0n, 1n, 1n, 0n);
|
|
1065
1105
|
Point.BASE = G;
|
|
1066
1106
|
Point.ZERO = I;
|
|
1067
|
-
var numTo32bLE = (
|
|
1107
|
+
var numTo32bLE = (num2) => hexToBytes(padh(assertRange(num2, 0n, B256), L2)).reverse();
|
|
1068
1108
|
var bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes(b)).reverse()));
|
|
1069
1109
|
var pow2 = (x, power) => {
|
|
1070
1110
|
let r = x;
|
|
@@ -1237,10 +1277,10 @@ function clean(...arrays) {
|
|
|
1237
1277
|
function createView(arr) {
|
|
1238
1278
|
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
1239
1279
|
}
|
|
1240
|
-
function utf8ToBytes(
|
|
1241
|
-
if (typeof
|
|
1280
|
+
function utf8ToBytes(str2) {
|
|
1281
|
+
if (typeof str2 !== "string")
|
|
1242
1282
|
throw new Error("string expected");
|
|
1243
|
-
return new Uint8Array(new TextEncoder().encode(
|
|
1283
|
+
return new Uint8Array(new TextEncoder().encode(str2));
|
|
1244
1284
|
}
|
|
1245
1285
|
function toBytes(data) {
|
|
1246
1286
|
if (typeof data === "string")
|
|
@@ -1846,6 +1886,270 @@ async function verifyBundleAttestation(bundle, options) {
|
|
|
1846
1886
|
if (contextLines.length === 0) return sigResult;
|
|
1847
1887
|
return sigResult.ok ? { ok: true, code: CerVerifyCode.OK, details: contextLines } : { ...sigResult, details: [...contextLines, ...sigResult.details ?? []] };
|
|
1848
1888
|
}
|
|
1889
|
+
|
|
1890
|
+
// src/certifyFromProvider.ts
|
|
1891
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
1892
|
+
|
|
1893
|
+
// src/providerExtract.ts
|
|
1894
|
+
function str(v) {
|
|
1895
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
1896
|
+
}
|
|
1897
|
+
function num(v, fallback) {
|
|
1898
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
1899
|
+
}
|
|
1900
|
+
function numOrNull(v) {
|
|
1901
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
1902
|
+
}
|
|
1903
|
+
function extractChoicesOutput(response) {
|
|
1904
|
+
const choices = response.choices;
|
|
1905
|
+
if (!Array.isArray(choices) || choices.length === 0) return null;
|
|
1906
|
+
const first = choices[0];
|
|
1907
|
+
if (!first || typeof first !== "object") return null;
|
|
1908
|
+
const msg = first.message;
|
|
1909
|
+
if (!msg || typeof msg !== "object") return null;
|
|
1910
|
+
if (typeof msg.content === "string") return msg.content;
|
|
1911
|
+
if (Array.isArray(msg.content)) {
|
|
1912
|
+
const texts = msg.content.filter((p) => typeof p === "object" && p !== null).map((p) => str(p.text)).filter((t) => t !== null);
|
|
1913
|
+
return texts.length > 0 ? texts.join("\n") : null;
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
function extractAnthropicOutput(response) {
|
|
1918
|
+
const content = response.content;
|
|
1919
|
+
if (!Array.isArray(content) || content.length === 0) return null;
|
|
1920
|
+
const first = content[0];
|
|
1921
|
+
if (!first || typeof first !== "object") return null;
|
|
1922
|
+
return str(first.text);
|
|
1923
|
+
}
|
|
1924
|
+
function extractGeminiOutput(response) {
|
|
1925
|
+
const candidates = response.candidates;
|
|
1926
|
+
if (!Array.isArray(candidates) || candidates.length === 0) return null;
|
|
1927
|
+
const first = candidates[0];
|
|
1928
|
+
if (!first || typeof first !== "object") return null;
|
|
1929
|
+
const contentObj = first.content;
|
|
1930
|
+
if (!contentObj || typeof contentObj !== "object") return null;
|
|
1931
|
+
const parts = contentObj.parts;
|
|
1932
|
+
if (!Array.isArray(parts) || parts.length === 0) return null;
|
|
1933
|
+
const part = parts[0];
|
|
1934
|
+
if (!part || typeof part !== "object") return null;
|
|
1935
|
+
return str(part.text);
|
|
1936
|
+
}
|
|
1937
|
+
function extractGenericOutput(response) {
|
|
1938
|
+
return str(response.text) ?? str(response.output_text) ?? (typeof response.output === "string" ? response.output : null) ?? str(response.result) ?? null;
|
|
1939
|
+
}
|
|
1940
|
+
function extractOutput(response) {
|
|
1941
|
+
return extractChoicesOutput(response) ?? extractAnthropicOutput(response) ?? extractGeminiOutput(response) ?? extractGenericOutput(response);
|
|
1942
|
+
}
|
|
1943
|
+
function extractInput(request) {
|
|
1944
|
+
if (Array.isArray(request.messages) && request.messages.length > 0) {
|
|
1945
|
+
return { messages: request.messages };
|
|
1946
|
+
}
|
|
1947
|
+
if (Array.isArray(request.contents) && request.contents.length > 0) {
|
|
1948
|
+
return { contents: request.contents };
|
|
1949
|
+
}
|
|
1950
|
+
if (typeof request.prompt === "string") return request.prompt;
|
|
1951
|
+
if (typeof request.input === "string") return request.input;
|
|
1952
|
+
if (typeof request.input === "object" && request.input !== null) {
|
|
1953
|
+
return request.input;
|
|
1954
|
+
}
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
function derivePrompt(request) {
|
|
1958
|
+
if (typeof request.prompt === "string") return request.prompt;
|
|
1959
|
+
if (Array.isArray(request.messages)) {
|
|
1960
|
+
const msgs = request.messages;
|
|
1961
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1962
|
+
const msg = msgs[i];
|
|
1963
|
+
if (msg.role === "user" || msg.role === "human") {
|
|
1964
|
+
const content = msg.content;
|
|
1965
|
+
if (typeof content === "string") return content;
|
|
1966
|
+
if (Array.isArray(content)) {
|
|
1967
|
+
const text = content.filter((p) => typeof p === "object" && p !== null).map((p) => str(p.text)).filter((t) => t !== null).join("\n");
|
|
1968
|
+
if (text) return text;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
if (Array.isArray(request.contents)) {
|
|
1974
|
+
const contents = request.contents;
|
|
1975
|
+
for (let i = contents.length - 1; i >= 0; i--) {
|
|
1976
|
+
const c = contents[i];
|
|
1977
|
+
if (c.role === "user") {
|
|
1978
|
+
const parts = c.parts;
|
|
1979
|
+
if (Array.isArray(parts)) {
|
|
1980
|
+
const text = parts.map((p) => str(p.text)).filter((t) => t !== null).join("\n");
|
|
1981
|
+
if (text) return text;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
function extractParams(request) {
|
|
1989
|
+
const cfg = typeof request.generationConfig === "object" && request.generationConfig !== null ? request.generationConfig : request;
|
|
1990
|
+
return {
|
|
1991
|
+
temperature: num(cfg.temperature, 1),
|
|
1992
|
+
maxTokens: num(cfg.max_tokens ?? cfg.maxTokens ?? cfg.maxOutputTokens ?? cfg.max_output_tokens, 1024),
|
|
1993
|
+
topP: numOrNull(cfg.top_p ?? cfg.topP),
|
|
1994
|
+
seed: numOrNull(cfg.seed)
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
function extractModel(providerHint, request, response, override) {
|
|
1998
|
+
const raw = override ?? str(request.model) ?? str(request.modelId) ?? // Bedrock
|
|
1999
|
+
str(response.model) ?? str(response.modelId) ?? null;
|
|
2000
|
+
if (!raw) return null;
|
|
2001
|
+
const parts = raw.split("/");
|
|
2002
|
+
const modelName = parts[parts.length - 1];
|
|
2003
|
+
const modelVersion = str(response.model_version ?? response.modelVersion) ?? null;
|
|
2004
|
+
return { model: modelName, modelVersion };
|
|
2005
|
+
}
|
|
2006
|
+
function tryUnwrapBedrock(request) {
|
|
2007
|
+
const modelId = str(request.modelId);
|
|
2008
|
+
if (!modelId) return null;
|
|
2009
|
+
let body = {};
|
|
2010
|
+
if (typeof request.body === "string") {
|
|
2011
|
+
try {
|
|
2012
|
+
body = JSON.parse(request.body);
|
|
2013
|
+
} catch {
|
|
2014
|
+
body = {};
|
|
2015
|
+
}
|
|
2016
|
+
} else if (typeof request.body === "object" && request.body !== null) {
|
|
2017
|
+
body = request.body;
|
|
2018
|
+
}
|
|
2019
|
+
return { req: { ...body, modelId }, modelId };
|
|
2020
|
+
}
|
|
2021
|
+
function extractFromProviderCall(provider, modelOverride, rawRequest, rawResponse) {
|
|
2022
|
+
let request = rawRequest;
|
|
2023
|
+
if (provider === "bedrock" || str(rawRequest.modelId)) {
|
|
2024
|
+
const unwrapped = tryUnwrapBedrock(rawRequest);
|
|
2025
|
+
if (unwrapped) request = unwrapped.req;
|
|
2026
|
+
}
|
|
2027
|
+
const modelResult = extractModel(provider, request, rawResponse, modelOverride);
|
|
2028
|
+
if (!modelResult) {
|
|
2029
|
+
return {
|
|
2030
|
+
ok: false,
|
|
2031
|
+
reason: "Could not determine model name. Provide it via the `model` field, or ensure request.model / request.modelId is present."
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
const input = extractInput(request);
|
|
2035
|
+
if (input === null) {
|
|
2036
|
+
return {
|
|
2037
|
+
ok: false,
|
|
2038
|
+
reason: "Could not extract input. Expected request.messages (OpenAI/Anthropic/Mistral), request.contents (Gemini), or request.prompt/request.input (generic)."
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
const prompt = derivePrompt(request) ?? (typeof input === "string" ? input : "[structured input]");
|
|
2042
|
+
const output = extractOutput(rawResponse);
|
|
2043
|
+
if (output === null) {
|
|
2044
|
+
return {
|
|
2045
|
+
ok: false,
|
|
2046
|
+
reason: "Could not extract output text. Expected response.choices[0].message.content (OpenAI/Mistral), response.content[0].text (Anthropic), response.candidates[0].content.parts[0].text (Gemini), or response.text / response.output_text (generic)."
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
const parameters = extractParams(request);
|
|
2050
|
+
return {
|
|
2051
|
+
ok: true,
|
|
2052
|
+
data: {
|
|
2053
|
+
model: modelResult.model,
|
|
2054
|
+
modelVersion: modelResult.modelVersion,
|
|
2055
|
+
prompt,
|
|
2056
|
+
input,
|
|
2057
|
+
output,
|
|
2058
|
+
parameters
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
// src/certifyFromProvider.ts
|
|
2064
|
+
function certifyDecisionFromProviderCall(params) {
|
|
2065
|
+
const extracted = extractFromProviderCall(
|
|
2066
|
+
params.provider,
|
|
2067
|
+
params.model,
|
|
2068
|
+
params.request,
|
|
2069
|
+
params.response
|
|
2070
|
+
);
|
|
2071
|
+
if (!extracted.ok) {
|
|
2072
|
+
return { ok: false, code: CerVerifyCode.SCHEMA_ERROR, reason: extracted.reason };
|
|
2073
|
+
}
|
|
2074
|
+
const { model, modelVersion, prompt, input, output, parameters } = extracted.data;
|
|
2075
|
+
try {
|
|
2076
|
+
const snapshot = createSnapshot({
|
|
2077
|
+
executionId: params.executionId ?? crypto5.randomUUID(),
|
|
2078
|
+
timestamp: params.timestamp,
|
|
2079
|
+
provider: params.provider,
|
|
2080
|
+
model,
|
|
2081
|
+
modelVersion,
|
|
2082
|
+
prompt,
|
|
2083
|
+
input,
|
|
2084
|
+
parameters,
|
|
2085
|
+
output,
|
|
2086
|
+
appId: params.appId ?? null,
|
|
2087
|
+
workflowId: params.workflowId ?? null,
|
|
2088
|
+
conversationId: params.conversationId ?? null
|
|
2089
|
+
});
|
|
2090
|
+
const bundle = sealCer(snapshot, { meta: params.meta, createdAt: params.createdAt });
|
|
2091
|
+
return { ok: true, bundle };
|
|
2092
|
+
} catch (err2) {
|
|
2093
|
+
return {
|
|
2094
|
+
ok: false,
|
|
2095
|
+
code: CerVerifyCode.SCHEMA_ERROR,
|
|
2096
|
+
reason: err2 instanceof Error ? err2.message : String(err2)
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
// src/client.ts
|
|
2102
|
+
async function resolveApiKey(key) {
|
|
2103
|
+
if (typeof key === "function") return key();
|
|
2104
|
+
return key ?? "";
|
|
2105
|
+
}
|
|
2106
|
+
function mergeDefaultMeta(defaults, meta) {
|
|
2107
|
+
const base = {};
|
|
2108
|
+
if (defaults.tags && defaults.tags.length > 0) base.tags = defaults.tags;
|
|
2109
|
+
if (defaults.source) base.source = defaults.source;
|
|
2110
|
+
if (!meta && Object.keys(base).length === 0) return void 0;
|
|
2111
|
+
return { ...base, ...meta };
|
|
2112
|
+
}
|
|
2113
|
+
function createClient(defaults = {}) {
|
|
2114
|
+
return {
|
|
2115
|
+
certifyDecision(params) {
|
|
2116
|
+
return certifyDecision({
|
|
2117
|
+
appId: defaults.appId ?? null,
|
|
2118
|
+
workflowId: defaults.workflowId ?? null,
|
|
2119
|
+
...params,
|
|
2120
|
+
meta: mergeDefaultMeta(defaults, params.meta)
|
|
2121
|
+
});
|
|
2122
|
+
},
|
|
2123
|
+
async certifyAndAttestDecision(params, options) {
|
|
2124
|
+
const nodeUrl = options?.nodeUrl ?? defaults.nodeUrl;
|
|
2125
|
+
if (!nodeUrl) {
|
|
2126
|
+
throw new Error("certifyAndAttestDecision requires nodeUrl (set in defaults or options)");
|
|
2127
|
+
}
|
|
2128
|
+
const apiKey = options?.apiKey ?? await resolveApiKey(defaults.apiKey);
|
|
2129
|
+
const mergedParams = {
|
|
2130
|
+
appId: defaults.appId ?? null,
|
|
2131
|
+
workflowId: defaults.workflowId ?? null,
|
|
2132
|
+
...params,
|
|
2133
|
+
meta: mergeDefaultMeta(defaults, params.meta)
|
|
2134
|
+
};
|
|
2135
|
+
return certifyAndAttestDecision(mergedParams, {
|
|
2136
|
+
nodeUrl,
|
|
2137
|
+
apiKey,
|
|
2138
|
+
timeoutMs: options?.timeoutMs
|
|
2139
|
+
});
|
|
2140
|
+
},
|
|
2141
|
+
verify(bundle) {
|
|
2142
|
+
return verifyCer(bundle);
|
|
2143
|
+
},
|
|
2144
|
+
async verifyBundleAttestation(bundle, options) {
|
|
2145
|
+
const nodeUrl = options?.nodeUrl ?? defaults.nodeUrl;
|
|
2146
|
+
if (!nodeUrl) {
|
|
2147
|
+
throw new Error("verifyBundleAttestation requires nodeUrl (set in defaults or options)");
|
|
2148
|
+
}
|
|
2149
|
+
return verifyBundleAttestation(bundle, { nodeUrl, kid: options?.kid });
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
1849
2153
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1850
2154
|
0 && (module.exports = {
|
|
1851
2155
|
CerAttestationError,
|
|
@@ -1856,8 +2160,10 @@ async function verifyBundleAttestation(bundle, options) {
|
|
|
1856
2160
|
attestIfNeeded,
|
|
1857
2161
|
certifyAndAttestDecision,
|
|
1858
2162
|
certifyDecision,
|
|
2163
|
+
certifyDecisionFromProviderCall,
|
|
1859
2164
|
computeInputHash,
|
|
1860
2165
|
computeOutputHash,
|
|
2166
|
+
createClient,
|
|
1861
2167
|
createSnapshot,
|
|
1862
2168
|
exportCer,
|
|
1863
2169
|
fetchNodeKeys,
|
|
@@ -1867,6 +2173,8 @@ async function verifyBundleAttestation(bundle, options) {
|
|
|
1867
2173
|
hashUtf8,
|
|
1868
2174
|
importCer,
|
|
1869
2175
|
sanitizeForAttestation,
|
|
2176
|
+
sanitizeForStamp,
|
|
2177
|
+
sanitizeForStorage,
|
|
1870
2178
|
sealCer,
|
|
1871
2179
|
selectNodeKey,
|
|
1872
2180
|
sha256Hex,
|