@nexart/ai-execution 0.9.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 +277 -5
- package/dist/index.cjs +118 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.mjs +116 -1
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +496 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +194 -0
- package/dist/langchain.d.ts +194 -0
- package/dist/langchain.mjs +460 -0
- package/dist/langchain.mjs.map +1 -0
- 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 +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/anthropic.ts","../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts"],"sourcesContent":["import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface AnthropicChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface AnthropicChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runAnthropicExecution(params: AnthropicChatParams): Promise<AnthropicChatResult> {\n const apiKey = params.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing Anthropic API key. Set ANTHROPIC_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'claude-sonnet-4-20250514';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: [\n { role: 'user', content: params.input },\n ],\n system: params.prompt,\n temperature,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n content: Array<{ type: string; text: string }>;\n model: string;\n };\n\n const textBlock = json.content.find(b => b.type === 'text');\n const output = textBlock?.text ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'anthropic',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\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"],"mappings":"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","names":["crypto"]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/anthropic.ts","../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts"],"sourcesContent":["import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface AnthropicChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface AnthropicChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runAnthropicExecution(params: AnthropicChatParams): Promise<AnthropicChatResult> {\n const apiKey = params.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing Anthropic API key. Set ANTHROPIC_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'claude-sonnet-4-20250514';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: [\n { role: 'user', content: params.input },\n ],\n system: params.prompt,\n temperature,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n content: Array<{ type: string; text: string }>;\n model: string;\n };\n\n const textBlock = json.content.find(b => b.type === 'text');\n const output = textBlock?.text ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'anthropic',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\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"],"mappings":"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","names":["crypto"]}
|
|
@@ -135,14 +135,22 @@ function computeCertificateHash(payload) {
|
|
|
135
135
|
const canonical = toCanonicalJson(payload);
|
|
136
136
|
return `sha256:${sha256Hex(canonical)}`;
|
|
137
137
|
}
|
|
138
|
+
function buildContext(signals) {
|
|
139
|
+
if (!signals || signals.length === 0) return void 0;
|
|
140
|
+
return { signals };
|
|
141
|
+
}
|
|
138
142
|
function sealCer(snapshot, options) {
|
|
139
143
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
144
|
+
const context = buildContext(options?.signals);
|
|
140
145
|
const payload = {
|
|
141
146
|
bundleType: "cer.ai.execution.v1",
|
|
142
147
|
createdAt,
|
|
143
148
|
snapshot,
|
|
144
149
|
version: "0.1"
|
|
145
150
|
};
|
|
151
|
+
if (context) {
|
|
152
|
+
payload.context = context;
|
|
153
|
+
}
|
|
146
154
|
const certificateHash = computeCertificateHash(payload);
|
|
147
155
|
const bundle = {
|
|
148
156
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -151,6 +159,9 @@ function sealCer(snapshot, options) {
|
|
|
151
159
|
version: "0.1",
|
|
152
160
|
snapshot
|
|
153
161
|
};
|
|
162
|
+
if (context) {
|
|
163
|
+
bundle.context = context;
|
|
164
|
+
}
|
|
154
165
|
if (options?.meta) {
|
|
155
166
|
bundle.meta = options.meta;
|
|
156
167
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts","../../src/providers/anthropic.ts"],"sourcesContent":["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","import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface AnthropicChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface AnthropicChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runAnthropicExecution(params: AnthropicChatParams): Promise<AnthropicChatResult> {\n const apiKey = params.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing Anthropic API key. Set ANTHROPIC_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'claude-sonnet-4-20250514';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: [\n { role: 'user', content: params.input },\n ],\n system: params.prompt,\n temperature,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n content: Array<{ type: string; text: string }>;\n model: string;\n };\n\n const textBlock = json.content.find(b => b.type === 'text');\n const output = textBlock?.text ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'anthropic',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\n}\n"],"mappings":"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","names":["crypto"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts","../../src/providers/anthropic.ts"],"sourcesContent":["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","import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface AnthropicChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface AnthropicChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runAnthropicExecution(params: AnthropicChatParams): Promise<AnthropicChatResult> {\n const apiKey = params.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing Anthropic API key. Set ANTHROPIC_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'claude-sonnet-4-20250514';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: [\n { role: 'user', content: params.input },\n ],\n system: params.prompt,\n temperature,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Anthropic API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n content: Array<{ type: string; text: string }>;\n model: string;\n };\n\n const textBlock = json.content.find(b => b.type === 'text');\n const output = textBlock?.text ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'anthropic',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\n}\n"],"mappings":"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","names":["crypto"]}
|
|
@@ -171,14 +171,22 @@ function computeCertificateHash(payload) {
|
|
|
171
171
|
const canonical = toCanonicalJson(payload);
|
|
172
172
|
return `sha256:${sha256Hex(canonical)}`;
|
|
173
173
|
}
|
|
174
|
+
function buildContext(signals) {
|
|
175
|
+
if (!signals || signals.length === 0) return void 0;
|
|
176
|
+
return { signals };
|
|
177
|
+
}
|
|
174
178
|
function sealCer(snapshot, options) {
|
|
175
179
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
180
|
+
const context = buildContext(options?.signals);
|
|
176
181
|
const payload = {
|
|
177
182
|
bundleType: "cer.ai.execution.v1",
|
|
178
183
|
createdAt,
|
|
179
184
|
snapshot,
|
|
180
185
|
version: "0.1"
|
|
181
186
|
};
|
|
187
|
+
if (context) {
|
|
188
|
+
payload.context = context;
|
|
189
|
+
}
|
|
182
190
|
const certificateHash = computeCertificateHash(payload);
|
|
183
191
|
const bundle = {
|
|
184
192
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -187,6 +195,9 @@ function sealCer(snapshot, options) {
|
|
|
187
195
|
version: "0.1",
|
|
188
196
|
snapshot
|
|
189
197
|
};
|
|
198
|
+
if (context) {
|
|
199
|
+
bundle.context = context;
|
|
200
|
+
}
|
|
190
201
|
if (options?.meta) {
|
|
191
202
|
bundle.meta = options.meta;
|
|
192
203
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/openai.ts","../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts"],"sourcesContent":["import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface OpenAIChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface OpenAIChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runOpenAIChatExecution(params: OpenAIChatParams): Promise<OpenAIChatResult> {\n const apiKey = params.apiKey || process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing OpenAI API key. Set OPENAI_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'gpt-4o';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n messages: [\n { role: 'system', content: params.prompt },\n { role: 'user', content: params.input },\n ],\n temperature,\n max_tokens: maxTokens,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n if (seed !== null) {\n requestBody.seed = seed;\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n model: string;\n };\n\n const output = json.choices[0]?.message?.content ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'openai',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,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;;;AJlDA,IAAAA,UAAwB;AAuBxB,eAAsB,uBAAuB,QAAqD;AAChG,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,cAAc,OAAO,YAAY,eAAe;AACtD,QAAM,YAAY,OAAO,YAAY,aAAa;AAClD,QAAM,OAAO,OAAO,YAAY,QAAQ;AACxC,QAAM,OAAO,OAAO,YAAY,QAAQ;AAExC,QAAM,cAAuC;AAAA,IAC3C;AAAA,IACA,UAAU;AAAA,MACR,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO;AAAA,MACzC,EAAE,MAAM,QAAQ,SAAS,OAAO,MAAM;AAAA,IACxC;AAAA,IACA;AAAA,IACA,YAAY;AAAA,EACd;AAEA,MAAI,SAAS,MAAM;AACjB,gBAAY,QAAQ;AAAA,EACtB;AACA,MAAI,SAAS,MAAM;AACjB,gBAAY,OAAO;AAAA,EACrB;AAEA,QAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,IACzE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB,UAAU,MAAM;AAAA,IACnC;AAAA,IACA,MAAM,KAAK,UAAU,WAAW;AAAA,EAClC,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,YAAY,MAAM,SAAS,KAAK;AACtC,UAAM,IAAI,MAAM,qBAAqB,SAAS,MAAM,MAAM,SAAS,EAAE;AAAA,EACvE;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAKjC,QAAM,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS,WAAW;AAEpD,QAAM,WAAW,eAAe;AAAA,IAC9B,aAAoB,mBAAW;AAAA,IAC/B,UAAU;AAAA,IACV;AAAA,IACA,cAAc,OAAO,gBAAgB,KAAK,SAAS;AAAA,IACnD,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACA,OAAO,OAAO,SAAS;AAAA,EACzB,CAAC;AAED,QAAM,SAAS,QAAQ,QAAQ;AAE/B,SAAO,EAAE,QAAQ,UAAU,OAAO;AACpC;","names":["crypto"]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/openai.ts","../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts"],"sourcesContent":["import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface OpenAIChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface OpenAIChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runOpenAIChatExecution(params: OpenAIChatParams): Promise<OpenAIChatResult> {\n const apiKey = params.apiKey || process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing OpenAI API key. Set OPENAI_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'gpt-4o';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n messages: [\n { role: 'system', content: params.prompt },\n { role: 'user', content: params.input },\n ],\n temperature,\n max_tokens: maxTokens,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n if (seed !== null) {\n requestBody.seed = seed;\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n model: string;\n };\n\n const output = json.choices[0]?.message?.content ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'openai',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\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"],"mappings":"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","names":["crypto"]}
|
|
@@ -135,14 +135,22 @@ function computeCertificateHash(payload) {
|
|
|
135
135
|
const canonical = toCanonicalJson(payload);
|
|
136
136
|
return `sha256:${sha256Hex(canonical)}`;
|
|
137
137
|
}
|
|
138
|
+
function buildContext(signals) {
|
|
139
|
+
if (!signals || signals.length === 0) return void 0;
|
|
140
|
+
return { signals };
|
|
141
|
+
}
|
|
138
142
|
function sealCer(snapshot, options) {
|
|
139
143
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
144
|
+
const context = buildContext(options?.signals);
|
|
140
145
|
const payload = {
|
|
141
146
|
bundleType: "cer.ai.execution.v1",
|
|
142
147
|
createdAt,
|
|
143
148
|
snapshot,
|
|
144
149
|
version: "0.1"
|
|
145
150
|
};
|
|
151
|
+
if (context) {
|
|
152
|
+
payload.context = context;
|
|
153
|
+
}
|
|
146
154
|
const certificateHash = computeCertificateHash(payload);
|
|
147
155
|
const bundle = {
|
|
148
156
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -151,6 +159,9 @@ function sealCer(snapshot, options) {
|
|
|
151
159
|
version: "0.1",
|
|
152
160
|
snapshot
|
|
153
161
|
};
|
|
162
|
+
if (context) {
|
|
163
|
+
bundle.context = context;
|
|
164
|
+
}
|
|
154
165
|
if (options?.meta) {
|
|
155
166
|
bundle.meta = options.meta;
|
|
156
167
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts","../../src/providers/openai.ts"],"sourcesContent":["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","import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface OpenAIChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface OpenAIChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runOpenAIChatExecution(params: OpenAIChatParams): Promise<OpenAIChatResult> {\n const apiKey = params.apiKey || process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing OpenAI API key. Set OPENAI_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'gpt-4o';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n messages: [\n { role: 'system', content: params.prompt },\n { role: 'user', content: params.input },\n ],\n temperature,\n max_tokens: maxTokens,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n if (seed !== null) {\n requestBody.seed = seed;\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n model: string;\n };\n\n const output = json.choices[0]?.message?.content ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'openai',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\n}\n"],"mappings":";AAAA,YAAY,YAAY;;;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;;;AClDA,YAAYA,aAAY;AAuBxB,eAAsB,uBAAuB,QAAqD;AAChG,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,cAAc,OAAO,YAAY,eAAe;AACtD,QAAM,YAAY,OAAO,YAAY,aAAa;AAClD,QAAM,OAAO,OAAO,YAAY,QAAQ;AACxC,QAAM,OAAO,OAAO,YAAY,QAAQ;AAExC,QAAM,cAAuC;AAAA,IAC3C;AAAA,IACA,UAAU;AAAA,MACR,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO;AAAA,MACzC,EAAE,MAAM,QAAQ,SAAS,OAAO,MAAM;AAAA,IACxC;AAAA,IACA;AAAA,IACA,YAAY;AAAA,EACd;AAEA,MAAI,SAAS,MAAM;AACjB,gBAAY,QAAQ;AAAA,EACtB;AACA,MAAI,SAAS,MAAM;AACjB,gBAAY,OAAO;AAAA,EACrB;AAEA,QAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,IACzE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB,UAAU,MAAM;AAAA,IACnC;AAAA,IACA,MAAM,KAAK,UAAU,WAAW;AAAA,EAClC,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,YAAY,MAAM,SAAS,KAAK;AACtC,UAAM,IAAI,MAAM,qBAAqB,SAAS,MAAM,MAAM,SAAS,EAAE;AAAA,EACvE;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAKjC,QAAM,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS,WAAW;AAEpD,QAAM,WAAW,eAAe;AAAA,IAC9B,aAAoB,mBAAW;AAAA,IAC/B,UAAU;AAAA,IACV;AAAA,IACA,cAAc,OAAO,gBAAgB,KAAK,SAAS;AAAA,IACnD,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACA,OAAO,OAAO,SAAS;AAAA,EACzB,CAAC;AAED,QAAM,SAAS,QAAQ,QAAQ;AAE/B,SAAO,EAAE,QAAQ,UAAU,OAAO;AACpC;","names":["crypto"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hash.ts","../../src/canonicalJson.ts","../../src/snapshot.ts","../../src/cer.ts","../../src/providers/openai.ts"],"sourcesContent":["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","import type { AiExecutionSnapshotV1, CerAiExecutionBundle } from '../types.js';\nimport { createSnapshot } from '../snapshot.js';\nimport { sealCer } from '../cer.js';\nimport * as crypto from 'crypto';\n\nexport interface OpenAIChatParams {\n prompt: string;\n input: string;\n model?: string;\n modelVersion?: string | null;\n parameters?: {\n temperature?: number;\n maxTokens?: number;\n topP?: number | null;\n seed?: number | null;\n };\n appId?: string | null;\n apiKey?: string;\n}\n\nexport interface OpenAIChatResult {\n output: string;\n snapshot: AiExecutionSnapshotV1;\n bundle: CerAiExecutionBundle;\n}\n\nexport async function runOpenAIChatExecution(params: OpenAIChatParams): Promise<OpenAIChatResult> {\n const apiKey = params.apiKey || process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'Missing OpenAI API key. Set OPENAI_API_KEY environment variable or pass apiKey in params.'\n );\n }\n\n const model = params.model ?? 'gpt-4o';\n const temperature = params.parameters?.temperature ?? 0.7;\n const maxTokens = params.parameters?.maxTokens ?? 1024;\n const topP = params.parameters?.topP ?? null;\n const seed = params.parameters?.seed ?? null;\n\n const requestBody: Record<string, unknown> = {\n model,\n messages: [\n { role: 'system', content: params.prompt },\n { role: 'user', content: params.input },\n ],\n temperature,\n max_tokens: maxTokens,\n };\n\n if (topP !== null) {\n requestBody.top_p = topP;\n }\n if (seed !== null) {\n requestBody.seed = seed;\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const json = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n model: string;\n };\n\n const output = json.choices[0]?.message?.content ?? '';\n\n const snapshot = createSnapshot({\n executionId: crypto.randomUUID(),\n provider: 'openai',\n model,\n modelVersion: params.modelVersion ?? json.model ?? null,\n prompt: params.prompt,\n input: params.input,\n parameters: {\n temperature,\n maxTokens,\n topP,\n seed,\n },\n output,\n appId: params.appId ?? null,\n });\n\n const bundle = sealCer(snapshot);\n\n return { output, snapshot, bundle };\n}\n"],"mappings":"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","names":["crypto"]}
|
package/dist/providers/wrap.cjs
CHANGED
|
@@ -172,14 +172,22 @@ function computeCertificateHash(payload) {
|
|
|
172
172
|
const canonical = toCanonicalJson(payload);
|
|
173
173
|
return `sha256:${sha256Hex(canonical)}`;
|
|
174
174
|
}
|
|
175
|
+
function buildContext(signals) {
|
|
176
|
+
if (!signals || signals.length === 0) return void 0;
|
|
177
|
+
return { signals };
|
|
178
|
+
}
|
|
175
179
|
function sealCer(snapshot, options) {
|
|
176
180
|
const createdAt = options?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
181
|
+
const context = buildContext(options?.signals);
|
|
177
182
|
const payload = {
|
|
178
183
|
bundleType: "cer.ai.execution.v1",
|
|
179
184
|
createdAt,
|
|
180
185
|
snapshot,
|
|
181
186
|
version: "0.1"
|
|
182
187
|
};
|
|
188
|
+
if (context) {
|
|
189
|
+
payload.context = context;
|
|
190
|
+
}
|
|
183
191
|
const certificateHash = computeCertificateHash(payload);
|
|
184
192
|
const bundle = {
|
|
185
193
|
bundleType: "cer.ai.execution.v1",
|
|
@@ -188,6 +196,9 @@ function sealCer(snapshot, options) {
|
|
|
188
196
|
version: "0.1",
|
|
189
197
|
snapshot
|
|
190
198
|
};
|
|
199
|
+
if (context) {
|
|
200
|
+
bundle.context = context;
|
|
201
|
+
}
|
|
191
202
|
if (options?.meta) {
|
|
192
203
|
bundle.meta = options.meta;
|
|
193
204
|
}
|