@nexart/ai-execution 0.8.0 → 0.10.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 +167 -3
- package/dist/index.cjs +182 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -3
- package/dist/index.d.ts +68 -3
- package/dist/index.mjs +178 -1
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +480 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +185 -0
- package/dist/langchain.d.ts +185 -0
- package/dist/langchain.mjs +444 -0
- package/dist/langchain.mjs.map +1 -0
- package/dist/providers/anthropic.d.cts +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/openai.d.cts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/wrap.d.cts +1 -1
- package/dist/providers/wrap.d.ts +1 -1
- package/dist/{types-CcqCDPrD.d.cts → types-C5t12OK8.d.cts} +2 -0
- package/dist/{types-CcqCDPrD.d.ts → types-C5t12OK8.d.ts} +2 -0
- package/package.json +7 -2
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
// src/langchain.ts
|
|
2
|
+
import * as crypto3 from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/certify.ts
|
|
5
|
+
import * as crypto2 from "crypto";
|
|
6
|
+
|
|
7
|
+
// src/hash.ts
|
|
8
|
+
import * as crypto from "crypto";
|
|
9
|
+
|
|
10
|
+
// src/canonicalJson.ts
|
|
11
|
+
function toCanonicalJson(value) {
|
|
12
|
+
return canonicalize(value);
|
|
13
|
+
}
|
|
14
|
+
function canonicalize(value) {
|
|
15
|
+
if (value === null) {
|
|
16
|
+
return "null";
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === "boolean") {
|
|
19
|
+
return value ? "true" : "false";
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === "number") {
|
|
22
|
+
if (!Number.isFinite(value)) {
|
|
23
|
+
throw new Error(`Non-finite number not allowed in canonical JSON: ${value}`);
|
|
24
|
+
}
|
|
25
|
+
return JSON.stringify(value);
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === "string") {
|
|
28
|
+
return JSON.stringify(value);
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
const items = value.map((item) => canonicalize(item));
|
|
32
|
+
return "[" + items.join(",") + "]";
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === "object") {
|
|
35
|
+
const obj = value;
|
|
36
|
+
const keys = Object.keys(obj).sort();
|
|
37
|
+
const entries = keys.map((key) => {
|
|
38
|
+
const val = obj[key];
|
|
39
|
+
if (val === void 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return JSON.stringify(key) + ":" + canonicalize(val);
|
|
43
|
+
}).filter((e) => e !== null);
|
|
44
|
+
return "{" + entries.join(",") + "}";
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Unsupported type for canonical JSON: ${typeof value}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/hash.ts
|
|
50
|
+
function sha256Hex(data) {
|
|
51
|
+
const hash = crypto.createHash("sha256");
|
|
52
|
+
if (typeof data === "string") {
|
|
53
|
+
hash.update(data, "utf-8");
|
|
54
|
+
} else {
|
|
55
|
+
hash.update(data);
|
|
56
|
+
}
|
|
57
|
+
return hash.digest("hex");
|
|
58
|
+
}
|
|
59
|
+
function hashUtf8(value) {
|
|
60
|
+
return `sha256:${sha256Hex(value)}`;
|
|
61
|
+
}
|
|
62
|
+
function hashCanonicalJson(value) {
|
|
63
|
+
const canonical = toCanonicalJson(value);
|
|
64
|
+
return `sha256:${sha256Hex(canonical)}`;
|
|
65
|
+
}
|
|
66
|
+
function computeInputHash(input) {
|
|
67
|
+
if (typeof input === "string") {
|
|
68
|
+
return hashUtf8(input);
|
|
69
|
+
}
|
|
70
|
+
return hashCanonicalJson(input);
|
|
71
|
+
}
|
|
72
|
+
function computeOutputHash(output) {
|
|
73
|
+
if (typeof output === "string") {
|
|
74
|
+
return hashUtf8(output);
|
|
75
|
+
}
|
|
76
|
+
return hashCanonicalJson(output);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/snapshot.ts
|
|
80
|
+
var PACKAGE_VERSION = "0.8.0";
|
|
81
|
+
function validateParameters(params) {
|
|
82
|
+
const errors = [];
|
|
83
|
+
if (typeof params.temperature !== "number" || !Number.isFinite(params.temperature)) {
|
|
84
|
+
errors.push(`parameters.temperature must be a finite number, got: ${params.temperature}`);
|
|
85
|
+
}
|
|
86
|
+
if (typeof params.maxTokens !== "number" || !Number.isFinite(params.maxTokens)) {
|
|
87
|
+
errors.push(`parameters.maxTokens must be a finite number, got: ${params.maxTokens}`);
|
|
88
|
+
}
|
|
89
|
+
if (params.topP !== null && (typeof params.topP !== "number" || !Number.isFinite(params.topP))) {
|
|
90
|
+
errors.push(`parameters.topP must be a finite number or null, got: ${params.topP}`);
|
|
91
|
+
}
|
|
92
|
+
if (params.seed !== null && (typeof params.seed !== "number" || !Number.isFinite(params.seed))) {
|
|
93
|
+
errors.push(`parameters.seed must be a finite number or null, got: ${params.seed}`);
|
|
94
|
+
}
|
|
95
|
+
return errors;
|
|
96
|
+
}
|
|
97
|
+
function createSnapshot(params) {
|
|
98
|
+
const paramErrors = validateParameters(params.parameters);
|
|
99
|
+
if (paramErrors.length > 0) {
|
|
100
|
+
throw new Error(`Invalid parameters: ${paramErrors.join("; ")}`);
|
|
101
|
+
}
|
|
102
|
+
const inputHash = computeInputHash(params.input);
|
|
103
|
+
const outputHash = computeOutputHash(params.output);
|
|
104
|
+
const snapshot = {
|
|
105
|
+
type: "ai.execution.v1",
|
|
106
|
+
protocolVersion: "1.2.0",
|
|
107
|
+
executionSurface: "ai",
|
|
108
|
+
executionId: params.executionId,
|
|
109
|
+
timestamp: params.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
110
|
+
provider: params.provider,
|
|
111
|
+
model: params.model,
|
|
112
|
+
modelVersion: params.modelVersion ?? null,
|
|
113
|
+
prompt: params.prompt,
|
|
114
|
+
input: params.input,
|
|
115
|
+
inputHash,
|
|
116
|
+
parameters: {
|
|
117
|
+
temperature: params.parameters.temperature,
|
|
118
|
+
maxTokens: params.parameters.maxTokens,
|
|
119
|
+
topP: params.parameters.topP ?? null,
|
|
120
|
+
seed: params.parameters.seed ?? null
|
|
121
|
+
},
|
|
122
|
+
output: params.output,
|
|
123
|
+
outputHash,
|
|
124
|
+
sdkVersion: params.sdkVersion ?? PACKAGE_VERSION,
|
|
125
|
+
appId: params.appId ?? null
|
|
126
|
+
};
|
|
127
|
+
if (params.runId !== void 0) snapshot.runId = params.runId ?? null;
|
|
128
|
+
if (params.stepId !== void 0) snapshot.stepId = params.stepId ?? null;
|
|
129
|
+
if (params.stepIndex !== void 0) snapshot.stepIndex = params.stepIndex ?? null;
|
|
130
|
+
if (params.workflowId !== void 0) snapshot.workflowId = params.workflowId ?? null;
|
|
131
|
+
if (params.conversationId !== void 0) snapshot.conversationId = params.conversationId ?? null;
|
|
132
|
+
if (params.prevStepHash !== void 0) snapshot.prevStepHash = params.prevStepHash ?? null;
|
|
133
|
+
if (params.toolCalls !== void 0 && params.toolCalls.length > 0) {
|
|
134
|
+
snapshot.toolCalls = params.toolCalls;
|
|
135
|
+
}
|
|
136
|
+
return snapshot;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/cer.ts
|
|
140
|
+
function computeCertificateHash(payload) {
|
|
141
|
+
const canonical = toCanonicalJson(payload);
|
|
142
|
+
return `sha256:${sha256Hex(canonical)}`;
|
|
143
|
+
}
|
|
144
|
+
function sealCer(snapshot, options) {
|
|
145
|
+
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
146
|
+
const payload = {
|
|
147
|
+
bundleType: "cer.ai.execution.v1",
|
|
148
|
+
createdAt,
|
|
149
|
+
snapshot,
|
|
150
|
+
version: "0.1"
|
|
151
|
+
};
|
|
152
|
+
const certificateHash = computeCertificateHash(payload);
|
|
153
|
+
const bundle = {
|
|
154
|
+
bundleType: "cer.ai.execution.v1",
|
|
155
|
+
certificateHash,
|
|
156
|
+
createdAt,
|
|
157
|
+
version: "0.1",
|
|
158
|
+
snapshot
|
|
159
|
+
};
|
|
160
|
+
if (options?.meta) {
|
|
161
|
+
bundle.meta = options.meta;
|
|
162
|
+
}
|
|
163
|
+
if (options?.declaration) {
|
|
164
|
+
bundle.declaration = options.declaration;
|
|
165
|
+
}
|
|
166
|
+
return bundle;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/certify.ts
|
|
170
|
+
function certifyDecision(params) {
|
|
171
|
+
const executionId = params.executionId ?? crypto2.randomUUID();
|
|
172
|
+
const snapshot = createSnapshot({
|
|
173
|
+
executionId,
|
|
174
|
+
timestamp: params.timestamp,
|
|
175
|
+
provider: params.provider,
|
|
176
|
+
model: params.model,
|
|
177
|
+
modelVersion: params.modelVersion,
|
|
178
|
+
prompt: params.prompt,
|
|
179
|
+
input: params.input,
|
|
180
|
+
parameters: params.parameters,
|
|
181
|
+
output: params.output,
|
|
182
|
+
sdkVersion: params.sdkVersion,
|
|
183
|
+
appId: params.appId,
|
|
184
|
+
runId: params.runId,
|
|
185
|
+
stepId: params.stepId,
|
|
186
|
+
stepIndex: params.stepIndex,
|
|
187
|
+
workflowId: params.workflowId,
|
|
188
|
+
conversationId: params.conversationId,
|
|
189
|
+
prevStepHash: params.prevStepHash
|
|
190
|
+
});
|
|
191
|
+
return sealCer(snapshot, { createdAt: params.createdAt, meta: params.meta });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/errors.ts
|
|
195
|
+
var CerAttestationError = class extends Error {
|
|
196
|
+
statusCode;
|
|
197
|
+
responseBody;
|
|
198
|
+
details;
|
|
199
|
+
constructor(message, statusCode, responseBody, details) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.name = "CerAttestationError";
|
|
202
|
+
this.statusCode = statusCode;
|
|
203
|
+
this.responseBody = responseBody;
|
|
204
|
+
this.details = details;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/sanitize.ts
|
|
209
|
+
function deepRemoveUndefined(value) {
|
|
210
|
+
if (value === null || value === void 0) return value;
|
|
211
|
+
if (typeof value === "bigint") {
|
|
212
|
+
throw new Error("BigInt values are not JSON-safe and cannot be sanitized");
|
|
213
|
+
}
|
|
214
|
+
if (typeof value === "function") {
|
|
215
|
+
throw new Error("Function values are not JSON-safe and cannot be sanitized");
|
|
216
|
+
}
|
|
217
|
+
if (typeof value === "symbol") {
|
|
218
|
+
throw new Error("Symbol values are not JSON-safe and cannot be sanitized");
|
|
219
|
+
}
|
|
220
|
+
if (Array.isArray(value)) {
|
|
221
|
+
return value.map(deepRemoveUndefined);
|
|
222
|
+
}
|
|
223
|
+
if (typeof value === "object") {
|
|
224
|
+
const result = {};
|
|
225
|
+
for (const [key, val] of Object.entries(value)) {
|
|
226
|
+
if (val === void 0) continue;
|
|
227
|
+
result[key] = deepRemoveUndefined(val);
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
function sanitizeForAttestation(bundle) {
|
|
234
|
+
return deepRemoveUndefined(bundle);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/attest.ts
|
|
238
|
+
var SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
239
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
240
|
+
function validateHashFormat(value, fieldName) {
|
|
241
|
+
if (typeof value !== "string") return null;
|
|
242
|
+
if (!SHA256_PATTERN.test(value)) {
|
|
243
|
+
return `${fieldName} is not in sha256:<64hex> format: "${value}"`;
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
async function attest(bundle, options) {
|
|
248
|
+
const url = `${options.nodeUrl.replace(/\/+$/, "")}/api/attest`;
|
|
249
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
250
|
+
const sanitized = sanitizeForAttestation(bundle);
|
|
251
|
+
const controller = new AbortController();
|
|
252
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
253
|
+
let response;
|
|
254
|
+
try {
|
|
255
|
+
response = await fetch(url, {
|
|
256
|
+
method: "POST",
|
|
257
|
+
headers: {
|
|
258
|
+
"Content-Type": "application/json",
|
|
259
|
+
"Authorization": `Bearer ${options.apiKey}`
|
|
260
|
+
},
|
|
261
|
+
body: JSON.stringify(sanitized),
|
|
262
|
+
signal: controller.signal
|
|
263
|
+
});
|
|
264
|
+
} catch (err) {
|
|
265
|
+
clearTimeout(timer);
|
|
266
|
+
const error = err;
|
|
267
|
+
if (error.name === "AbortError") {
|
|
268
|
+
throw new CerAttestationError(
|
|
269
|
+
`Attestation request timed out after ${timeoutMs}ms`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
throw new CerAttestationError(
|
|
273
|
+
`Network error contacting attestation node: ${error.message}`
|
|
274
|
+
);
|
|
275
|
+
} finally {
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
}
|
|
278
|
+
let body;
|
|
279
|
+
try {
|
|
280
|
+
body = await response.json();
|
|
281
|
+
} catch {
|
|
282
|
+
const text = await response.text().catch(() => "");
|
|
283
|
+
throw new CerAttestationError(
|
|
284
|
+
`Attestation node returned non-JSON response (${response.status}): ${text}`,
|
|
285
|
+
response.status
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (!response.ok) {
|
|
289
|
+
const result2 = body;
|
|
290
|
+
const msg = typeof result2.error === "string" ? result2.error : `HTTP ${response.status}`;
|
|
291
|
+
const details = Array.isArray(result2.details) ? result2.details : void 0;
|
|
292
|
+
throw new CerAttestationError(
|
|
293
|
+
`Attestation failed: ${msg}`,
|
|
294
|
+
response.status,
|
|
295
|
+
body,
|
|
296
|
+
details
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const result = body;
|
|
300
|
+
const errors = [];
|
|
301
|
+
if (typeof result.certificateHash === "string" && result.certificateHash !== bundle.certificateHash) {
|
|
302
|
+
errors.push(
|
|
303
|
+
`Node returned certificateHash "${result.certificateHash}" but bundle has "${bundle.certificateHash}"`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const certHashErr = validateHashFormat(result.certificateHash, "response.certificateHash");
|
|
307
|
+
if (certHashErr) errors.push(certHashErr);
|
|
308
|
+
const runtimeHashErr = validateHashFormat(result.nodeRuntimeHash, "response.nodeRuntimeHash");
|
|
309
|
+
if (runtimeHashErr) errors.push(runtimeHashErr);
|
|
310
|
+
if (errors.length > 0) {
|
|
311
|
+
throw new CerAttestationError(
|
|
312
|
+
`Attestation response validation failed: ${errors.join("; ")}`,
|
|
313
|
+
response.status,
|
|
314
|
+
body,
|
|
315
|
+
errors
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
ok: true,
|
|
320
|
+
attestationId: typeof result.attestationId === "string" ? result.attestationId : void 0,
|
|
321
|
+
nodeRuntimeHash: typeof result.nodeRuntimeHash === "string" ? result.nodeRuntimeHash : void 0,
|
|
322
|
+
certificateHash: typeof result.certificateHash === "string" ? result.certificateHash : void 0,
|
|
323
|
+
protocolVersion: typeof result.protocolVersion === "string" ? result.protocolVersion : void 0,
|
|
324
|
+
raw: body
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/wrappers.ts
|
|
329
|
+
function receiptFromProof(proof, bundle) {
|
|
330
|
+
if (!proof.attestationId || !proof.nodeRuntimeHash || !proof.protocolVersion) {
|
|
331
|
+
throw new CerAttestationError(
|
|
332
|
+
"Attestation proof is missing required fields (attestationId, nodeRuntimeHash, protocolVersion)"
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
attestationId: proof.attestationId,
|
|
337
|
+
certificateHash: proof.certificateHash ?? bundle.certificateHash,
|
|
338
|
+
nodeRuntimeHash: proof.nodeRuntimeHash,
|
|
339
|
+
protocolVersion: proof.protocolVersion
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
async function certifyAndAttestDecision(params, options) {
|
|
343
|
+
const bundle = certifyDecision(params);
|
|
344
|
+
const proof = await attest(bundle, options);
|
|
345
|
+
const receipt = receiptFromProof(proof, bundle);
|
|
346
|
+
return { bundle, receipt };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/langchain.ts
|
|
350
|
+
function normalizeCerValue(value) {
|
|
351
|
+
if (typeof value === "string") return value;
|
|
352
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
353
|
+
return value;
|
|
354
|
+
}
|
|
355
|
+
if (Array.isArray(value)) return { items: value };
|
|
356
|
+
return String(value);
|
|
357
|
+
}
|
|
358
|
+
function extractPrompt(input, metadata) {
|
|
359
|
+
if (typeof metadata?.prompt === "string" && metadata.prompt.length > 0) {
|
|
360
|
+
return metadata.prompt;
|
|
361
|
+
}
|
|
362
|
+
if (input !== null && typeof input === "object" && !Array.isArray(input)) {
|
|
363
|
+
const obj = input;
|
|
364
|
+
if (Array.isArray(obj.messages) && obj.messages.length > 0) {
|
|
365
|
+
const first = obj.messages[0];
|
|
366
|
+
if (typeof first?.content === "string" && first.content.length > 0) {
|
|
367
|
+
return first.content;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (typeof obj.prompt === "string" && obj.prompt.length > 0) return obj.prompt;
|
|
371
|
+
if (typeof obj.text === "string" && obj.text.length > 0) return obj.text;
|
|
372
|
+
}
|
|
373
|
+
if (typeof input === "string" && input.length > 0) return input;
|
|
374
|
+
return "[LangChain run]";
|
|
375
|
+
}
|
|
376
|
+
function buildMeta(metadata) {
|
|
377
|
+
if (!metadata || Object.keys(metadata).length === 0) return void 0;
|
|
378
|
+
const reserved = /* @__PURE__ */ new Set(["prompt", "appId", "runId", "workflowId", "conversationId"]);
|
|
379
|
+
const extraTags = [];
|
|
380
|
+
for (const [k, v] of Object.entries(metadata)) {
|
|
381
|
+
if (!reserved.has(k)) {
|
|
382
|
+
extraTags.push(typeof v === "string" ? `${k}:${v}` : `${k}:${JSON.stringify(v)}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return extraTags.length > 0 ? { tags: extraTags } : void 0;
|
|
386
|
+
}
|
|
387
|
+
function resolveParameters(params) {
|
|
388
|
+
return {
|
|
389
|
+
temperature: params?.temperature ?? 0,
|
|
390
|
+
maxTokens: params?.maxTokens ?? 0,
|
|
391
|
+
topP: params?.topP ?? null,
|
|
392
|
+
seed: params?.seed ?? null
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function buildCertifyParams(input, executionId) {
|
|
396
|
+
return {
|
|
397
|
+
executionId,
|
|
398
|
+
timestamp: input.timestamp,
|
|
399
|
+
createdAt: input.createdAt,
|
|
400
|
+
provider: input.provider,
|
|
401
|
+
model: input.model,
|
|
402
|
+
modelVersion: input.modelVersion ?? null,
|
|
403
|
+
prompt: extractPrompt(input.input, input.metadata),
|
|
404
|
+
input: normalizeCerValue(input.input),
|
|
405
|
+
output: normalizeCerValue(input.output),
|
|
406
|
+
parameters: resolveParameters(input.parameters),
|
|
407
|
+
appId: typeof input.metadata?.appId === "string" ? input.metadata.appId : null,
|
|
408
|
+
runId: typeof input.metadata?.runId === "string" ? input.metadata.runId : void 0,
|
|
409
|
+
workflowId: typeof input.metadata?.workflowId === "string" ? input.metadata.workflowId : void 0,
|
|
410
|
+
conversationId: typeof input.metadata?.conversationId === "string" ? input.metadata.conversationId : void 0,
|
|
411
|
+
meta: buildMeta(input.metadata)
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function createLangChainCer(input) {
|
|
415
|
+
const executionId = input.executionId ?? crypto3.randomUUID();
|
|
416
|
+
const bundle = certifyDecision(buildCertifyParams(input, executionId));
|
|
417
|
+
return {
|
|
418
|
+
bundle,
|
|
419
|
+
certificateHash: bundle.certificateHash,
|
|
420
|
+
executionId: bundle.snapshot.executionId
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function certifyLangChainRun(input, _options) {
|
|
424
|
+
if (input.nodeUrl && input.apiKey) {
|
|
425
|
+
const executionId = input.executionId ?? crypto3.randomUUID();
|
|
426
|
+
const certifyParams = buildCertifyParams({ ...input, executionId }, executionId);
|
|
427
|
+
const attestFn = _options?._attestFn ?? certifyAndAttestDecision;
|
|
428
|
+
return attestFn(certifyParams, { nodeUrl: input.nodeUrl, apiKey: input.apiKey }).then(
|
|
429
|
+
({ bundle, receipt }) => ({
|
|
430
|
+
bundle,
|
|
431
|
+
receipt,
|
|
432
|
+
certificateHash: bundle.certificateHash,
|
|
433
|
+
executionId: bundle.snapshot.executionId,
|
|
434
|
+
attested: true
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
return createLangChainCer(input);
|
|
439
|
+
}
|
|
440
|
+
export {
|
|
441
|
+
certifyLangChainRun,
|
|
442
|
+
createLangChainCer
|
|
443
|
+
};
|
|
444
|
+
//# sourceMappingURL=langchain.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/langchain.ts","../src/certify.ts","../src/hash.ts","../src/canonicalJson.ts","../src/snapshot.ts","../src/cer.ts","../src/errors.ts","../src/sanitize.ts","../src/attest.ts","../src/wrappers.ts"],"sourcesContent":["/**\n * @nexart/ai-execution — LangChain Integration\n *\n * Provides a minimal helper surface for certifying the final input and\n * final output of a LangChain run as a Certified Execution Record (CER).\n *\n * Two helpers:\n *\n * createLangChainCer(input)\n * Always synchronous. Local CER creation only — no network required.\n * Returns LangChainCerResult.\n *\n * certifyLangChainRun(input)\n * • Without nodeUrl/apiKey → identical to createLangChainCer (sync).\n * • With nodeUrl + apiKey → sends the sealed bundle to a NexArt node\n * for attestation via the existing certifyAndAttestDecision() path.\n * Returns Promise<LangChainAttestedResult>.\n *\n * Both helpers produce bundles that are fully protocol-aligned and pass\n * verifyCer() / verifyAiCerBundleDetailed(). No LangChain-specific schema.\n */\n\nimport * as crypto from 'crypto';\nimport type {\n CerAiExecutionBundle,\n AiExecutionParameters,\n AttestOptions,\n AttestationReceipt,\n CertifyDecisionParams,\n} from './types.js';\nimport { certifyDecision } from './certify.js';\nimport { certifyAndAttestDecision } from './wrappers.js';\n\n// ─── Injectable attest type (for testing) ────────────────────────────────────\n\n/**\n * Injectable attestation function type.\n * Defaults to the real certifyAndAttestDecision(); override in tests to avoid\n * hitting the network. Follows the same injectable pattern as\n * certifyAndAttestRun()'s attestStep option.\n *\n * @internal\n */\nexport type AttestDecisionFn = (\n params: CertifyDecisionParams,\n options: AttestOptions,\n) => Promise<{ bundle: CerAiExecutionBundle; receipt: AttestationReceipt }>;\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/**\n * Normalized input for LangChain CER helpers.\n *\n * Accepts the practical final-run shape produced by LangChain chains,\n * agents, and runnables without requiring any LangChain import.\n */\nexport interface LangChainCertificationInput {\n /** Optional run ID — a UUID is generated if omitted. */\n executionId?: string;\n /** AI provider name (e.g. \"openai\", \"anthropic\", \"google\"). */\n provider: string;\n /** Model name (e.g. \"gpt-4o-mini\", \"claude-3-haiku-20240307\"). */\n model: string;\n /** Model version string — null if unknown. */\n modelVersion?: string | null;\n /**\n * Final run input. Accepts any shape produced by LangChain:\n * { messages: [...] }, a plain string, HumanMessage array, etc.\n * Arrays are wrapped as { items: [...] }.\n */\n input: unknown;\n /**\n * Final run output. Accepts any shape produced by LangChain:\n * AIMessage, { text: \"...\" }, a plain string, etc.\n * Arrays are wrapped as { items: [...] }.\n */\n output: unknown;\n /**\n * Optional metadata from the LangChain run. Recognized keys:\n * - prompt?: string — used as the CER prompt field\n * - appId?: string — included in the CER snapshot\n * - runId?: string\n * - workflowId?: string\n * - conversationId?: string\n * All other keys are preserved in the CER bundle's meta.tags.\n */\n metadata?: Record<string, unknown>;\n /**\n * Optional override for the AI inference timestamp.\n * Defaults to current time if omitted.\n */\n timestamp?: string;\n /**\n * Optional override for the CER bundle creation timestamp.\n * Pin this to a fixed value to get a deterministic certificateHash.\n * Defaults to current time if omitted.\n */\n createdAt?: string;\n /**\n * Optional model inference parameters.\n * Fields not provided default to 0 / null (protocol-safe).\n * These values are included in the certificateHash.\n */\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n /**\n * NexArt node URL for attestation (e.g. \"https://node.nexart.io\").\n * When set together with apiKey, certifyLangChainRun() will call the\n * node to produce an AttestationReceipt alongside the local CER.\n * Has no effect on createLangChainCer().\n */\n nodeUrl?: string;\n /**\n * NexArt API key for node attestation.\n * Required when nodeUrl is set.\n */\n apiKey?: string;\n}\n\n/** Result returned by createLangChainCer() and certifyLangChainRun() in local mode. */\nexport interface LangChainCerResult {\n /** The sealed CER bundle. Pass to verifyCer() to verify integrity. */\n bundle: CerAiExecutionBundle;\n /** SHA-256 certificate hash — the tamper-evident fingerprint of this record. */\n certificateHash: string;\n /** The executionId embedded in the CER snapshot. */\n executionId: string;\n}\n\n/**\n * Result returned by certifyLangChainRun() when nodeUrl + apiKey are provided.\n * Extends LangChainCerResult with the receipt from the attestation node.\n */\nexport interface LangChainAttestedResult extends LangChainCerResult {\n /** The attestation receipt from the NexArt node. */\n receipt: AttestationReceipt;\n /** Always true — distinguishes an attested result from a local result. */\n attested: true;\n}\n\n// ─── Internal normalization ───────────────────────────────────────────────────\n\nfunction normalizeCerValue(value: unknown): string | Record<string, unknown> {\n if (typeof value === 'string') return value;\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (Array.isArray(value)) return { items: value };\n return String(value);\n}\n\nfunction extractPrompt(input: unknown, metadata?: Record<string, unknown>): string {\n if (typeof metadata?.prompt === 'string' && metadata.prompt.length > 0) {\n return metadata.prompt;\n }\n if (input !== null && typeof input === 'object' && !Array.isArray(input)) {\n const obj = input as Record<string, unknown>;\n if (Array.isArray(obj.messages) && obj.messages.length > 0) {\n const first = obj.messages[0] as Record<string, unknown> | undefined;\n if (typeof first?.content === 'string' && first.content.length > 0) {\n return first.content;\n }\n }\n if (typeof obj.prompt === 'string' && obj.prompt.length > 0) return obj.prompt;\n if (typeof obj.text === 'string' && obj.text.length > 0) return obj.text;\n }\n if (typeof input === 'string' && input.length > 0) return input;\n return '[LangChain run]';\n}\n\nfunction buildMeta(metadata?: Record<string, unknown>): Record<string, unknown> | undefined {\n if (!metadata || Object.keys(metadata).length === 0) return undefined;\n const reserved = new Set(['prompt', 'appId', 'runId', 'workflowId', 'conversationId']);\n const extraTags: string[] = [];\n for (const [k, v] of Object.entries(metadata)) {\n if (!reserved.has(k)) {\n extraTags.push(typeof v === 'string' ? `${k}:${v}` : `${k}:${JSON.stringify(v)}`);\n }\n }\n return extraTags.length > 0 ? { tags: extraTags } : undefined;\n}\n\nfunction resolveParameters(\n params?: LangChainCertificationInput['parameters'],\n): AiExecutionParameters {\n return {\n temperature: params?.temperature ?? 0,\n maxTokens: params?.maxTokens ?? 0,\n topP: params?.topP ?? null,\n seed: params?.seed ?? null,\n };\n}\n\n/** Build the CertifyDecisionParams from a LangChainCertificationInput. */\nfunction buildCertifyParams(\n input: LangChainCertificationInput,\n executionId: string,\n): CertifyDecisionParams {\n return {\n executionId,\n timestamp: input.timestamp,\n createdAt: input.createdAt,\n provider: input.provider,\n model: input.model,\n modelVersion: input.modelVersion ?? null,\n prompt: extractPrompt(input.input, input.metadata),\n input: normalizeCerValue(input.input),\n output: normalizeCerValue(input.output),\n parameters: resolveParameters(input.parameters),\n appId: typeof input.metadata?.appId === 'string' ? input.metadata.appId : null,\n runId: typeof input.metadata?.runId === 'string' ? input.metadata.runId : undefined,\n workflowId:\n typeof input.metadata?.workflowId === 'string' ? input.metadata.workflowId : undefined,\n conversationId:\n typeof input.metadata?.conversationId === 'string'\n ? input.metadata.conversationId\n : undefined,\n meta: buildMeta(input.metadata) as CertifyDecisionParams['meta'],\n };\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Create a CER bundle locally from a LangChain final run.\n *\n * Always synchronous — no network required. The resulting bundle is fully\n * aligned with the CER Protocol and verifies via verifyCer() /\n * verifyAiCerBundleDetailed().\n *\n * @example\n * ```ts\n * const { bundle, certificateHash } = createLangChainCer({\n * provider: 'openai',\n * model: 'gpt-4o-mini',\n * input: { messages: [{ role: 'user', content: 'What is 2+2?' }] },\n * output: { text: '4' },\n * createdAt: new Date().toISOString(),\n * });\n * ```\n */\nexport function createLangChainCer(input: LangChainCertificationInput): LangChainCerResult {\n const executionId = input.executionId ?? crypto.randomUUID();\n const bundle = certifyDecision(buildCertifyParams(input, executionId));\n return {\n bundle,\n certificateHash: bundle.certificateHash,\n executionId: bundle.snapshot.executionId,\n };\n}\n\n// ─── certifyLangChainRun overloads ────────────────────────────────────────────\n\n/**\n * Attested mode: call the NexArt node to produce an AttestationReceipt\n * alongside the local CER. Requires nodeUrl + apiKey on the input.\n *\n * @example\n * ```ts\n * const { bundle, certificateHash, receipt } = await certifyLangChainRun({\n * provider: 'openai',\n * model: 'gpt-4o-mini',\n * input: { messages: [...] },\n * output: { text: '...' },\n * nodeUrl: 'https://node.nexart.io',\n * apiKey: 'nxa_...',\n * });\n * ```\n */\nexport function certifyLangChainRun(\n input: LangChainCertificationInput & { nodeUrl: string; apiKey: string },\n _options?: { _attestFn?: AttestDecisionFn },\n): Promise<LangChainAttestedResult>;\n\n/**\n * Local mode: identical to createLangChainCer() — synchronous, no network.\n *\n * @example\n * ```ts\n * const { bundle, certificateHash } = certifyLangChainRun({\n * provider: 'openai',\n * model: 'gpt-4o-mini',\n * input: { messages: [...] },\n * output: { text: '...' },\n * createdAt: new Date().toISOString(),\n * });\n * ```\n */\nexport function certifyLangChainRun(input: LangChainCertificationInput): LangChainCerResult;\n\nexport function certifyLangChainRun(\n input: LangChainCertificationInput & { nodeUrl?: string; apiKey?: string },\n _options?: { _attestFn?: AttestDecisionFn },\n): LangChainCerResult | Promise<LangChainAttestedResult> {\n if (input.nodeUrl && input.apiKey) {\n const executionId = input.executionId ?? crypto.randomUUID();\n const certifyParams = buildCertifyParams({ ...input, executionId }, executionId);\n const attestFn = _options?._attestFn ?? certifyAndAttestDecision;\n return attestFn(certifyParams, { nodeUrl: input.nodeUrl, apiKey: input.apiKey }).then(\n ({ bundle, receipt }) => ({\n bundle,\n receipt,\n certificateHash: bundle.certificateHash,\n executionId: bundle.snapshot.executionId,\n attested: true as const,\n }),\n );\n }\n return createLangChainCer(input);\n}\n","import * as crypto from 'crypto';\nimport type { CerAiExecutionBundle, CertifyDecisionParams } from './types.js';\nimport { createSnapshot } from './snapshot.js';\nimport { sealCer } from './cer.js';\n\nexport function certifyDecision(params: CertifyDecisionParams): CerAiExecutionBundle {\n const executionId = params.executionId ?? crypto.randomUUID();\n\n const snapshot = createSnapshot({\n executionId,\n timestamp: params.timestamp,\n provider: params.provider,\n model: params.model,\n modelVersion: params.modelVersion,\n prompt: params.prompt,\n input: params.input,\n parameters: params.parameters,\n output: params.output,\n sdkVersion: params.sdkVersion,\n appId: params.appId,\n runId: params.runId,\n stepId: params.stepId,\n stepIndex: params.stepIndex,\n workflowId: params.workflowId,\n conversationId: params.conversationId,\n prevStepHash: params.prevStepHash,\n });\n\n return sealCer(snapshot, { createdAt: params.createdAt, meta: params.meta });\n}\n","import * as crypto from 'crypto';\nimport { toCanonicalJson } from './canonicalJson.js';\n\nexport function sha256Hex(data: string | Uint8Array): string {\n const hash = crypto.createHash('sha256');\n if (typeof data === 'string') {\n hash.update(data, 'utf-8');\n } else {\n hash.update(data);\n }\n return hash.digest('hex');\n}\n\nexport function hashUtf8(value: string): string {\n return `sha256:${sha256Hex(value)}`;\n}\n\nexport function hashCanonicalJson(value: unknown): string {\n const canonical = toCanonicalJson(value);\n return `sha256:${sha256Hex(canonical)}`;\n}\n\nexport function computeInputHash(input: string | Record<string, unknown>): string {\n if (typeof input === 'string') {\n return hashUtf8(input);\n }\n return hashCanonicalJson(input);\n}\n\nexport function computeOutputHash(output: string | Record<string, unknown>): string {\n if (typeof output === 'string') {\n return hashUtf8(output);\n }\n return hashCanonicalJson(output);\n}\n","export function toCanonicalJson(value: unknown): string {\n return canonicalize(value);\n}\n\nfunction canonicalize(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error(`Non-finite number not allowed in canonical JSON: ${value}`);\n }\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n const items = value.map(item => canonicalize(item));\n return '[' + items.join(',') + ']';\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const entries = keys.map(key => {\n const val = obj[key];\n if (val === undefined) {\n return null;\n }\n return JSON.stringify(key) + ':' + canonicalize(val);\n }).filter(e => e !== null);\n return '{' + entries.join(',') + '}';\n }\n\n throw new Error(`Unsupported type for canonical JSON: ${typeof value}`);\n}\n","import type { AiExecutionSnapshotV1, CreateSnapshotParams, VerificationResult, AiExecutionParameters } from './types.js';\nimport { CerVerifyCode } from './types.js';\nimport { computeInputHash, computeOutputHash } from './hash.js';\n\nconst PACKAGE_VERSION = '0.8.0';\n\nfunction validateParameters(params: AiExecutionParameters): string[] {\n const errors: string[] = [];\n\n if (typeof params.temperature !== 'number' || !Number.isFinite(params.temperature)) {\n errors.push(`parameters.temperature must be a finite number, got: ${params.temperature}`);\n }\n\n if (typeof params.maxTokens !== 'number' || !Number.isFinite(params.maxTokens)) {\n errors.push(`parameters.maxTokens must be a finite number, got: ${params.maxTokens}`);\n }\n\n if (params.topP !== null && (typeof params.topP !== 'number' || !Number.isFinite(params.topP))) {\n errors.push(`parameters.topP must be a finite number or null, got: ${params.topP}`);\n }\n\n if (params.seed !== null && (typeof params.seed !== 'number' || !Number.isFinite(params.seed))) {\n errors.push(`parameters.seed must be a finite number or null, got: ${params.seed}`);\n }\n\n return errors;\n}\n\nexport function createSnapshot(params: CreateSnapshotParams): AiExecutionSnapshotV1 {\n const paramErrors = validateParameters(params.parameters);\n if (paramErrors.length > 0) {\n throw new Error(`Invalid parameters: ${paramErrors.join('; ')}`);\n }\n\n const inputHash = computeInputHash(params.input);\n const outputHash = computeOutputHash(params.output);\n\n const snapshot: AiExecutionSnapshotV1 = {\n type: 'ai.execution.v1',\n protocolVersion: '1.2.0',\n executionSurface: 'ai',\n executionId: params.executionId,\n timestamp: params.timestamp ?? new Date().toISOString(),\n provider: params.provider,\n model: params.model,\n modelVersion: params.modelVersion ?? null,\n prompt: params.prompt,\n input: params.input,\n inputHash,\n parameters: {\n temperature: params.parameters.temperature,\n maxTokens: params.parameters.maxTokens,\n topP: params.parameters.topP ?? null,\n seed: params.parameters.seed ?? null,\n },\n output: params.output,\n outputHash,\n sdkVersion: params.sdkVersion ?? PACKAGE_VERSION,\n appId: params.appId ?? null,\n };\n\n if (params.runId !== undefined) snapshot.runId = params.runId ?? null;\n if (params.stepId !== undefined) snapshot.stepId = params.stepId ?? null;\n if (params.stepIndex !== undefined) snapshot.stepIndex = params.stepIndex ?? null;\n if (params.workflowId !== undefined) snapshot.workflowId = params.workflowId ?? null;\n if (params.conversationId !== undefined) snapshot.conversationId = params.conversationId ?? null;\n if (params.prevStepHash !== undefined) snapshot.prevStepHash = params.prevStepHash ?? null;\n\n // v0.7.0: AIEF-06 tool/dependency evidence — included in certificateHash when present\n if (params.toolCalls !== undefined && params.toolCalls.length > 0) {\n snapshot.toolCalls = params.toolCalls;\n }\n\n return snapshot;\n}\n\nexport function verifySnapshot(snapshot: AiExecutionSnapshotV1): VerificationResult {\n const schemaErrors: string[] = [];\n const formatErrors: string[] = [];\n const inputHashErrors: string[] = [];\n const outputHashErrors: string[] = [];\n\n if (snapshot.type !== 'ai.execution.v1') {\n schemaErrors.push(`Expected type \"ai.execution.v1\", got \"${snapshot.type}\"`);\n }\n\n if (snapshot.protocolVersion !== '1.2.0') {\n schemaErrors.push(`Expected protocolVersion \"1.2.0\", got \"${snapshot.protocolVersion}\"`);\n }\n\n if (snapshot.executionSurface !== 'ai') {\n schemaErrors.push(`Expected executionSurface \"ai\", got \"${snapshot.executionSurface}\"`);\n }\n\n if (!snapshot.executionId || typeof snapshot.executionId !== 'string') {\n schemaErrors.push('executionId must be a non-empty string');\n }\n\n if (!snapshot.timestamp || typeof snapshot.timestamp !== 'string') {\n schemaErrors.push('timestamp must be a non-empty string');\n }\n\n if (!snapshot.provider || typeof snapshot.provider !== 'string') {\n schemaErrors.push('provider must be a non-empty string');\n }\n\n if (!snapshot.model || typeof snapshot.model !== 'string') {\n schemaErrors.push('model must be a non-empty string');\n }\n\n if (!snapshot.prompt || typeof snapshot.prompt !== 'string') {\n schemaErrors.push('prompt must be a non-empty string');\n }\n\n if (snapshot.input === undefined || snapshot.input === null) {\n schemaErrors.push('input must be a string or object');\n }\n\n if (snapshot.output === undefined || snapshot.output === null) {\n schemaErrors.push('output must be a string or object');\n }\n\n const paramErrors = validateParameters(snapshot.parameters);\n schemaErrors.push(...paramErrors);\n\n if (!snapshot.inputHash || !snapshot.inputHash.startsWith('sha256:')) {\n formatErrors.push(`inputHash must start with \"sha256:\", got \"${snapshot.inputHash}\"`);\n }\n\n if (!snapshot.outputHash || !snapshot.outputHash.startsWith('sha256:')) {\n formatErrors.push(`outputHash must start with \"sha256:\", got \"${snapshot.outputHash}\"`);\n }\n\n if (formatErrors.length === 0) {\n const expectedInputHash = computeInputHash(snapshot.input);\n if (snapshot.inputHash !== expectedInputHash) {\n inputHashErrors.push(`inputHash mismatch: expected ${expectedInputHash}, got ${snapshot.inputHash}`);\n }\n\n const expectedOutputHash = computeOutputHash(snapshot.output);\n if (snapshot.outputHash !== expectedOutputHash) {\n outputHashErrors.push(`outputHash mismatch: expected ${expectedOutputHash}, got ${snapshot.outputHash}`);\n }\n }\n\n const errors = [...schemaErrors, ...formatErrors, ...inputHashErrors, ...outputHashErrors];\n\n if (errors.length === 0) {\n return { ok: true, errors: [], code: CerVerifyCode.OK };\n }\n\n let code: typeof CerVerifyCode[keyof typeof CerVerifyCode];\n let details: string[];\n\n if (schemaErrors.length > 0) {\n code = CerVerifyCode.SCHEMA_ERROR;\n details = schemaErrors;\n } else if (formatErrors.length > 0) {\n code = CerVerifyCode.INVALID_SHA256_FORMAT;\n details = formatErrors;\n } else if (inputHashErrors.length > 0 && outputHashErrors.length > 0) {\n code = CerVerifyCode.SNAPSHOT_HASH_MISMATCH;\n details = [...inputHashErrors, ...outputHashErrors];\n } else if (inputHashErrors.length > 0) {\n code = CerVerifyCode.INPUT_HASH_MISMATCH;\n details = inputHashErrors;\n } else if (outputHashErrors.length > 0) {\n code = CerVerifyCode.OUTPUT_HASH_MISMATCH;\n details = outputHashErrors;\n } else {\n code = CerVerifyCode.UNKNOWN_ERROR;\n details = errors;\n }\n\n return { ok: false, errors, code, details };\n}\n","import type { AiExecutionSnapshotV1, CerAiExecutionBundle, CerMeta, BundleDeclaration, VerificationResult } from './types.js';\nimport { CerVerifyCode } from './types.js';\nimport { toCanonicalJson } from './canonicalJson.js';\nimport { sha256Hex } from './hash.js';\nimport { verifySnapshot } from './snapshot.js';\n\ninterface CertificatePayload {\n bundleType: 'cer.ai.execution.v1';\n createdAt: string;\n snapshot: AiExecutionSnapshotV1;\n version: '0.1';\n}\n\nfunction computeCertificateHash(payload: CertificatePayload): string {\n const canonical = toCanonicalJson(payload);\n return `sha256:${sha256Hex(canonical)}`;\n}\n\nexport function sealCer(\n snapshot: AiExecutionSnapshotV1,\n options?: { createdAt?: string; meta?: CerMeta; declaration?: BundleDeclaration }\n): CerAiExecutionBundle {\n const createdAt = options?.createdAt ?? new Date().toISOString();\n\n // certificateHash covers ONLY { bundleType, createdAt, snapshot, version }.\n // `declaration` and `meta` are intentionally excluded — they are non-evidentiary.\n const payload: CertificatePayload = {\n bundleType: 'cer.ai.execution.v1',\n createdAt,\n snapshot,\n version: '0.1',\n };\n\n const certificateHash = computeCertificateHash(payload);\n\n const bundle: CerAiExecutionBundle = {\n bundleType: 'cer.ai.execution.v1',\n certificateHash,\n createdAt,\n version: '0.1',\n snapshot,\n };\n\n if (options?.meta) {\n bundle.meta = options.meta;\n }\n\n // v0.7.0: attach declaration block AFTER hashing — excluded from certificateHash by design.\n if (options?.declaration) {\n bundle.declaration = options.declaration;\n }\n\n return bundle;\n}\n\nexport function verifyCer(bundle: CerAiExecutionBundle): VerificationResult {\n const schemaErrors: string[] = [];\n const formatErrors: string[] = [];\n\n if (bundle.bundleType !== 'cer.ai.execution.v1') {\n schemaErrors.push(`Expected bundleType \"cer.ai.execution.v1\", got \"${bundle.bundleType}\"`);\n }\n\n if (bundle.version !== '0.1') {\n schemaErrors.push(`Expected version \"0.1\", got \"${bundle.version}\"`);\n }\n\n if (!bundle.createdAt || typeof bundle.createdAt !== 'string') {\n schemaErrors.push('createdAt must be a non-empty string');\n }\n\n if (!bundle.certificateHash || !bundle.certificateHash.startsWith('sha256:')) {\n formatErrors.push(`certificateHash must start with \"sha256:\", got \"${bundle.certificateHash}\"`);\n }\n\n if (!bundle.snapshot) {\n schemaErrors.push('snapshot is required');\n const allErrors = [...schemaErrors, ...formatErrors];\n return { ok: false, errors: allErrors, code: CerVerifyCode.SCHEMA_ERROR, details: schemaErrors };\n }\n\n let canonicalizationError: string | null = null;\n let snapshotResult: VerificationResult | null = null;\n\n try {\n snapshotResult = verifySnapshot(bundle.snapshot);\n } catch (err) {\n canonicalizationError = err instanceof Error ? err.message : String(err);\n }\n\n if (canonicalizationError !== null) {\n const errors = [...schemaErrors, ...formatErrors, canonicalizationError];\n return { ok: false, errors, code: CerVerifyCode.CANONICALIZATION_ERROR, details: [canonicalizationError] };\n }\n\n const snapshotErrors = snapshotResult!.errors;\n\n const certHashErrors: string[] = [];\n try {\n const payload: CertificatePayload = {\n bundleType: 'cer.ai.execution.v1',\n createdAt: bundle.createdAt,\n snapshot: bundle.snapshot,\n version: '0.1',\n };\n const expectedHash = computeCertificateHash(payload);\n if (bundle.certificateHash !== expectedHash) {\n certHashErrors.push(`certificateHash mismatch: expected ${expectedHash}, got ${bundle.certificateHash}`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const errors = [...schemaErrors, ...formatErrors, ...snapshotErrors, msg];\n return { ok: false, errors, code: CerVerifyCode.CANONICALIZATION_ERROR, details: [msg] };\n }\n\n const errors = [...schemaErrors, ...formatErrors, ...snapshotErrors, ...certHashErrors];\n\n if (errors.length === 0) {\n return { ok: true, errors: [], code: CerVerifyCode.OK };\n }\n\n let code: typeof CerVerifyCode[keyof typeof CerVerifyCode];\n let details: string[];\n\n if (schemaErrors.length > 0) {\n code = CerVerifyCode.SCHEMA_ERROR;\n details = schemaErrors;\n } else if (formatErrors.length > 0) {\n code = CerVerifyCode.INVALID_SHA256_FORMAT;\n details = formatErrors;\n } else if (certHashErrors.length > 0 && snapshotErrors.length === 0) {\n code = CerVerifyCode.CERTIFICATE_HASH_MISMATCH;\n details = certHashErrors;\n } else if (snapshotResult && snapshotResult.code !== CerVerifyCode.OK) {\n code = snapshotResult.code;\n details = snapshotResult.details ?? snapshotErrors;\n } else if (certHashErrors.length > 0) {\n code = CerVerifyCode.CERTIFICATE_HASH_MISMATCH;\n details = certHashErrors;\n } else {\n code = CerVerifyCode.UNKNOWN_ERROR;\n details = errors;\n }\n\n return { ok: false, errors, code, details };\n}\n","export class CerVerificationError extends Error {\n public readonly errors: string[];\n\n constructor(errors: string[]) {\n super(`CER verification failed: ${errors.join('; ')}`);\n this.name = 'CerVerificationError';\n this.errors = errors;\n }\n}\n\nexport class CerAttestationError extends Error {\n public readonly statusCode?: number;\n public readonly responseBody?: unknown;\n public readonly details?: string[];\n\n constructor(message: string, statusCode?: number, responseBody?: unknown, details?: string[]) {\n super(message);\n this.name = 'CerAttestationError';\n this.statusCode = statusCode;\n this.responseBody = responseBody;\n this.details = details;\n }\n}\n","import type { CerAiExecutionBundle, SanitizeStorageOptions } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal deep sanitizer\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction deepRemoveUndefined(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n\n if (typeof value === 'bigint') {\n throw new Error('BigInt values are not JSON-safe and cannot be sanitized');\n }\n if (typeof value === 'function') {\n throw new Error('Function values are not JSON-safe and cannot be sanitized');\n }\n if (typeof value === 'symbol') {\n throw new Error('Symbol values are not JSON-safe and cannot be sanitized');\n }\n\n if (Array.isArray(value)) {\n return value.map(deepRemoveUndefined);\n }\n\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n if (val === undefined) continue;\n result[key] = deepRemoveUndefined(val);\n }\n return result;\n }\n\n return value;\n}\n\n/** Redact a value at a dot-separated path inside an object (immutable). */\nfunction redactPath(\n obj: Record<string, unknown>,\n path: string,\n replacement: string,\n): Record<string, unknown> {\n const parts = path.split('.');\n const clone = { ...obj };\n let cursor: Record<string, unknown> = clone;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const key = parts[i];\n if (typeof cursor[key] !== 'object' || cursor[key] === null) return clone;\n cursor[key] = { ...(cursor[key] as Record<string, unknown>) };\n cursor = cursor[key] as Record<string, unknown>;\n }\n\n const last = parts[parts.length - 1];\n if (last in cursor) cursor[last] = replacement;\n return clone;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// v0.4 legacy: sanitizeForAttestation (kept for backward compatibility)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** @deprecated Use sanitizeForStorage or sanitizeForStamp instead. */\nexport function sanitizeForAttestation(bundle: CerAiExecutionBundle): CerAiExecutionBundle {\n return deepRemoveUndefined(bundle) as CerAiExecutionBundle;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// v0.6 helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prepare a bundle for safe storage:\n * - Removes undefined keys.\n * - Removes non-JSON-serializable types (bigint, function, symbol).\n * - Optionally redacts sensitive field paths.\n *\n * Does NOT recompute certificateHash or any content hashes.\n */\nexport function sanitizeForStorage(\n bundle: CerAiExecutionBundle,\n options?: SanitizeStorageOptions,\n): CerAiExecutionBundle {\n let cleaned = deepRemoveUndefined(bundle) as CerAiExecutionBundle;\n\n if (options?.redactPaths && options.redactPaths.length > 0) {\n const replacement = options.redactWith ?? '[REDACTED]';\n let obj = cleaned as unknown as Record<string, unknown>;\n for (const path of options.redactPaths) {\n obj = redactPath(obj, path, replacement);\n }\n cleaned = obj as unknown as CerAiExecutionBundle;\n }\n\n return cleaned;\n}\n\n/**\n * Produce the minimal \"attestable core\" envelope for a bundle.\n *\n * Returns only: bundleType, certificateHash, createdAt, version, snapshot.\n * The meta field is excluded — it is informational and not included in the\n * certificateHash computation.\n *\n * Does NOT recompute any hashes.\n */\nexport function sanitizeForStamp(bundle: CerAiExecutionBundle): Omit<CerAiExecutionBundle, 'meta'> {\n const core: Omit<CerAiExecutionBundle, 'meta'> = {\n bundleType: bundle.bundleType,\n certificateHash: bundle.certificateHash,\n createdAt: bundle.createdAt,\n version: bundle.version,\n snapshot: bundle.snapshot,\n };\n return deepRemoveUndefined(core) as Omit<CerAiExecutionBundle, 'meta'>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Unchanged utility\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport function hasAttestation(bundle: unknown): boolean {\n if (typeof bundle !== 'object' || bundle === null) return false;\n const b = bundle as Record<string, unknown>;\n\n if (typeof b.attestationId === 'string' && b.attestationId.length > 0) return true;\n if (typeof b.nodeRuntimeHash === 'string' && b.nodeRuntimeHash.length > 0) return true;\n\n if (typeof b.attestation === 'object' && b.attestation !== null) {\n const att = b.attestation as Record<string, unknown>;\n if (typeof att.attestationId === 'string' && att.attestationId.length > 0) return true;\n if (typeof att.nodeRuntimeHash === 'string' && att.nodeRuntimeHash.length > 0) return true;\n }\n\n return false;\n}\n","import type { CerAiExecutionBundle, AttestationResult, AttestOptions } from './types.js';\nimport { CerAttestationError } from './errors.js';\nimport { sanitizeForAttestation } from './sanitize.js';\n\nconst SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction validateHashFormat(value: unknown, fieldName: string): string | null {\n if (typeof value !== 'string') return null;\n if (!SHA256_PATTERN.test(value)) {\n return `${fieldName} is not in sha256:<64hex> format: \"${value}\"`;\n }\n return null;\n}\n\nexport async function attest(\n bundle: CerAiExecutionBundle,\n options: AttestOptions,\n): Promise<AttestationResult> {\n const url = `${options.nodeUrl.replace(/\\/+$/, '')}/api/attest`;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sanitized = sanitizeForAttestation(bundle);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${options.apiKey}`,\n },\n body: JSON.stringify(sanitized),\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n const error = err as Error;\n if (error.name === 'AbortError') {\n throw new CerAttestationError(\n `Attestation request timed out after ${timeoutMs}ms`,\n );\n }\n throw new CerAttestationError(\n `Network error contacting attestation node: ${error.message}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n const text = await response.text().catch(() => '');\n throw new CerAttestationError(\n `Attestation node returned non-JSON response (${response.status}): ${text}`,\n response.status,\n );\n }\n\n if (!response.ok) {\n const result = body as Record<string, unknown>;\n const msg = typeof result.error === 'string'\n ? result.error\n : `HTTP ${response.status}`;\n const details = Array.isArray(result.details) ? result.details as string[] : undefined;\n throw new CerAttestationError(\n `Attestation failed: ${msg}`,\n response.status,\n body,\n details,\n );\n }\n\n const result = body as Record<string, unknown>;\n const errors: string[] = [];\n\n if (typeof result.certificateHash === 'string' && result.certificateHash !== bundle.certificateHash) {\n errors.push(\n `Node returned certificateHash \"${result.certificateHash}\" but bundle has \"${bundle.certificateHash}\"`,\n );\n }\n\n const certHashErr = validateHashFormat(result.certificateHash, 'response.certificateHash');\n if (certHashErr) errors.push(certHashErr);\n\n const runtimeHashErr = validateHashFormat(result.nodeRuntimeHash, 'response.nodeRuntimeHash');\n if (runtimeHashErr) errors.push(runtimeHashErr);\n\n if (errors.length > 0) {\n throw new CerAttestationError(\n `Attestation response validation failed: ${errors.join('; ')}`,\n response.status,\n body,\n errors,\n );\n }\n\n return {\n ok: true,\n attestationId: typeof result.attestationId === 'string' ? result.attestationId : undefined,\n nodeRuntimeHash: typeof result.nodeRuntimeHash === 'string' ? result.nodeRuntimeHash : undefined,\n certificateHash: typeof result.certificateHash === 'string' ? result.certificateHash : undefined,\n protocolVersion: typeof result.protocolVersion === 'string' ? result.protocolVersion : undefined,\n raw: body,\n };\n}\n","import type { CerAiExecutionBundle, CertifyDecisionParams, AttestOptions, AttestationReceipt } from './types.js';\nimport { certifyDecision } from './certify.js';\nimport { attest } from './attest.js';\nimport { hasAttestation } from './sanitize.js';\nimport { getAttestationReceipt } from './receipt.js';\nimport { CerAttestationError } from './errors.js';\n\nfunction receiptFromProof(\n proof: Awaited<ReturnType<typeof attest>>,\n bundle: CerAiExecutionBundle,\n): AttestationReceipt {\n if (\n !proof.attestationId ||\n !proof.nodeRuntimeHash ||\n !proof.protocolVersion\n ) {\n throw new CerAttestationError(\n 'Attestation proof is missing required fields (attestationId, nodeRuntimeHash, protocolVersion)',\n );\n }\n\n return {\n attestationId: proof.attestationId,\n certificateHash: proof.certificateHash ?? bundle.certificateHash,\n nodeRuntimeHash: proof.nodeRuntimeHash,\n protocolVersion: proof.protocolVersion,\n };\n}\n\nexport async function certifyAndAttestDecision(\n params: CertifyDecisionParams,\n options: AttestOptions,\n): Promise<{ bundle: CerAiExecutionBundle; receipt: AttestationReceipt }> {\n const bundle = certifyDecision(params);\n const proof = await attest(bundle, options);\n const receipt = receiptFromProof(proof, bundle);\n return { bundle, receipt };\n}\n\nexport async function attestIfNeeded(\n bundle: CerAiExecutionBundle,\n options: AttestOptions,\n): Promise<{ bundle: CerAiExecutionBundle; receipt: AttestationReceipt }> {\n if (hasAttestation(bundle)) {\n const existing = getAttestationReceipt(bundle);\n if (existing) {\n return { bundle, receipt: existing };\n }\n }\n\n const proof = await attest(bundle, options);\n const receipt = receiptFromProof(proof, bundle);\n return { bundle, receipt };\n}\n"],"mappings":"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","names":["crypto","crypto","result"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ProviderConfig, W as WrappedExecutionParams, v as WrappedExecutionResult } from '../types-
|
|
1
|
+
import { t as ProviderConfig, W as WrappedExecutionParams, v as WrappedExecutionResult } from '../types-C5t12OK8.cjs';
|
|
2
2
|
|
|
3
3
|
declare function wrapProvider<TInput = unknown, TOutput = unknown>(config: ProviderConfig<TInput, TOutput>): {
|
|
4
4
|
execute(params: WrappedExecutionParams & {
|
package/dist/providers/wrap.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ProviderConfig, W as WrappedExecutionParams, v as WrappedExecutionResult } from '../types-
|
|
1
|
+
import { t as ProviderConfig, W as WrappedExecutionParams, v as WrappedExecutionResult } from '../types-C5t12OK8.js';
|
|
2
2
|
|
|
3
3
|
declare function wrapProvider<TInput = unknown, TOutput = unknown>(config: ProviderConfig<TInput, TOutput>): {
|
|
4
4
|
execute(params: WrappedExecutionParams & {
|
|
@@ -211,6 +211,8 @@ interface AttestOptions {
|
|
|
211
211
|
interface CertifyDecisionParams {
|
|
212
212
|
executionId?: string;
|
|
213
213
|
timestamp?: string;
|
|
214
|
+
/** CER bundle creation timestamp. Defaults to current time if omitted. */
|
|
215
|
+
createdAt?: string;
|
|
214
216
|
provider: string;
|
|
215
217
|
model: string;
|
|
216
218
|
modelVersion?: string | null;
|
|
@@ -211,6 +211,8 @@ interface AttestOptions {
|
|
|
211
211
|
interface CertifyDecisionParams {
|
|
212
212
|
executionId?: string;
|
|
213
213
|
timestamp?: string;
|
|
214
|
+
/** CER bundle creation timestamp. Defaults to current time if omitted. */
|
|
215
|
+
createdAt?: string;
|
|
214
216
|
provider: string;
|
|
215
217
|
model: string;
|
|
216
218
|
modelVersion?: string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexart/ai-execution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "AI Execution Integrity — tamper-evident records and Certified Execution Records (CER) for AI operations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
"import": "./dist/index.mjs",
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
},
|
|
15
|
+
"./langchain": {
|
|
16
|
+
"types": "./dist/langchain.d.ts",
|
|
17
|
+
"import": "./dist/langchain.mjs",
|
|
18
|
+
"require": "./dist/langchain.cjs"
|
|
19
|
+
},
|
|
15
20
|
"./providers/openai": {
|
|
16
21
|
"types": "./dist/providers/openai.d.ts",
|
|
17
22
|
"import": "./dist/providers/openai.mjs",
|
|
@@ -32,7 +37,7 @@
|
|
|
32
37
|
"scripts": {
|
|
33
38
|
"build": "node \"$(node -p 'require.resolve(\"tsup/dist/cli-default\")')\"",
|
|
34
39
|
"build:tsc": "node \"$(node -p 'require.resolve(\"typescript/bin/tsc\")')\"",
|
|
35
|
-
"test": "node \"$(node -p 'require.resolve(\"typescript/bin/tsc\")')\" && node --test --test-concurrency 1 dist-tsc/__tests__/vectors.test.js dist-tsc/__tests__/fixtures.test.js dist-tsc/__tests__/v020.test.js dist-tsc/__tests__/v030.test.js dist-tsc/__tests__/v040.test.js dist-tsc/__tests__/v041.test.js dist-tsc/__tests__/v042.test.js dist-tsc/__tests__/v050.test.js dist-tsc/__tests__/v060.test.js dist-tsc/__tests__/v070.test.js dist-tsc/__tests__/v071.test.js",
|
|
40
|
+
"test": "node \"$(node -p 'require.resolve(\"typescript/bin/tsc\")')\" && node --test --test-concurrency 1 dist-tsc/__tests__/vectors.test.js dist-tsc/__tests__/fixtures.test.js dist-tsc/__tests__/v020.test.js dist-tsc/__tests__/v030.test.js dist-tsc/__tests__/v040.test.js dist-tsc/__tests__/v041.test.js dist-tsc/__tests__/v042.test.js dist-tsc/__tests__/v050.test.js dist-tsc/__tests__/v060.test.js dist-tsc/__tests__/v070.test.js dist-tsc/__tests__/v071.test.js dist-tsc/__tests__/cerProtocol.test.js dist-tsc/__tests__/langchain.test.js",
|
|
36
41
|
"release": "npm run build && npm run test && npm version patch && npm publish --access public",
|
|
37
42
|
"prepublishOnly": "npm run build && npm run test"
|
|
38
43
|
},
|