{"version":3,"sources":["../src/json.ts","../src/attribution.ts"],"names":["z"],"mappings":"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","file":"attribution.cjs","sourcesContent":["/**\n * JSON-safe validation schemas\n *\n * Provides Zod schemas that guarantee JSON roundtrip safety:\n * - Rejects NaN, Infinity, -Infinity (not valid JSON numbers)\n * - Rejects undefined (dropped by JSON.stringify)\n * - Rejects non-plain objects (Date, Map, Set, class instances)\n * - Rejects functions, symbols, bigints\n */\n\nimport { z } from 'zod';\nimport type { JsonValue, JsonObject, JsonArray } from '@peac/kernel';\nimport { KERNEL_CONSTRAINTS } from './constraints';\n\n/**\n * Check if value is a plain object (not Date, Map, Set, class instance, etc.)\n *\n * A plain object has prototype of Object.prototype or null.\n * This rejects Date, Map, Set, Array, and class instances even when\n * they have zero enumerable properties (which would pass z.record()).\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  if (value === null || typeof value !== 'object') {\n    return false;\n  }\n  const proto = Object.getPrototypeOf(value);\n  return proto === Object.prototype || proto === null;\n}\n\n/**\n * JSON number schema - rejects NaN and Infinity\n *\n * JSON.stringify(NaN) === \"null\" and JSON.stringify(Infinity) === \"null\"\n * which silently corrupts data. We reject these at validation time.\n */\nconst JsonNumberSchema = z.number().finite();\n\n/**\n * JSON primitive schema - string, finite number, boolean, null\n */\nexport const JsonPrimitiveSchema = z.union([z.string(), JsonNumberSchema, z.boolean(), z.null()]);\n\n/**\n * Plain object schema (internal) - validates object is plain before recursive validation\n */\nconst PlainObjectSchema = z.unknown().refine(isPlainObject, {\n  message: 'Expected plain object, received non-plain object (Date, Map, Set, or class instance)',\n});\n\n/**\n * JSON value schema - recursive type for any valid JSON value\n *\n * Validates:\n * - Primitives: string, finite number, boolean, null\n * - Arrays: containing valid JSON values\n * - Objects: plain objects with string keys and valid JSON values\n *\n * Rejects:\n * - undefined (dropped by JSON.stringify)\n * - NaN, Infinity, -Infinity (become null in JSON)\n * - BigInt (throws in JSON.stringify)\n * - Date (becomes ISO string - implicit conversion)\n * - Map, Set (become {} in JSON)\n * - Functions, Symbols (dropped by JSON.stringify)\n * - Class instances (prototype chain lost)\n */\nexport const JsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n  z.union([\n    JsonPrimitiveSchema,\n    z.array(JsonValueSchema),\n    // Plain object check then record validation\n    PlainObjectSchema.transform((obj) => obj as Record<string, unknown>).pipe(\n      z.record(z.string(), JsonValueSchema)\n    ),\n  ])\n) as z.ZodType<JsonValue>;\n\n/**\n * JSON object schema - plain object with string keys and JSON values\n *\n * Rejects non-plain objects (Date, Map, Set, class instances).\n */\nexport const JsonObjectSchema: z.ZodType<JsonObject> = PlainObjectSchema.transform(\n  (obj) => obj as Record<string, unknown>\n).pipe(z.record(z.string(), JsonValueSchema)) as z.ZodType<JsonObject>;\n\n/**\n * JSON array schema - array of JSON values\n */\nexport const JsonArraySchema: z.ZodType<JsonArray> = z.array(JsonValueSchema);\n\n/**\n * Default limits for JSON evidence validation.\n *\n * Derived from KERNEL_CONSTRAINTS (single source of truth).\n * These are conservative defaults to prevent DoS attacks via deeply nested\n * or excessively large JSON structures.\n */\nexport const JSON_EVIDENCE_LIMITS = {\n  /** Maximum nesting depth (default: 32) */\n  maxDepth: KERNEL_CONSTRAINTS.MAX_NESTED_DEPTH,\n  /** Maximum array length (default: 10,000) */\n  maxArrayLength: KERNEL_CONSTRAINTS.MAX_ARRAY_LENGTH,\n  /** Maximum object keys (default: 1,000) */\n  maxObjectKeys: KERNEL_CONSTRAINTS.MAX_OBJECT_KEYS,\n  /** Maximum string length in code units (default: 65,536) */\n  maxStringLength: KERNEL_CONSTRAINTS.MAX_STRING_LENGTH,\n  /** Maximum total nodes to visit (default: 100,000) */\n  maxTotalNodes: KERNEL_CONSTRAINTS.MAX_TOTAL_NODES,\n} as const;\n\n/**\n * Limits for JSON evidence validation\n *\n * @internal - Not part of public API. Use validateEvidence() with defaults.\n *\n * For testing only: import via UNSAFE_JsonEvidenceLimits\n */\nexport interface JsonEvidenceLimits {\n  maxDepth?: number;\n  maxArrayLength?: number;\n  maxObjectKeys?: number;\n  maxStringLength?: number;\n  maxTotalNodes?: number;\n}\n\n/**\n * Result of JSON safety validation\n */\nexport type JsonSafetyResult =\n  | { ok: true }\n  | { ok: false; error: string; path: (string | number)[] };\n\n/**\n * Stack entry type for iterative traversal\n *\n * - \"enter\": entering an object/array, need to validate and push children\n * - \"exit\": exiting an object/array, remove from current path (for cycle detection)\n */\ntype StackEntry =\n  | { type: 'enter'; value: unknown; path: (string | number)[]; depth: number }\n  | { type: 'exit'; obj: object };\n\n/**\n * Iterative JSON safety validator\n *\n * Validates that a value is JSON-safe without using recursion, preventing\n * stack overflow on deeply nested structures. Uses an explicit stack for\n * traversal with entry/exit markers for correct cycle detection.\n *\n * Cycle Detection:\n * Uses a path-based approach where only objects on the current traversal\n * path are tracked. This correctly allows diamond structures (same object\n * referenced from multiple paths) while rejecting actual cycles (object\n * references itself through its descendants).\n *\n * Rejects:\n * - Cycles (object references itself directly or indirectly)\n * - Non-plain objects (Date, Map, Set, class instances)\n * - Non-finite numbers (NaN, Infinity, -Infinity)\n * - undefined, BigInt, functions, symbols\n * - Structures exceeding depth/size limits\n *\n * Allows:\n * - Diamond structures (same object referenced from multiple paths)\n *\n * @param value - Value to validate\n * @param limits - Optional limits (defaults to JSON_EVIDENCE_LIMITS)\n * @returns Result indicating success or failure with error details\n */\nexport function assertJsonSafeIterative(\n  value: unknown,\n  limits: JsonEvidenceLimits = {}\n): JsonSafetyResult {\n  const maxDepth = limits.maxDepth ?? JSON_EVIDENCE_LIMITS.maxDepth;\n  const maxArrayLength = limits.maxArrayLength ?? JSON_EVIDENCE_LIMITS.maxArrayLength;\n  const maxObjectKeys = limits.maxObjectKeys ?? JSON_EVIDENCE_LIMITS.maxObjectKeys;\n  const maxStringLength = limits.maxStringLength ?? JSON_EVIDENCE_LIMITS.maxStringLength;\n  const maxTotalNodes = limits.maxTotalNodes ?? JSON_EVIDENCE_LIMITS.maxTotalNodes;\n\n  // Track objects on the current traversal path for cycle detection.\n  // An object appearing twice on the same path is a cycle.\n  // An object appearing on different paths (diamond) is NOT a cycle.\n  const pathSet = new WeakSet<object>();\n\n  // Track total nodes visited for DoS protection\n  let nodeCount = 0;\n\n  // Stack with entry/exit markers for path tracking\n  const stack: StackEntry[] = [{ type: 'enter', value, path: [], depth: 0 }];\n\n  while (stack.length > 0) {\n    const entry = stack.pop()!;\n\n    // Handle exit marker - remove object from current path\n    if (entry.type === 'exit') {\n      pathSet.delete(entry.obj);\n      continue;\n    }\n\n    const { value: current, path, depth } = entry;\n\n    // Check total node limit\n    nodeCount++;\n    if (nodeCount > maxTotalNodes) {\n      return {\n        ok: false,\n        error: `Maximum total nodes exceeded (limit: ${maxTotalNodes})`,\n        path,\n      };\n    }\n\n    // Check depth limit\n    if (depth > maxDepth) {\n      return {\n        ok: false,\n        error: `Maximum depth exceeded (limit: ${maxDepth})`,\n        path,\n      };\n    }\n\n    // Handle null (valid JSON)\n    if (current === null) {\n      continue;\n    }\n\n    // Handle primitives\n    const type = typeof current;\n\n    if (type === 'string') {\n      if ((current as string).length > maxStringLength) {\n        return {\n          ok: false,\n          error: `String exceeds maximum length (limit: ${maxStringLength})`,\n          path,\n        };\n      }\n      continue;\n    }\n\n    if (type === 'number') {\n      if (!Number.isFinite(current as number)) {\n        return {\n          ok: false,\n          error: `Non-finite number: ${current}`,\n          path,\n        };\n      }\n      continue;\n    }\n\n    if (type === 'boolean') {\n      continue;\n    }\n\n    // Reject non-JSON types\n    if (type === 'undefined') {\n      return { ok: false, error: 'undefined is not valid JSON', path };\n    }\n\n    if (type === 'bigint') {\n      return { ok: false, error: 'BigInt is not valid JSON', path };\n    }\n\n    if (type === 'function') {\n      return { ok: false, error: 'Function is not valid JSON', path };\n    }\n\n    if (type === 'symbol') {\n      return { ok: false, error: 'Symbol is not valid JSON', path };\n    }\n\n    // Handle objects (arrays and plain objects)\n    if (type === 'object') {\n      const obj = current as object;\n\n      // Cycle detection - check if object is already on the current path\n      // (not just visited anywhere, but specifically an ancestor)\n      if (pathSet.has(obj)) {\n        return { ok: false, error: 'Cycle detected in object graph', path };\n      }\n\n      // Add to current path and push exit marker to remove when done\n      pathSet.add(obj);\n      stack.push({ type: 'exit', obj });\n\n      // Handle arrays\n      if (Array.isArray(obj)) {\n        if (obj.length > maxArrayLength) {\n          return {\n            ok: false,\n            error: `Array exceeds maximum length (limit: ${maxArrayLength})`,\n            path,\n          };\n        }\n        // Push array elements to stack in reverse order for correct traversal\n        for (let i = obj.length - 1; i >= 0; i--) {\n          stack.push({ type: 'enter', value: obj[i], path: [...path, i], depth: depth + 1 });\n        }\n        continue;\n      }\n\n      // Check for non-plain objects (Date, Map, Set, class instances, etc.)\n      const proto = Object.getPrototypeOf(obj);\n      if (proto !== Object.prototype && proto !== null) {\n        const constructorName = obj.constructor?.name ?? 'unknown';\n        return {\n          ok: false,\n          error: `Non-plain object (${constructorName}) is not valid JSON`,\n          path,\n        };\n      }\n\n      // Handle plain objects\n      const keys = Object.keys(obj);\n      if (keys.length > maxObjectKeys) {\n        return {\n          ok: false,\n          error: `Object exceeds maximum key count (limit: ${maxObjectKeys})`,\n          path,\n        };\n      }\n      // Push object values to stack\n      for (let i = keys.length - 1; i >= 0; i--) {\n        const key = keys[i];\n        stack.push({\n          type: 'enter',\n          value: (obj as Record<string, unknown>)[key],\n          path: [...path, key],\n          depth: depth + 1,\n        });\n      }\n      continue;\n    }\n\n    // Shouldn't reach here, but reject unknown types\n    return { ok: false, error: `Unknown type: ${type}`, path };\n  }\n\n  return { ok: true };\n}\n","/**\n * Attribution Attestation Types and Validators (v0.9.26+)\n *\n * Provides content derivation and usage proof for PEAC receipts,\n * enabling chain tracking and compliance artifacts.\n *\n * @see docs/specs/ATTRIBUTION.md for normative specification\n */\nimport { z } from 'zod';\nimport type { JsonValue } from '@peac/kernel';\nimport { JsonValueSchema } from './json';\n\n// =============================================================================\n// ATTRIBUTION LIMITS (v0.9.26+)\n// =============================================================================\n\n/**\n * Attribution limits for DoS protection and verification feasibility.\n *\n * These are implementation safety limits, not protocol constraints.\n */\nexport const ATTRIBUTION_LIMITS = {\n  /** Maximum sources per attestation */\n  maxSources: 100,\n  /** Maximum chain resolution depth */\n  maxDepth: 8,\n  /** Maximum attestation size in bytes (64KB) */\n  maxAttestationSize: 65536,\n  /** Per-hop resolution timeout in milliseconds */\n  resolutionTimeout: 5000,\n  /** Maximum receipt reference length */\n  maxReceiptRefLength: 2048,\n  /** Maximum model ID length */\n  maxModelIdLength: 256,\n} as const;\n\n// =============================================================================\n// CONTENT HASH (v0.9.26+)\n// =============================================================================\n\n/**\n * Supported hash algorithms for content hashing.\n * Only sha-256 is supported in v0.9.26.\n */\nexport const HashAlgorithmSchema = z.literal('sha-256');\nexport type HashAlgorithm = z.infer<typeof HashAlgorithmSchema>;\n\n/**\n * Supported encoding formats for hash values.\n */\nexport const HashEncodingSchema = z.literal('base64url');\nexport type HashEncoding = z.infer<typeof HashEncodingSchema>;\n\n/**\n * ContentHash - deterministic content identification.\n *\n * Provides cryptographic verification of content identity using SHA-256.\n * The hash value is base64url-encoded without padding (RFC 4648 Section 5).\n *\n * @example\n * ```typescript\n * const hash: ContentHash = {\n *   alg: 'sha-256',\n *   value: 'n4bQgYhMfWWaL28IoEbM8Qa8jG7x0QXJZJqL-w_zZdA',\n *   enc: 'base64url',\n * };\n * ```\n */\nexport const ContentHashSchema = z\n  .object({\n    /** Hash algorithm (REQUIRED, must be 'sha-256') */\n    alg: HashAlgorithmSchema,\n\n    /** Base64url-encoded hash value without padding (REQUIRED, 43 chars for SHA-256) */\n    value: z\n      .string()\n      .min(43)\n      .max(43)\n      .regex(/^[A-Za-z0-9_-]+$/, 'Invalid base64url characters'),\n\n    /** Encoding format (REQUIRED, must be 'base64url') */\n    enc: HashEncodingSchema,\n  })\n  .strict();\nexport type ContentHash = z.infer<typeof ContentHashSchema>;\n\n// =============================================================================\n// ATTRIBUTION USAGE (v0.9.26+)\n// =============================================================================\n\n/**\n * How source content was used in derivation.\n *\n * - 'training_input': Used to train a model\n * - 'rag_context': Retrieved for RAG context\n * - 'direct_reference': Directly quoted or referenced\n * - 'synthesis_source': Combined with other sources to create new content\n * - 'embedding_source': Used to create embeddings/vectors\n */\nexport const AttributionUsageSchema = z.enum([\n  'training_input',\n  'rag_context',\n  'direct_reference',\n  'synthesis_source',\n  'embedding_source',\n]);\nexport type AttributionUsage = z.infer<typeof AttributionUsageSchema>;\n\n/**\n * Array of valid attribution usage types for runtime checks.\n */\nexport const ATTRIBUTION_USAGES = [\n  'training_input',\n  'rag_context',\n  'direct_reference',\n  'synthesis_source',\n  'embedding_source',\n] as const;\n\n// =============================================================================\n// DERIVATION TYPE (v0.9.26+)\n// =============================================================================\n\n/**\n * Type of content derivation.\n *\n * - 'training': Model training or fine-tuning\n * - 'inference': Runtime inference with RAG/grounding\n * - 'rag': Retrieval-augmented generation\n * - 'synthesis': Multi-source content synthesis\n * - 'embedding': Vector embedding generation\n */\nexport const DerivationTypeSchema = z.enum([\n  'training',\n  'inference',\n  'rag',\n  'synthesis',\n  'embedding',\n]);\nexport type DerivationType = z.infer<typeof DerivationTypeSchema>;\n\n/**\n * Array of valid derivation types for runtime checks.\n */\nexport const DERIVATION_TYPES = ['training', 'inference', 'rag', 'synthesis', 'embedding'] as const;\n\n// =============================================================================\n// ATTRIBUTION SOURCE (v0.9.26+)\n// =============================================================================\n\n/**\n * Receipt reference format validation.\n *\n * Valid formats:\n * - jti:{receipt_id} - Direct receipt identifier\n * - https://... - Resolvable receipt URL\n * - urn:peac:receipt:{id} - URN-formatted identifier\n */\nconst ReceiptRefSchema = z\n  .string()\n  .min(1)\n  .max(ATTRIBUTION_LIMITS.maxReceiptRefLength)\n  .refine(\n    (ref) => {\n      // jti: prefix\n      if (ref.startsWith('jti:')) return true;\n      // URL\n      if (ref.startsWith('https://') || ref.startsWith('http://')) return true;\n      // URN\n      if (ref.startsWith('urn:peac:receipt:')) return true;\n      return false;\n    },\n    { message: 'Invalid receipt reference format. Must be jti:{id}, URL, or urn:peac:receipt:{id}' }\n  );\n\n/**\n * AttributionSource - links to a source receipt and describes how content was used.\n *\n * For cross-issuer resolution, include `receipt_issuer` when using `jti:*` references.\n * URL-based references (`https://...`) are self-resolvable.\n *\n * @example\n * ```typescript\n * const source: AttributionSource = {\n *   receipt_ref: 'jti:rec_abc123def456',\n *   receipt_issuer: 'https://publisher.example.com',\n *   content_hash: { alg: 'sha-256', value: '...', enc: 'base64url' },\n *   usage: 'rag_context',\n *   weight: 0.3,\n * };\n * ```\n */\nexport const AttributionSourceSchema = z\n  .object({\n    /** Reference to source PEAC receipt (REQUIRED) */\n    receipt_ref: ReceiptRefSchema,\n\n    /**\n     * Issuer of the referenced receipt (OPTIONAL but RECOMMENDED for jti: refs).\n     *\n     * Required for cross-issuer resolution when receipt_ref is jti:{id} format.\n     * Not needed for URL-based references which are self-resolvable.\n     * Used to construct resolution URL: {receipt_issuer}/.well-known/peac/receipts/{id}\n     */\n    receipt_issuer: z.string().url().max(2048).optional(),\n\n    /** Hash of source content (OPTIONAL) */\n    content_hash: ContentHashSchema.optional(),\n\n    /** Hash of used excerpt (OPTIONAL, content-minimizing, not privacy-preserving for short text) */\n    excerpt_hash: ContentHashSchema.optional(),\n\n    /** How the source was used (REQUIRED) */\n    usage: AttributionUsageSchema,\n\n    /** Relative contribution weight 0.0-1.0 (OPTIONAL) */\n    weight: z.number().min(0).max(1).optional(),\n  })\n  .strict();\nexport type AttributionSource = z.infer<typeof AttributionSourceSchema>;\n\n// =============================================================================\n// ATTRIBUTION EVIDENCE (v0.9.26+)\n// =============================================================================\n\n/**\n * AttributionEvidence - the payload of an AttributionAttestation.\n *\n * Contains the sources, derivation type, and optional output metadata.\n */\nexport const AttributionEvidenceSchema = z\n  .object({\n    /** Array of attribution sources (REQUIRED, 1-100 sources) */\n    sources: z.array(AttributionSourceSchema).min(1).max(ATTRIBUTION_LIMITS.maxSources),\n\n    /** Type of derivation (REQUIRED) */\n    derivation_type: DerivationTypeSchema,\n\n    /** Hash of derived output (OPTIONAL) */\n    output_hash: ContentHashSchema.optional(),\n\n    /** Model identifier (OPTIONAL) */\n    model_id: z.string().max(ATTRIBUTION_LIMITS.maxModelIdLength).optional(),\n\n    /** Inference provider URL (OPTIONAL) */\n    inference_provider: z.string().url().max(2048).optional(),\n\n    /** Session correlation ID (OPTIONAL) */\n    session_id: z.string().max(256).optional(),\n\n    /** Additional type-specific metadata (OPTIONAL) */\n    metadata: z.record(z.string(), JsonValueSchema).optional(),\n  })\n  .strict();\nexport type AttributionEvidence = z.infer<typeof AttributionEvidenceSchema>;\n\n// =============================================================================\n// ATTRIBUTION ATTESTATION (v0.9.26+)\n// =============================================================================\n\n/**\n * Attestation type literal for attribution\n */\nexport const ATTRIBUTION_TYPE = 'peac/attribution' as const;\n\n/**\n * AttributionAttestation - proves content derivation and usage.\n *\n * This attestation provides cryptographic evidence that content was derived\n * from specific sources, enabling chain tracking and compliance.\n *\n * @example\n * ```typescript\n * const attestation: AttributionAttestation = {\n *   type: 'peac/attribution',\n *   issuer: 'https://ai.example.com',\n *   issued_at: '2026-01-04T12:00:00Z',\n *   evidence: {\n *     sources: [\n *       { receipt_ref: 'jti:rec_abc123', usage: 'rag_context', weight: 0.5 },\n *       { receipt_ref: 'jti:rec_def456', usage: 'rag_context', weight: 0.5 },\n *     ],\n *     derivation_type: 'rag',\n *     model_id: 'gpt-4',\n *   },\n * };\n * ```\n */\nexport const AttributionAttestationSchema = z\n  .object({\n    /** Attestation type (MUST be 'peac/attribution') */\n    type: z.literal(ATTRIBUTION_TYPE),\n\n    /** Issuer of the attestation (inference provider, platform) */\n    issuer: z.string().min(1).max(2048),\n\n    /** When the attestation was issued (RFC 3339) */\n    issued_at: z.string().datetime(),\n\n    /** When the attestation expires (RFC 3339, OPTIONAL) */\n    expires_at: z.string().datetime().optional(),\n\n    /** Reference to external verification endpoint (OPTIONAL) */\n    ref: z.string().url().max(2048).optional(),\n\n    /** Attribution evidence */\n    evidence: AttributionEvidenceSchema,\n  })\n  .strict();\nexport type AttributionAttestation = z.infer<typeof AttributionAttestationSchema>;\n\n// =============================================================================\n// CHAIN VERIFICATION RESULT (v0.9.26+)\n// =============================================================================\n\n/**\n * Result of chain verification including depth and resolved sources.\n */\nexport interface ChainVerificationResult {\n  /** Whether the chain is valid */\n  valid: boolean;\n  /** Maximum depth encountered in the chain */\n  maxDepth: number;\n  /** Total number of sources across the chain */\n  totalSources: number;\n  /** Any cycle detected in the chain */\n  cycleDetected?: string;\n  /** Error message if validation failed */\n  error?: string;\n}\n\n// =============================================================================\n// VALIDATION HELPERS (v0.9.26+)\n// =============================================================================\n\n/**\n * Validate a ContentHash.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated hash or error message\n */\nexport function validateContentHash(\n  data: unknown\n): { ok: true; value: ContentHash } | { ok: false; error: string } {\n  const result = ContentHashSchema.safeParse(data);\n  if (result.success) {\n    return { ok: true, value: result.data };\n  }\n  return { ok: false, error: result.error.message };\n}\n\n/**\n * Validate an AttributionSource.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated source or error message\n */\nexport function validateAttributionSource(\n  data: unknown\n): { ok: true; value: AttributionSource } | { ok: false; error: string } {\n  const result = AttributionSourceSchema.safeParse(data);\n  if (result.success) {\n    return { ok: true, value: result.data };\n  }\n  return { ok: false, error: result.error.message };\n}\n\n/**\n * Validate an AttributionAttestation.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated attestation or error message\n *\n * @example\n * ```typescript\n * const result = validateAttributionAttestation(data);\n * if (result.ok) {\n *   console.log('Sources:', result.value.evidence.sources.length);\n * } else {\n *   console.error('Validation error:', result.error);\n * }\n * ```\n */\nexport function validateAttributionAttestation(\n  data: unknown\n): { ok: true; value: AttributionAttestation } | { ok: false; error: string } {\n  const result = AttributionAttestationSchema.safeParse(data);\n  if (result.success) {\n    return { ok: true, value: result.data };\n  }\n  return { ok: false, error: result.error.message };\n}\n\n/**\n * Check if an object is an AttributionAttestation.\n *\n * @param attestation - Object with a type field\n * @returns True if the type is 'peac/attribution'\n */\nexport function isAttributionAttestation(attestation: {\n  type: string;\n}): attestation is AttributionAttestation {\n  return attestation.type === ATTRIBUTION_TYPE;\n}\n\n/**\n * Parameters for creating an AttributionAttestation.\n */\nexport interface CreateAttributionAttestationParams {\n  /** Issuer of the attestation */\n  issuer: string;\n  /** Attribution sources */\n  sources: AttributionSource[];\n  /** Type of derivation */\n  derivation_type: DerivationType;\n  /** Hash of derived output (optional) */\n  output_hash?: ContentHash;\n  /** Model identifier (optional) */\n  model_id?: string;\n  /** Inference provider URL (optional) */\n  inference_provider?: string;\n  /** Session correlation ID (optional) */\n  session_id?: string;\n  /** When the attestation expires (optional) */\n  expires_at?: string;\n  /** External verification endpoint (optional) */\n  ref?: string;\n  /** Additional metadata (optional, must be JSON-safe) */\n  metadata?: Record<string, JsonValue>;\n}\n\n/**\n * Create an AttributionAttestation with current timestamp.\n *\n * @param params - Attestation parameters\n * @returns A valid AttributionAttestation\n *\n * @example\n * ```typescript\n * const attestation = createAttributionAttestation({\n *   issuer: 'https://ai.example.com',\n *   sources: [\n *     { receipt_ref: 'jti:rec_abc123', usage: 'rag_context' },\n *   ],\n *   derivation_type: 'rag',\n *   model_id: 'gpt-4',\n * });\n * ```\n */\nexport function createAttributionAttestation(\n  params: CreateAttributionAttestationParams\n): AttributionAttestation {\n  const evidence: AttributionEvidence = {\n    sources: params.sources,\n    derivation_type: params.derivation_type,\n  };\n\n  if (params.output_hash) {\n    evidence.output_hash = params.output_hash;\n  }\n  if (params.model_id) {\n    evidence.model_id = params.model_id;\n  }\n  if (params.inference_provider) {\n    evidence.inference_provider = params.inference_provider;\n  }\n  if (params.session_id) {\n    evidence.session_id = params.session_id;\n  }\n  if (params.metadata) {\n    evidence.metadata = params.metadata;\n  }\n\n  const attestation: AttributionAttestation = {\n    type: ATTRIBUTION_TYPE,\n    issuer: params.issuer,\n    issued_at: new Date().toISOString(),\n    evidence,\n  };\n\n  if (params.expires_at) {\n    attestation.expires_at = params.expires_at;\n  }\n  if (params.ref) {\n    attestation.ref = params.ref;\n  }\n\n  return attestation;\n}\n\n/**\n * Check if an attribution attestation is expired.\n *\n * @param attestation - The attestation to check\n * @param clockSkew - Optional clock skew tolerance in milliseconds (default: 30000)\n * @returns True if the attestation has expired\n */\nexport function isAttributionExpired(\n  attestation: AttributionAttestation,\n  clockSkew: number = 30000\n): boolean {\n  if (!attestation.expires_at) {\n    return false; // No expiry = never expires\n  }\n  const expiresAt = new Date(attestation.expires_at).getTime();\n  const now = Date.now();\n  return expiresAt < now - clockSkew;\n}\n\n/**\n * Check if an attribution attestation is not yet valid.\n *\n * @param attestation - The attestation to check\n * @param clockSkew - Optional clock skew tolerance in milliseconds (default: 30000)\n * @returns True if the attestation is not yet valid (issued_at in the future)\n */\nexport function isAttributionNotYetValid(\n  attestation: AttributionAttestation,\n  clockSkew: number = 30000\n): boolean {\n  const issuedAt = new Date(attestation.issued_at).getTime();\n  const now = Date.now();\n  return issuedAt > now + clockSkew;\n}\n\n/**\n * Compute total weight of sources (for validation).\n *\n * @param sources - Array of attribution sources\n * @returns Total weight, or undefined if no weights specified\n */\nexport function computeTotalWeight(sources: AttributionSource[]): number | undefined {\n  const weights = sources.filter((s) => s.weight !== undefined).map((s) => s.weight as number);\n  if (weights.length === 0) {\n    return undefined;\n  }\n  return weights.reduce((sum, w) => sum + w, 0);\n}\n\n/**\n * Detect cycles in attribution sources (for chain validation).\n *\n * @param sources - Array of attribution sources\n * @param visited - Set of visited receipt refs (for recursion)\n * @returns Receipt ref that caused cycle, or undefined if no cycle\n */\nexport function detectCycleInSources(\n  sources: AttributionSource[],\n  visited: Set<string> = new Set()\n): string | undefined {\n  for (const source of sources) {\n    if (visited.has(source.receipt_ref)) {\n      return source.receipt_ref;\n    }\n  }\n  return undefined;\n}\n"]}