@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/dist/langchain.cjs
CHANGED
|
@@ -176,14 +176,22 @@ function computeCertificateHash(payload) {
|
|
|
176
176
|
const canonical = toCanonicalJson(payload);
|
|
177
177
|
return `sha256:${sha256Hex(canonical)}`;
|
|
178
178
|
}
|
|
179
|
+
function buildContext(signals) {
|
|
180
|
+
if (!signals || signals.length === 0) return void 0;
|
|
181
|
+
return { signals };
|
|
182
|
+
}
|
|
179
183
|
function sealCer(snapshot, options) {
|
|
180
184
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
185
|
+
const context = buildContext(options?.signals);
|
|
181
186
|
const payload = {
|
|
182
187
|
bundleType: "cer.ai.execution.v1",
|
|
183
188
|
createdAt,
|
|
184
189
|
snapshot,
|
|
185
190
|
version: "0.1"
|
|
186
191
|
};
|
|
192
|
+
if (context) {
|
|
193
|
+
payload.context = context;
|
|
194
|
+
}
|
|
187
195
|
const certificateHash = computeCertificateHash(payload);
|
|
188
196
|
const bundle = {
|
|
189
197
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -192,6 +200,9 @@ function sealCer(snapshot, options) {
|
|
|
192
200
|
version: "0.1",
|
|
193
201
|
snapshot
|
|
194
202
|
};
|
|
203
|
+
if (context) {
|
|
204
|
+
bundle.context = context;
|
|
205
|
+
}
|
|
195
206
|
if (options?.meta) {
|
|
196
207
|
bundle.meta = options.meta;
|
|
197
208
|
}
|
|
@@ -223,7 +234,11 @@ function certifyDecision(params) {
|
|
|
223
234
|
conversationId: params.conversationId,
|
|
224
235
|
prevStepHash: params.prevStepHash
|
|
225
236
|
});
|
|
226
|
-
return sealCer(snapshot, {
|
|
237
|
+
return sealCer(snapshot, {
|
|
238
|
+
createdAt: params.createdAt,
|
|
239
|
+
meta: params.meta,
|
|
240
|
+
signals: params.signals
|
|
241
|
+
});
|
|
227
242
|
}
|
|
228
243
|
|
|
229
244
|
// src/errors.ts
|
|
@@ -443,7 +458,8 @@ function buildCertifyParams(input, executionId) {
|
|
|
443
458
|
runId: typeof input.metadata?.runId === "string" ? input.metadata.runId : void 0,
|
|
444
459
|
workflowId: typeof input.metadata?.workflowId === "string" ? input.metadata.workflowId : void 0,
|
|
445
460
|
conversationId: typeof input.metadata?.conversationId === "string" ? input.metadata.conversationId : void 0,
|
|
446
|
-
meta: buildMeta(input.metadata)
|
|
461
|
+
meta: buildMeta(input.metadata),
|
|
462
|
+
signals: input.signals
|
|
447
463
|
};
|
|
448
464
|
}
|
|
449
465
|
function createLangChainCer(input) {
|
package/dist/langchain.cjs.map
CHANGED
|
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA,IAAAA,UAAwB;;;ACtBxB,IAAAC,UAAwB;;;ACAxB,aAAwB;;;ACAjB,SAAS,gBAAgB,OAAwB;AACtD,SAAO,aAAa,KAAK;AAC3B;AAEA,SAAS,aAAa,OAAwB;AAC5C,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO,QAAQ,SAAS;AAAA,EAC1B;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,YAAM,IAAI,MAAM,oDAAoD,KAAK,EAAE;AAAA,IAC7E;AACA,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,MAAM,IAAI,UAAQ,aAAa,IAAI,CAAC;AAClD,WAAO,MAAM,MAAM,KAAK,GAAG,IAAI;AAAA,EACjC;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,MAAM;AACZ,UAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AACnC,UAAM,UAAU,KAAK,IAAI,SAAO;AAC9B,YAAM,MAAM,IAAI,GAAG;AACnB,UAAI,QAAQ,QAAW;AACrB,eAAO;AAAA,MACT;AACA,aAAO,KAAK,UAAU,GAAG,IAAI,MAAM,aAAa,GAAG;AAAA,IACrD,CAAC,EAAE,OAAO,OAAK,MAAM,IAAI;AACzB,WAAO,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACnC;AAEA,QAAM,IAAI,MAAM,wCAAwC,OAAO,KAAK,EAAE;AACxE;;;ADxCO,SAAS,UAAU,MAAmC;AAC3D,QAAM,OAAc,kBAAW,QAAQ;AACvC,MAAI,OAAO,SAAS,UAAU;AAC5B,SAAK,OAAO,MAAM,OAAO;AAAA,EAC3B,OAAO;AACL,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,SAAO,KAAK,OAAO,KAAK;AAC1B;AAEO,SAAS,SAAS,OAAuB;AAC9C,SAAO,UAAU,UAAU,KAAK,CAAC;AACnC;AAEO,SAAS,kBAAkB,OAAwB;AACxD,QAAM,YAAY,gBAAgB,KAAK;AACvC,SAAO,UAAU,UAAU,SAAS,CAAC;AACvC;AAEO,SAAS,iBAAiB,OAAiD;AAChF,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,SAAS,KAAK;AAAA,EACvB;AACA,SAAO,kBAAkB,KAAK;AAChC;AAEO,SAAS,kBAAkB,QAAkD;AAClF,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,SAAS,MAAM;AAAA,EACxB;AACA,SAAO,kBAAkB,MAAM;AACjC;;;AE9BA,IAAM,kBAAkB;AAExB,SAAS,mBAAmB,QAAyC;AACnE,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,OAAO,gBAAgB,YAAY,CAAC,OAAO,SAAS,OAAO,WAAW,GAAG;AAClF,WAAO,KAAK,wDAAwD,OAAO,WAAW,EAAE;AAAA,EAC1F;AAEA,MAAI,OAAO,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,OAAO,SAAS,GAAG;AAC9E,WAAO,KAAK,sDAAsD,OAAO,SAAS,EAAE;AAAA,EACtF;AAEA,MAAI,OAAO,SAAS,SAAS,OAAO,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,IAAI;AAC9F,WAAO,KAAK,yDAAyD,OAAO,IAAI,EAAE;AAAA,EACpF;AAEA,MAAI,OAAO,SAAS,SAAS,OAAO,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,IAAI;AAC9F,WAAO,KAAK,yDAAyD,OAAO,IAAI,EAAE;AAAA,EACpF;AAEA,SAAO;AACT;AAEO,SAAS,eAAe,QAAqD;AAClF,QAAM,cAAc,mBAAmB,OAAO,UAAU;AACxD,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,IAAI,MAAM,uBAAuB,YAAY,KAAK,IAAI,CAAC,EAAE;AAAA,EACjE;AAEA,QAAM,YAAY,iBAAiB,OAAO,KAAK;AAC/C,QAAM,aAAa,kBAAkB,OAAO,MAAM;AAElD,QAAM,WAAkC;AAAA,IACtC,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACtD,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd,cAAc,OAAO,gBAAgB;AAAA,IACrC,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd;AAAA,IACA,YAAY;AAAA,MACV,aAAa,OAAO,WAAW;AAAA,MAC/B,WAAW,OAAO,WAAW;AAAA,MAC7B,MAAM,OAAO,WAAW,QAAQ;AAAA,MAChC,MAAM,OAAO,WAAW,QAAQ;AAAA,IAClC;AAAA,IACA,QAAQ,OAAO;AAAA,IACf;AAAA,IACA,YAAY,OAAO,cAAc;AAAA,IACjC,OAAO,OAAO,SAAS;AAAA,EACzB;AAEA,MAAI,OAAO,UAAU,OAAW,UAAS,QAAQ,OAAO,SAAS;AACjE,MAAI,OAAO,WAAW,OAAW,UAAS,SAAS,OAAO,UAAU;AACpE,MAAI,OAAO,cAAc,OAAW,UAAS,YAAY,OAAO,aAAa;AAC7E,MAAI,OAAO,eAAe,OAAW,UAAS,aAAa,OAAO,cAAc;AAChF,MAAI,OAAO,mBAAmB,OAAW,UAAS,iBAAiB,OAAO,kBAAkB;AAC5F,MAAI,OAAO,iBAAiB,OAAW,UAAS,eAAe,OAAO,gBAAgB;AAGtF,MAAI,OAAO,cAAc,UAAa,OAAO,UAAU,SAAS,GAAG;AACjE,aAAS,YAAY,OAAO;AAAA,EAC9B;AAEA,SAAO;AACT;;;AC7DA,SAAS,uBAAuB,SAAqC;AACnE,QAAM,YAAY,gBAAgB,OAAO;AACzC,SAAO,UAAU,UAAU,SAAS,CAAC;AACvC;AAEO,SAAS,QACd,UACA,SACsB;AACtB,QAAM,YAAY,SAAS,cAAa,oBAAI,KAAK,GAAE,YAAY;AAI/D,QAAM,UAA8B;AAAA,IAClC,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX;AAEA,QAAM,kBAAkB,uBAAuB,OAAO;AAEtD,QAAM,SAA+B;AAAA,IACnC,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF;AAEA,MAAI,SAAS,MAAM;AACjB,WAAO,OAAO,QAAQ;AAAA,EACxB;AAGA,MAAI,SAAS,aAAa;AACxB,WAAO,cAAc,QAAQ;AAAA,EAC/B;AAEA,SAAO;AACT;;;AJhDO,SAAS,gBAAgB,QAAqD;AACnF,QAAM,cAAc,OAAO,eAAsB,mBAAW;AAE5D,QAAM,WAAW,eAAe;AAAA,IAC9B;AAAA,IACA,WAAW,OAAO;AAAA,IAClB,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd,cAAc,OAAO;AAAA,IACrB,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,YAAY,OAAO;AAAA,IACnB,QAAQ,OAAO;AAAA,IACf,YAAY,OAAO;AAAA,IACnB,OAAO,OAAO;AAAA,IACd,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,WAAW,OAAO;AAAA,IAClB,YAAY,OAAO;AAAA,IACnB,gBAAgB,OAAO;AAAA,IACvB,cAAc,OAAO;AAAA,EACvB,CAAC;AAED,SAAO,QAAQ,UAAU,EAAE,WAAW,OAAO,WAAW,MAAM,OAAO,KAAK,CAAC;AAC7E;;;AKnBO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EAEhB,YAAY,SAAiB,YAAqB,cAAwB,SAAoB;AAC5F,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,UAAU;AAAA,EACjB;AACF;;;AChBA,SAAS,oBAAoB,OAAyB;AACpD,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAElD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,MAAI,OAAO,UAAU,YAAY;AAC/B,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,mBAAmB;AAAA,EACtC;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACzE,UAAI,QAAQ,OAAW;AACvB,aAAO,GAAG,IAAI,oBAAoB,GAAG;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AA6BO,SAAS,uBAAuB,QAAoD;AACzF,SAAO,oBAAoB,MAAM;AACnC;;;AC5DA,IAAM,iBAAiB;AACvB,IAAM,qBAAqB;AAE3B,SAAS,mBAAmB,OAAgB,WAAkC;AAC5E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,CAAC,eAAe,KAAK,KAAK,GAAG;AAC/B,WAAO,GAAG,SAAS,sCAAsC,KAAK;AAAA,EAChE;AACA,SAAO;AACT;AAEA,eAAsB,OACpB,QACA,SAC4B;AAC5B,QAAM,MAAM,GAAG,QAAQ,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAClD,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,uBAAuB,MAAM;AAE/C,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE5D,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,KAAK;AAAA,MAC1B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,MAC3C;AAAA,MACA,MAAM,KAAK,UAAU,SAAS;AAAA,MAC9B,QAAQ,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,iBAAa,KAAK;AAClB,UAAM,QAAQ;AACd,QAAI,MAAM,SAAS,cAAc;AAC/B,YAAM,IAAI;AAAA,QACR,uCAAuC,SAAS;AAAA,MAClD;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR,8CAA8C,MAAM,OAAO;AAAA,IAC7D;AAAA,EACF,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,QAAQ;AACN,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,UAAM,IAAI;AAAA,MACR,gDAAgD,SAAS,MAAM,MAAM,IAAI;AAAA,MACzE,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAMC,UAAS;AACf,UAAM,MAAM,OAAOA,QAAO,UAAU,WAChCA,QAAO,QACP,QAAQ,SAAS,MAAM;AAC3B,UAAM,UAAU,MAAM,QAAQA,QAAO,OAAO,IAAIA,QAAO,UAAsB;AAC7E,UAAM,IAAI;AAAA,MACR,uBAAuB,GAAG;AAAA,MAC1B,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS;AACf,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,OAAO,oBAAoB,YAAY,OAAO,oBAAoB,OAAO,iBAAiB;AACnG,WAAO;AAAA,MACL,kCAAkC,OAAO,eAAe,qBAAqB,OAAO,eAAe;AAAA,IACrG;AAAA,EACF;AAEA,QAAM,cAAc,mBAAmB,OAAO,iBAAiB,0BAA0B;AACzF,MAAI,YAAa,QAAO,KAAK,WAAW;AAExC,QAAM,iBAAiB,mBAAmB,OAAO,iBAAiB,0BAA0B;AAC5F,MAAI,eAAgB,QAAO,KAAK,cAAc;AAE9C,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI;AAAA,MACR,2CAA2C,OAAO,KAAK,IAAI,CAAC;AAAA,MAC5D,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,eAAe,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,IACjF,iBAAiB,OAAO,OAAO,oBAAoB,WAAW,OAAO,kBAAkB;AAAA,IACvF,iBAAiB,OAAO,OAAO,oBAAoB,WAAW,OAAO,kBAAkB;AAAA,IACvF,iBAAiB,OAAO,OAAO,oBAAoB,WAAW,OAAO,kBAAkB;AAAA,IACvF,KAAK;AAAA,EACP;AACF;;;ACtGA,SAAS,iBACP,OACA,QACoB;AACpB,MACE,CAAC,MAAM,iBACP,CAAC,MAAM,mBACP,CAAC,MAAM,iBACP;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,eAAe,MAAM;AAAA,IACrB,iBAAiB,MAAM,mBAAmB,OAAO;AAAA,IACjD,iBAAiB,MAAM;AAAA,IACvB,iBAAiB,MAAM;AAAA,EACzB;AACF;AAEA,eAAsB,yBACpB,QACA,SACwE;AACxE,QAAM,SAAS,gBAAgB,MAAM;AACrC,QAAM,QAAQ,MAAM,OAAO,QAAQ,OAAO;AAC1C,QAAM,UAAU,iBAAiB,OAAO,MAAM;AAC9C,SAAO,EAAE,QAAQ,QAAQ;AAC3B;;;AT6GA,SAAS,kBAAkB,OAAkD;AAC3E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,EAAE,OAAO,MAAM;AAChD,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,cAAc,OAAgB,UAA4C;AACjF,MAAI,OAAO,UAAU,WAAW,YAAY,SAAS,OAAO,SAAS,GAAG;AACtE,WAAO,SAAS;AAAA,EAClB;AACA,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,UAAM,MAAM;AACZ,QAAI,MAAM,QAAQ,IAAI,QAAQ,KAAK,IAAI,SAAS,SAAS,GAAG;AAC1D,YAAM,QAAQ,IAAI,SAAS,CAAC;AAC5B,UAAI,OAAO,OAAO,YAAY,YAAY,MAAM,QAAQ,SAAS,GAAG;AAClE,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AACA,QAAI,OAAO,IAAI,WAAW,YAAY,IAAI,OAAO,SAAS,EAAG,QAAO,IAAI;AACxE,QAAI,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,SAAS,EAAG,QAAO,IAAI;AAAA,EACtE;AACA,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAC1D,SAAO;AACT;AAEA,SAAS,UAAU,UAAyE;AAC1F,MAAI,CAAC,YAAY,OAAO,KAAK,QAAQ,EAAE,WAAW,EAAG,QAAO;AAC5D,QAAM,WAAW,oBAAI,IAAI,CAAC,UAAU,SAAS,SAAS,cAAc,gBAAgB,CAAC;AACrF,QAAM,YAAsB,CAAC;AAC7B,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC7C,QAAI,CAAC,SAAS,IAAI,CAAC,GAAG;AACpB,gBAAU,KAAK,OAAO,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,EAAE;AAAA,IAClF;AAAA,EACF;AACA,SAAO,UAAU,SAAS,IAAI,EAAE,MAAM,UAAU,IAAI;AACtD;AAEA,SAAS,kBACP,QACuB;AACvB,SAAO;AAAA,IACL,aAAa,QAAQ,eAAe;AAAA,IACpC,WAAW,QAAQ,aAAa;AAAA,IAChC,MAAM,QAAQ,QAAQ;AAAA,IACtB,MAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAGA,SAAS,mBACP,OACA,aACuB;AACvB,SAAO;AAAA,IACL;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,cAAc,MAAM,gBAAgB;AAAA,IACpC,QAAQ,cAAc,MAAM,OAAO,MAAM,QAAQ;AAAA,IACjD,OAAO,kBAAkB,MAAM,KAAK;AAAA,IACpC,QAAQ,kBAAkB,MAAM,MAAM;AAAA,IACtC,YAAY,kBAAkB,MAAM,UAAU;AAAA,IAC9C,OAAO,OAAO,MAAM,UAAU,UAAU,WAAW,MAAM,SAAS,QAAQ;AAAA,IAC1E,OAAO,OAAO,MAAM,UAAU,UAAU,WAAW,MAAM,SAAS,QAAQ;AAAA,IAC1E,YACE,OAAO,MAAM,UAAU,eAAe,WAAW,MAAM,SAAS,aAAa;AAAA,IAC/E,gBACE,OAAO,MAAM,UAAU,mBAAmB,WACtC,MAAM,SAAS,iBACf;AAAA,IACN,MAAM,UAAU,MAAM,QAAQ;AAAA,EAChC;AACF;AAsBO,SAAS,mBAAmB,OAAwD;AACzF,QAAM,cAAc,MAAM,eAAsB,mBAAW;AAC3D,QAAM,SAAS,gBAAgB,mBAAmB,OAAO,WAAW,CAAC;AACrE,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB,OAAO;AAAA,IACxB,aAAa,OAAO,SAAS;AAAA,EAC/B;AACF;AAyCO,SAAS,oBACd,OACA,UACuD;AACvD,MAAI,MAAM,WAAW,MAAM,QAAQ;AACjC,UAAM,cAAc,MAAM,eAAsB,mBAAW;AAC3D,UAAM,gBAAgB,mBAAmB,EAAE,GAAG,OAAO,YAAY,GAAG,WAAW;AAC/E,UAAM,WAAW,UAAU,aAAa;AACxC,WAAO,SAAS,eAAe,EAAE,SAAS,MAAM,SAAS,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,MAC/E,CAAC,EAAE,QAAQ,QAAQ,OAAO;AAAA,QACxB;AAAA,QACA;AAAA,QACA,iBAAiB,OAAO;AAAA,QACxB,aAAa,OAAO,SAAS;AAAA,QAC7B,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,SAAO,mBAAmB,KAAK;AACjC;","names":["crypto","crypto","result"]}
|
|
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 CerContextSignal,\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 * v0.10.0 — Optional upstream signals to bind as evidence in the CER.\n * When provided, included in certificateHash via bundle.context.signals.\n *\n * Accepts NexArtSignal[] from @nexart/signals directly:\n * const collection = collector.export();\n * certifyLangChainRun({ ...input, signals: collection.signals });\n */\n signals?: CerContextSignal[];\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 signals: input.signals,\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, {\n createdAt: params.createdAt,\n meta: params.meta,\n signals: params.signals,\n });\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, CerContext, CerContextSignal, 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 // v0.10.0: included when signals are present — excluded when absent for backward compat.\n context?: CerContext;\n}\n\nfunction computeCertificateHash(payload: CertificatePayload): string {\n const canonical = toCanonicalJson(payload);\n return `sha256:${sha256Hex(canonical)}`;\n}\n\n/** Build a CerContext from an optional signals array. Returns undefined when signals is empty/absent. */\nfunction buildContext(signals?: CerContextSignal[]): CerContext | undefined {\n if (!signals || signals.length === 0) return undefined;\n return { signals };\n}\n\nexport function sealCer(\n snapshot: AiExecutionSnapshotV1,\n options?: {\n createdAt?: string;\n meta?: CerMeta;\n declaration?: BundleDeclaration;\n /** v0.10.0 — upstream signals to bind as evidence. Included in certificateHash. */\n signals?: CerContextSignal[];\n }\n): CerAiExecutionBundle {\n const createdAt = options?.createdAt ?? new Date().toISOString();\n const context = buildContext(options?.signals);\n\n // certificateHash covers { bundleType, createdAt, snapshot, version }.\n // `context` (signals) is INCLUDED when present — signals are evidence, not decoration.\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 if (context) {\n payload.context = context;\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 (context) {\n bundle.context = context;\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 // v0.10.0: include context in hash recomputation when the bundle has signals.\n const verifyContext = buildContext(bundle.context?.signals);\n if (verifyContext) {\n payload.context = verifyContext;\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"]}
|
package/dist/langchain.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as CertifyDecisionParams, f as AttestOptions, c as CerAiExecutionBundle, i as AttestationReceipt, b as CerContextSignal } from './types-C_M2xSWK.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @nexart/ai-execution — LangChain Integration
|
|
@@ -105,6 +105,15 @@ interface LangChainCertificationInput {
|
|
|
105
105
|
* Required when nodeUrl is set.
|
|
106
106
|
*/
|
|
107
107
|
apiKey?: string;
|
|
108
|
+
/**
|
|
109
|
+
* v0.10.0 — Optional upstream signals to bind as evidence in the CER.
|
|
110
|
+
* When provided, included in certificateHash via bundle.context.signals.
|
|
111
|
+
*
|
|
112
|
+
* Accepts NexArtSignal[] from @nexart/signals directly:
|
|
113
|
+
* const collection = collector.export();
|
|
114
|
+
* certifyLangChainRun({ ...input, signals: collection.signals });
|
|
115
|
+
*/
|
|
116
|
+
signals?: CerContextSignal[];
|
|
108
117
|
}
|
|
109
118
|
/** Result returned by createLangChainCer() and certifyLangChainRun() in local mode. */
|
|
110
119
|
interface LangChainCerResult {
|
package/dist/langchain.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as CertifyDecisionParams, f as AttestOptions, c as CerAiExecutionBundle, i as AttestationReceipt, b as CerContextSignal } from './types-C_M2xSWK.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @nexart/ai-execution — LangChain Integration
|
|
@@ -105,6 +105,15 @@ interface LangChainCertificationInput {
|
|
|
105
105
|
* Required when nodeUrl is set.
|
|
106
106
|
*/
|
|
107
107
|
apiKey?: string;
|
|
108
|
+
/**
|
|
109
|
+
* v0.10.0 — Optional upstream signals to bind as evidence in the CER.
|
|
110
|
+
* When provided, included in certificateHash via bundle.context.signals.
|
|
111
|
+
*
|
|
112
|
+
* Accepts NexArtSignal[] from @nexart/signals directly:
|
|
113
|
+
* const collection = collector.export();
|
|
114
|
+
* certifyLangChainRun({ ...input, signals: collection.signals });
|
|
115
|
+
*/
|
|
116
|
+
signals?: CerContextSignal[];
|
|
108
117
|
}
|
|
109
118
|
/** Result returned by createLangChainCer() and certifyLangChainRun() in local mode. */
|
|
110
119
|
interface LangChainCerResult {
|
package/dist/langchain.mjs
CHANGED
|
@@ -141,14 +141,22 @@ function computeCertificateHash(payload) {
|
|
|
141
141
|
const canonical = toCanonicalJson(payload);
|
|
142
142
|
return `sha256:${sha256Hex(canonical)}`;
|
|
143
143
|
}
|
|
144
|
+
function buildContext(signals) {
|
|
145
|
+
if (!signals || signals.length === 0) return void 0;
|
|
146
|
+
return { signals };
|
|
147
|
+
}
|
|
144
148
|
function sealCer(snapshot, options) {
|
|
145
149
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
150
|
+
const context = buildContext(options?.signals);
|
|
146
151
|
const payload = {
|
|
147
152
|
bundleType: "cer.ai.execution.v1",
|
|
148
153
|
createdAt,
|
|
149
154
|
snapshot,
|
|
150
155
|
version: "0.1"
|
|
151
156
|
};
|
|
157
|
+
if (context) {
|
|
158
|
+
payload.context = context;
|
|
159
|
+
}
|
|
152
160
|
const certificateHash = computeCertificateHash(payload);
|
|
153
161
|
const bundle = {
|
|
154
162
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -157,6 +165,9 @@ function sealCer(snapshot, options) {
|
|
|
157
165
|
version: "0.1",
|
|
158
166
|
snapshot
|
|
159
167
|
};
|
|
168
|
+
if (context) {
|
|
169
|
+
bundle.context = context;
|
|
170
|
+
}
|
|
160
171
|
if (options?.meta) {
|
|
161
172
|
bundle.meta = options.meta;
|
|
162
173
|
}
|
|
@@ -188,7 +199,11 @@ function certifyDecision(params) {
|
|
|
188
199
|
conversationId: params.conversationId,
|
|
189
200
|
prevStepHash: params.prevStepHash
|
|
190
201
|
});
|
|
191
|
-
return sealCer(snapshot, {
|
|
202
|
+
return sealCer(snapshot, {
|
|
203
|
+
createdAt: params.createdAt,
|
|
204
|
+
meta: params.meta,
|
|
205
|
+
signals: params.signals
|
|
206
|
+
});
|
|
192
207
|
}
|
|
193
208
|
|
|
194
209
|
// src/errors.ts
|
|
@@ -408,7 +423,8 @@ function buildCertifyParams(input, executionId) {
|
|
|
408
423
|
runId: typeof input.metadata?.runId === "string" ? input.metadata.runId : void 0,
|
|
409
424
|
workflowId: typeof input.metadata?.workflowId === "string" ? input.metadata.workflowId : void 0,
|
|
410
425
|
conversationId: typeof input.metadata?.conversationId === "string" ? input.metadata.conversationId : void 0,
|
|
411
|
-
meta: buildMeta(input.metadata)
|
|
426
|
+
meta: buildMeta(input.metadata),
|
|
427
|
+
signals: input.signals
|
|
412
428
|
};
|
|
413
429
|
}
|
|
414
430
|
function createLangChainCer(input) {
|