Spaces:
Running
Running
deploy(hf): sync szl-holdings/a11oy@648cf7c672d1b1e31260f3fe4b359dc9b364c54f derived COPY set
4ad2f4c verified | import * as crypto from "node:crypto"; | |
| import * as fs from "node:fs"; | |
| export type HashAlgorithm = "SHA-256" | "SHA3-256" | "SHA3-512"; | |
| export type ReceiptProtocol = "mcp" | "cursor" | "claude" | "a11oy"; | |
| export type ReceiptEventType = | |
| | "MCP_TOOL_CALL" | |
| | "CURSOR_AGENT_EDIT" | |
| | "CLAUDE_SUBAGENT_CALL" | |
| | "A11OY_OPERATION" | |
| | "AUTONOMOUS_LEARNING_PROPOSAL" | |
| | "AUTONOMOUS_LEARNING_EVALUATION" | |
| | "HUMAN_PROMOTION"; | |
| export interface ToolEnvelope { | |
| readonly protocol: ReceiptProtocol; | |
| readonly actor_id: string; | |
| readonly tool_name: string; | |
| readonly tool_version?: string; | |
| readonly invocation_id: string; | |
| readonly lambda_axes: readonly string[]; | |
| readonly payload: unknown; | |
| readonly metadata?: Record<string, unknown>; | |
| } | |
| export interface ReceiptPolicy { | |
| readonly algorithm: HashAlgorithm; | |
| readonly chaining: "hash_chain" | "merkle_dag"; | |
| readonly quorum: string; | |
| readonly nodes: readonly string[]; | |
| readonly vertical?: string; | |
| readonly regime?: string; | |
| } | |
| export interface QecWitness { | |
| readonly payload_byte: number; | |
| readonly shor_repetition_count: 9; | |
| readonly shor_majority_payload: number; | |
| readonly css_x_parity: number; | |
| readonly css_z_parity: number; | |
| readonly css_consistent: boolean; | |
| } | |
| export interface OperationalReceipt { | |
| readonly schema_version: "1.0.0"; | |
| readonly receipt_id: string; | |
| readonly event_type: ReceiptEventType; | |
| readonly timestamp_iso8601: string; | |
| readonly timestamp_tai64n: string; | |
| readonly sequence: number; | |
| readonly actor_id: string; | |
| readonly tool_name: string; | |
| readonly protocol: ReceiptProtocol; | |
| readonly payload_hash: string; | |
| readonly prev_receipt_hash: string | null; | |
| readonly quorum_signatures: readonly string[]; | |
| readonly policy?: Pick<ReceiptPolicy, "algorithm" | "chaining" | "quorum" | "nodes" | "vertical" | "regime">; | |
| readonly qec_witness: QecWitness; | |
| readonly envelope: ToolEnvelope; | |
| readonly merkle_root: string; | |
| } | |
| export interface EmitReceiptOptions { | |
| readonly previousReceipt?: OperationalReceipt | null; | |
| readonly policy?: Partial<ReceiptPolicy>; | |
| readonly quorumSignatures?: readonly string[]; | |
| readonly timestamp?: Date; | |
| readonly sequence?: number; | |
| readonly eventType?: ReceiptEventType; | |
| } | |
| export interface VerifyResult { | |
| readonly valid: boolean; | |
| readonly errors: readonly string[]; | |
| } | |
| const DEFAULT_POLICY: ReceiptPolicy = { | |
| algorithm: "SHA3-256", | |
| chaining: "hash_chain", | |
| quorum: "1-of-1", | |
| nodes: ["local-operator"], | |
| }; | |
| function normaliseStrings(value: unknown): unknown { | |
| if (typeof value === "string") return value.normalize("NFC"); | |
| if (Array.isArray(value)) return value.map(normaliseStrings); | |
| if (value && typeof value === "object") { | |
| const result: Record<string, unknown> = {}; | |
| for (const [key, child] of Object.entries(value as Record<string, unknown>)) { | |
| result[key.normalize("NFC")] = normaliseStrings(child); | |
| } | |
| return result; | |
| } | |
| return value; | |
| } | |
| export function canonicalJson(value: unknown): string { | |
| return JSON.stringify(canonicalValue(normaliseStrings(value))); | |
| } | |
| function canonicalValue(value: unknown): unknown { | |
| if (value === null) return null; | |
| if (typeof value === "string" || typeof value === "boolean") return value; | |
| if (typeof value === "number") { | |
| if (!Number.isFinite(value)) throw new Error("canonicalJson: non-finite numbers are not supported"); | |
| return value; | |
| } | |
| if (Array.isArray(value)) return value.map(canonicalValue); | |
| if (typeof value === "object") { | |
| const obj = value as Record<string, unknown>; | |
| const sorted: Record<string, unknown> = {}; | |
| for (const key of Object.keys(obj).sort()) { | |
| const child = obj[key]; | |
| if (typeof child === "undefined") { | |
| continue; | |
| } | |
| if (typeof child === "function" || typeof child === "symbol") { | |
| throw new Error(`canonicalJson: unsupported value at key '${key}'`); | |
| } | |
| sorted[key] = canonicalValue(child); | |
| } | |
| return sorted; | |
| } | |
| if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") { | |
| throw new Error("canonicalJson: unsupported root value"); | |
| } | |
| return value; | |
| } | |
| export function hashHex(value: unknown, algorithm: HashAlgorithm = "SHA3-256"): string { | |
| const nodeAlgorithm = algorithmToNode(algorithm); | |
| const bytes = Buffer.isBuffer(value) ? value : Buffer.from(typeof value === "string" ? value : canonicalJson(value), "utf8"); | |
| return crypto.createHash(nodeAlgorithm).update(bytes).digest("hex"); | |
| } | |
| function algorithmToNode(algorithm: HashAlgorithm): string { | |
| switch (algorithm) { | |
| case "SHA-256": | |
| return "sha256"; | |
| case "SHA3-256": | |
| return crypto.getHashes().includes("sha3-256") ? "sha3-256" : "sha256"; | |
| case "SHA3-512": | |
| return crypto.getHashes().includes("sha3-512") ? "sha3-512" : "sha512"; | |
| } | |
| } | |
| export function parseQuorum(quorum: string): { required: number; total: number } { | |
| const match = /^(\d+)-of-(\d+)$/.exec(quorum); | |
| if (!match) throw new Error(`Invalid quorum '${quorum}'; expected N-of-M`); | |
| const required = Number(match[1]); | |
| const total = Number(match[2]); | |
| if (!Number.isInteger(required) || !Number.isInteger(total) || required < 1 || total < 1 || required > total) { | |
| throw new Error(`Invalid quorum '${quorum}'; required signers must be between 1 and total`); | |
| } | |
| return { required, total }; | |
| } | |
| export function createToolEnvelope(input: { | |
| protocol?: ReceiptProtocol; | |
| actor_id: string; | |
| tool_name: string; | |
| tool_version?: string; | |
| invocation_id?: string; | |
| lambda_axes?: readonly string[]; | |
| payload: unknown; | |
| metadata?: Record<string, unknown>; | |
| }): ToolEnvelope { | |
| const invocationMaterial = { | |
| actor_id: input.actor_id, | |
| tool_name: input.tool_name, | |
| payload: input.payload, | |
| metadata: input.metadata ?? {}, | |
| }; | |
| return { | |
| protocol: input.protocol ?? "mcp", | |
| actor_id: input.actor_id.normalize("NFC"), | |
| tool_name: input.tool_name.normalize("NFC"), | |
| tool_version: input.tool_version?.normalize("NFC"), | |
| invocation_id: input.invocation_id?.normalize("NFC") ?? `inv-${hashHex(invocationMaterial).slice(0, 16)}`, | |
| lambda_axes: [...(input.lambda_axes ?? ["Λ7"])].map((axis) => axis.normalize("NFC")).sort(), | |
| payload: normaliseStrings(input.payload), | |
| metadata: input.metadata ? normaliseStrings(input.metadata) as Record<string, unknown> : undefined, | |
| }; | |
| } | |
| export function tai64n(date: Date = new Date()): string { | |
| const unixSeconds = BigInt(Math.floor(date.getTime() / 1000)); | |
| const nanos = BigInt(date.getMilliseconds()) * 1_000_000n; | |
| const taiSeconds = unixSeconds + 37n + 0x4000000000000000n; | |
| return `@${taiSeconds.toString(16).padStart(16, "0")}${nanos.toString(16).padStart(8, "0")}`; | |
| } | |
| export function qecWitness(payloadHash: string): QecWitness { | |
| const payloadByte = Number.parseInt(payloadHash.slice(0, 2), 16) & 0xff; | |
| const cssX = payloadByte; | |
| const cssZ = (payloadByte ^ 0xff) & 0xff; | |
| return { | |
| payload_byte: payloadByte, | |
| shor_repetition_count: 9, | |
| shor_majority_payload: payloadByte, | |
| css_x_parity: cssX, | |
| css_z_parity: cssZ, | |
| css_consistent: (cssX ^ cssZ) === 0xff, | |
| }; | |
| } | |
| export function emitReceipt(envelope: ToolEnvelope, options: EmitReceiptOptions = {}): OperationalReceipt { | |
| const policy = { ...DEFAULT_POLICY, ...options.policy }; | |
| parseQuorum(policy.quorum); | |
| const timestamp = options.timestamp ?? new Date(); | |
| const sequence = options.sequence ?? ((options.previousReceipt?.sequence ?? -1) + 1); | |
| const payloadHash = hashHex(envelope, policy.algorithm); | |
| const prevReceiptHash = options.previousReceipt?.merkle_root ?? null; | |
| const signatures = options.quorumSignatures ?? policy.nodes.slice(0, parseQuorum(policy.quorum).required); | |
| const partial = { | |
| schema_version: "1.0.0" as const, | |
| event_type: options.eventType ?? eventTypeForProtocol(envelope.protocol), | |
| timestamp_iso8601: timestamp.toISOString(), | |
| timestamp_tai64n: tai64n(timestamp), | |
| sequence, | |
| actor_id: envelope.actor_id, | |
| tool_name: envelope.tool_name, | |
| protocol: envelope.protocol, | |
| payload_hash: payloadHash, | |
| prev_receipt_hash: prevReceiptHash, | |
| quorum_signatures: [...signatures].sort(), | |
| policy: { | |
| algorithm: policy.algorithm, | |
| chaining: policy.chaining, | |
| quorum: policy.quorum, | |
| nodes: [...policy.nodes].sort(), | |
| vertical: policy.vertical, | |
| regime: policy.regime, | |
| }, | |
| qec_witness: qecWitness(payloadHash), | |
| envelope, | |
| }; | |
| const merkleRoot = hashHex(partial, policy.algorithm); | |
| const receiptId = `or-${hashHex({ merkleRoot, payloadHash, sequence }, policy.algorithm).slice(0, 20)}`; | |
| return { receipt_id: receiptId, ...partial, merkle_root: merkleRoot }; | |
| } | |
| function eventTypeForProtocol(protocol: ReceiptProtocol): ReceiptEventType { | |
| switch (protocol) { | |
| case "cursor": | |
| return "CURSOR_AGENT_EDIT"; | |
| case "claude": | |
| return "CLAUDE_SUBAGENT_CALL"; | |
| case "a11oy": | |
| return "A11OY_OPERATION"; | |
| case "mcp": | |
| return "MCP_TOOL_CALL"; | |
| } | |
| } | |
| export function appendReceipt(chain: readonly OperationalReceipt[], envelope: ToolEnvelope, options: Omit<EmitReceiptOptions, "previousReceipt"> = {}): OperationalReceipt[] { | |
| const previousReceipt = chain.length > 0 ? chain[chain.length - 1] : null; | |
| return [...chain, emitReceipt(envelope, { ...options, previousReceipt })]; | |
| } | |
| export function verifyReceipt(receipt: OperationalReceipt): VerifyResult { | |
| const errors: string[] = []; | |
| const algorithm = receipt.policy?.algorithm ?? DEFAULT_POLICY.algorithm; | |
| const expectedPayloadHash = hashHex(receipt.envelope, algorithm); | |
| if (receipt.payload_hash !== expectedPayloadHash) { | |
| errors.push(`payload_hash mismatch for ${receipt.receipt_id}`); | |
| } | |
| const { merkle_root: _merkleRoot, receipt_id: _receiptId, ...partial } = receipt; | |
| const expectedMerkleRoot = hashHex(partial, algorithm); | |
| if (receipt.merkle_root !== expectedMerkleRoot) { | |
| errors.push(`merkle_root mismatch for ${receipt.receipt_id}`); | |
| } | |
| const expectedReceiptId = `or-${hashHex({ | |
| merkleRoot: expectedMerkleRoot, | |
| payloadHash: expectedPayloadHash, | |
| sequence: receipt.sequence, | |
| }, algorithm).slice(0, 20)}`; | |
| if (receipt.receipt_id !== expectedReceiptId) { | |
| errors.push(`receipt_id mismatch for ${receipt.receipt_id}`); | |
| } | |
| if (!receipt.qec_witness.css_consistent || receipt.qec_witness.payload_byte !== receipt.qec_witness.shor_majority_payload) { | |
| errors.push(`qec witness mismatch for ${receipt.receipt_id}`); | |
| } | |
| return { valid: errors.length === 0, errors }; | |
| } | |
| export function verifyChain(chain: readonly OperationalReceipt[], policyOverride?: Partial<ReceiptPolicy>): VerifyResult { | |
| const errors: string[] = []; | |
| const seen = new Set<string>(); | |
| for (let i = 0; i < chain.length; i += 1) { | |
| const receipt = chain[i]; | |
| const receiptResult = verifyReceipt(receipt); | |
| errors.push(...receiptResult.errors.map((error) => `position ${i}: ${error}`)); | |
| if (seen.has(receipt.receipt_id)) { | |
| errors.push(`position ${i}: duplicate receipt_id ${receipt.receipt_id}`); | |
| } | |
| seen.add(receipt.receipt_id); | |
| const expectedPrev = i === 0 ? null : chain[i - 1].merkle_root; | |
| if (receipt.prev_receipt_hash !== expectedPrev) { | |
| errors.push(`position ${i}: prev_receipt_hash mismatch`); | |
| } | |
| if (!Number.isInteger(receipt.sequence) || receipt.sequence !== i) { | |
| errors.push(`position ${i}: sequence mismatch`); | |
| } | |
| if (i > 0 && receipt.timestamp_tai64n <= chain[i - 1].timestamp_tai64n) { | |
| errors.push(`position ${i}: timestamp regression`); | |
| } | |
| const policy = { ...DEFAULT_POLICY, ...receipt.policy, ...policyOverride }; | |
| try { | |
| const quorum = parseQuorum(policy.quorum); | |
| const uniqueSignatures = new Set(receipt.quorum_signatures); | |
| const knownNodes = new Set(policy.nodes); | |
| if (policy.nodes.length < quorum.total) { | |
| errors.push(`position ${i}: quorum total exceeds known nodes`); | |
| } | |
| if (uniqueSignatures.size < quorum.required) { | |
| errors.push(`position ${i}: insufficient quorum signatures`); | |
| } | |
| for (const signer of uniqueSignatures) { | |
| if (knownNodes.size > 0 && !knownNodes.has(signer)) { | |
| errors.push(`position ${i}: unknown quorum signer ${signer}`); | |
| } | |
| } | |
| } catch (error) { | |
| errors.push(`position ${i}: ${(error as Error).message}`); | |
| } | |
| } | |
| return { valid: errors.length === 0, errors }; | |
| } | |
| export function parseJsonlReceipts(jsonl: string): OperationalReceipt[] { | |
| return jsonl | |
| .split(/\r?\n/) | |
| .map((line) => line.trim()) | |
| .filter(Boolean) | |
| .map((line) => JSON.parse(line) as OperationalReceipt); | |
| } | |
| export function receiptToJsonl(receipt: OperationalReceipt): string { | |
| return `${canonicalJson(receipt)}\n`; | |
| } | |
| export function readReceiptJsonl(path: string): OperationalReceipt[] { | |
| if (!fs.existsSync(path)) return []; | |
| return parseJsonlReceipts(fs.readFileSync(path, "utf8")); | |
| } | |
| export function appendReceiptJsonl(path: string, receipt: OperationalReceipt): void { | |
| fs.appendFileSync(path, receiptToJsonl(receipt), "utf8"); | |
| } | |
| export const TWO_WITNESS_LIVE_ADAPTERS_ENABLED = false as const; | |
| export type ResearchEvidenceLabel = | |
| | "CORROBORATED" | |
| | "DIVERGENT" | |
| | "SINGLE_PROVIDER" | |
| | "INSUFFICIENT" | |
| | "UNAVAILABLE"; | |
| export type ResearchProvider = "openai" | "perplexity"; | |
| export type ResearchProviderStatus = "SUCCESS" | "UNAVAILABLE" | "ERROR"; | |
| export interface ResearchUsageInput { | |
| readonly input_tokens?: number; | |
| readonly output_tokens?: number; | |
| readonly total_tokens?: number; | |
| readonly reasoning_tokens?: number; | |
| readonly cached_tokens?: number; | |
| readonly search_queries?: number; | |
| } | |
| export interface OpenAIResearchSourceInput { | |
| readonly url: string; | |
| readonly title?: string; | |
| readonly published_at?: string; | |
| readonly last_updated_at?: string; | |
| } | |
| export interface PerplexityResearchResultInput { | |
| readonly url: string; | |
| readonly title?: string; | |
| readonly date?: string; | |
| readonly last_updated?: string; | |
| } | |
| export interface OpenAIWebSearchResultInput { | |
| readonly provider: "openai"; | |
| readonly status: ResearchProviderStatus; | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly response_id?: string; | |
| readonly model?: string; | |
| readonly http_status?: number; | |
| readonly latency_ms?: number; | |
| readonly usage?: ResearchUsageInput; | |
| readonly cost_usd?: number; | |
| readonly sources?: readonly OpenAIResearchSourceInput[]; | |
| } | |
| export interface PerplexitySearchResultInput { | |
| readonly provider: "perplexity"; | |
| readonly status: ResearchProviderStatus; | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly response_id?: string; | |
| readonly model?: string; | |
| readonly http_status?: number; | |
| readonly latency_ms?: number; | |
| readonly usage?: ResearchUsageInput; | |
| readonly cost_usd?: number; | |
| readonly results?: readonly PerplexityResearchResultInput[]; | |
| } | |
| export interface NormalizedResearchSource { | |
| readonly url: string; | |
| readonly domain: string; | |
| readonly title_sha256?: string; | |
| readonly published_at?: string; | |
| readonly last_updated_at?: string; | |
| } | |
| export interface NormalizedResearchUsage { | |
| readonly input_tokens?: number; | |
| readonly output_tokens?: number; | |
| readonly total_tokens?: number; | |
| readonly reasoning_tokens?: number; | |
| readonly cached_tokens?: number; | |
| readonly search_queries?: number; | |
| } | |
| export interface NormalizedResearchProviderEvidence { | |
| readonly schema_version: "a11oy.research_provider_evidence/v0"; | |
| readonly provider: ResearchProvider; | |
| readonly api_surface: "openai.responses.web_search" | "perplexity.search"; | |
| readonly tool: "web_search" | "search"; | |
| readonly status: ResearchProviderStatus; | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly response_id?: string; | |
| readonly model?: string; | |
| readonly http_status?: number; | |
| readonly caller_observed_latency_ms?: number; | |
| readonly usage?: NormalizedResearchUsage; | |
| readonly provider_reported_cost_usd?: number; | |
| readonly sources: readonly NormalizedResearchSource[]; | |
| readonly source_count: number; | |
| readonly source_list_sha256: string; | |
| } | |
| export interface ResearchEvidenceComparison { | |
| readonly schema_version: "a11oy.research_evidence_comparison/v0"; | |
| readonly label: ResearchEvidenceLabel; | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly providers: readonly NormalizedResearchProviderEvidence[]; | |
| readonly successful_provider_count: number; | |
| readonly evidence_provider_count: number; | |
| readonly source_union_count: number; | |
| readonly source_url_overlap_count: number; | |
| readonly source_domain_overlap_count: number; | |
| readonly source_url_jaccard: number; | |
| readonly integrity_valid: boolean; | |
| readonly integrity_errors: readonly string[]; | |
| readonly action_authorized: false; | |
| } | |
| export interface TwoWitnessResearchReceiptOptions extends EmitReceiptOptions { | |
| readonly actor_id: string; | |
| readonly invocation_id?: string; | |
| } | |
| const SHA256_PATTERN = /^[a-f0-9]{64}$/; | |
| const SENSITIVE_SOURCE_QUERY_KEYS = new Set([ | |
| "access_token", | |
| "api_key", | |
| "apikey", | |
| "auth", | |
| "authorization", | |
| "credential", | |
| "key", | |
| "password", | |
| "secret", | |
| "signature", | |
| "sig", | |
| "token", | |
| ]); | |
| const DIGESTED_SOURCE_QUERY_KEYS = new Set(["code", "q", "snippet"]); | |
| const ZONED_RFC3339_PATTERN = | |
| /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})$/; | |
| const SENSITIVE_SOURCE_QUERY_PREFIXES = ["x-amz-", "x-goog-", "x-oss-"]; | |
| const TRACKING_SOURCE_QUERY_PREFIXES = ["utm_"]; | |
| const TRACKING_SOURCE_QUERY_KEYS = new Set(["fbclid", "gclid", "mc_cid", "mc_eid"]); | |
| const NORMALIZED_PROVIDER_KEYS = new Set([ | |
| "schema_version", | |
| "provider", | |
| "api_surface", | |
| "tool", | |
| "status", | |
| "query_sha256", | |
| "policy_sha256", | |
| "response_id", | |
| "model", | |
| "http_status", | |
| "caller_observed_latency_ms", | |
| "usage", | |
| "provider_reported_cost_usd", | |
| "sources", | |
| "source_count", | |
| "source_list_sha256", | |
| ]); | |
| const NORMALIZED_SOURCE_KEYS = new Set([ | |
| "url", | |
| "domain", | |
| "title_sha256", | |
| "published_at", | |
| "last_updated_at", | |
| ]); | |
| const NORMALIZED_USAGE_KEYS = new Set([ | |
| "input_tokens", | |
| "output_tokens", | |
| "total_tokens", | |
| "reasoning_tokens", | |
| "cached_tokens", | |
| "search_queries", | |
| ]); | |
| function cleanOptionalText(value: unknown, maxLength = 256): string | undefined { | |
| if (typeof value !== "string") return undefined; | |
| const cleaned = value.normalize("NFC").trim(); | |
| if (!cleaned || cleaned.length > maxLength || /[\r\n]/.test(cleaned)) return undefined; | |
| return cleaned; | |
| } | |
| function cleanSha256(value: unknown): string { | |
| return typeof value === "string" ? value.trim().toLowerCase() : ""; | |
| } | |
| function cleanStatus(value: unknown): ResearchProviderStatus { | |
| if (value === "SUCCESS" || value === "UNAVAILABLE" || value === "ERROR") return value; | |
| return "ERROR"; | |
| } | |
| function cleanNonNegativeNumber(value: unknown): number | undefined { | |
| if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return undefined; | |
| return value; | |
| } | |
| function cleanNonNegativeInteger(value: unknown): number | undefined { | |
| const cleaned = cleanNonNegativeNumber(value); | |
| return cleaned !== undefined && Number.isInteger(cleaned) ? cleaned : undefined; | |
| } | |
| function cleanHttpStatus(value: unknown): number | undefined { | |
| const cleaned = cleanNonNegativeInteger(value); | |
| return cleaned !== undefined && cleaned >= 100 && cleaned <= 599 ? cleaned : undefined; | |
| } | |
| function cleanDate(value: unknown): string | undefined { | |
| if (typeof value !== "string") return undefined; | |
| const cleaned = value.trim(); | |
| const match = ZONED_RFC3339_PATTERN.exec(cleaned); | |
| if (!match) return undefined; | |
| const [, yearText, monthText, dayText, hourText, minuteText, secondText, fraction = "", zone] = | |
| match; | |
| const year = Number(yearText); | |
| const month = Number(monthText); | |
| const day = Number(dayText); | |
| const hour = Number(hourText); | |
| const minute = Number(minuteText); | |
| const second = Number(secondText); | |
| const millisecond = Number((fraction + "000").slice(0, 3)); | |
| if (zone === "-00:00") return undefined; | |
| if (zone !== "Z") { | |
| const offsetHour = Number(zone.slice(1, 3)); | |
| const offsetMinute = Number(zone.slice(4, 6)); | |
| if (offsetHour > 23 || offsetMinute > 59) return undefined; | |
| } | |
| const calendar = new Date(0); | |
| calendar.setUTCFullYear(year, month - 1, day); | |
| calendar.setUTCHours(hour, minute, second, millisecond); | |
| if ( | |
| calendar.getUTCFullYear() !== year || | |
| calendar.getUTCMonth() !== month - 1 || | |
| calendar.getUTCDate() !== day || | |
| calendar.getUTCHours() !== hour || | |
| calendar.getUTCMinutes() !== minute || | |
| calendar.getUTCSeconds() !== second | |
| ) { | |
| return undefined; | |
| } | |
| const date = new Date(cleaned); | |
| return Number.isNaN(date.getTime()) ? undefined : date.toISOString(); | |
| } | |
| function cleanSourceUrl(raw: unknown): string | undefined { | |
| if (typeof raw !== "string") return undefined; | |
| try { | |
| const url = new URL(raw.trim()); | |
| if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username || url.password) { | |
| return undefined; | |
| } | |
| url.hash = ""; | |
| for (const key of [...url.searchParams.keys()]) { | |
| const lower = key.toLowerCase(); | |
| if (DIGESTED_SOURCE_QUERY_KEYS.has(lower)) { | |
| const digests = url.searchParams | |
| .getAll(key) | |
| .map((value) => ( | |
| /^sha256:[a-f0-9]{64}$/.test(value) | |
| ? value.slice("sha256:".length) | |
| : hashHex(value, "SHA-256") | |
| )) | |
| .sort(); | |
| url.searchParams.delete(key); | |
| for (const digest of digests) { | |
| url.searchParams.append(key, `sha256:${digest}`); | |
| } | |
| continue; | |
| } | |
| if ( | |
| SENSITIVE_SOURCE_QUERY_KEYS.has(lower) | |
| || SENSITIVE_SOURCE_QUERY_PREFIXES.some((prefix) => lower.startsWith(prefix)) | |
| || TRACKING_SOURCE_QUERY_KEYS.has(lower) | |
| || TRACKING_SOURCE_QUERY_PREFIXES.some((prefix) => lower.startsWith(prefix)) | |
| ) { | |
| url.searchParams.delete(key); | |
| } | |
| } | |
| url.searchParams.sort(); | |
| url.hostname = url.hostname.toLowerCase(); | |
| if ((url.protocol === "http:" && url.port === "80") || (url.protocol === "https:" && url.port === "443")) { | |
| url.port = ""; | |
| } | |
| if (url.pathname.length > 1 && url.pathname.endsWith("/")) { | |
| url.pathname = url.pathname.replace(/\/+$/, ""); | |
| } | |
| return url.toString(); | |
| } catch { | |
| return undefined; | |
| } | |
| } | |
| function normalizeResearchSources( | |
| inputs: readonly { | |
| readonly url: string; | |
| readonly title?: string; | |
| readonly published_at?: string; | |
| readonly last_updated_at?: string; | |
| }[], | |
| ): readonly NormalizedResearchSource[] { | |
| const byUrl = new Map<string, NormalizedResearchSource>(); | |
| for (const input of inputs) { | |
| const url = cleanSourceUrl(input.url); | |
| if (!url) continue; | |
| const title = cleanOptionalText(input.title, 2_000); | |
| const domain = new URL(url).hostname; | |
| const source: NormalizedResearchSource = { | |
| url, | |
| domain, | |
| ...(title ? { title_sha256: hashHex(title, "SHA-256") } : {}), | |
| ...(cleanDate(input.published_at) ? { published_at: cleanDate(input.published_at) } : {}), | |
| ...(cleanDate(input.last_updated_at) ? { last_updated_at: cleanDate(input.last_updated_at) } : {}), | |
| }; | |
| byUrl.set(url, source); | |
| } | |
| return [...byUrl.values()].sort((left, right) => left.url.localeCompare(right.url)); | |
| } | |
| function normalizeResearchUsage(input: ResearchUsageInput | undefined): NormalizedResearchUsage | undefined { | |
| if (!input) return undefined; | |
| const usage: NormalizedResearchUsage = { | |
| ...(cleanNonNegativeInteger(input.input_tokens) !== undefined | |
| ? { input_tokens: cleanNonNegativeInteger(input.input_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.output_tokens) !== undefined | |
| ? { output_tokens: cleanNonNegativeInteger(input.output_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.total_tokens) !== undefined | |
| ? { total_tokens: cleanNonNegativeInteger(input.total_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.reasoning_tokens) !== undefined | |
| ? { reasoning_tokens: cleanNonNegativeInteger(input.reasoning_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.cached_tokens) !== undefined | |
| ? { cached_tokens: cleanNonNegativeInteger(input.cached_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.search_queries) !== undefined | |
| ? { search_queries: cleanNonNegativeInteger(input.search_queries) } | |
| : {}), | |
| }; | |
| return Object.keys(usage).length > 0 ? usage : undefined; | |
| } | |
| function isRecord(value: unknown): value is Record<string, unknown> { | |
| return typeof value === "object" && value !== null && !Array.isArray(value); | |
| } | |
| function unexpectedKeys( | |
| value: Record<string, unknown>, | |
| allowed: ReadonlySet<string>, | |
| ): readonly string[] { | |
| return Object.keys(value).filter((key) => !allowed.has(key)).sort(); | |
| } | |
| function projectNormalizedResearchSource( | |
| input: unknown, | |
| ): NormalizedResearchSource { | |
| const record = isRecord(input) ? input : {}; | |
| const url = cleanSourceUrl(record.url) ?? ""; | |
| const domain = cleanOptionalText(record.domain, 253)?.toLowerCase() ?? ""; | |
| const titleSha256 = record.title_sha256 === undefined | |
| ? undefined | |
| : cleanSha256(record.title_sha256); | |
| const publishedAt = cleanDate(record.published_at); | |
| const lastUpdatedAt = cleanDate(record.last_updated_at); | |
| return { | |
| url, | |
| domain, | |
| ...(titleSha256 !== undefined ? { title_sha256: titleSha256 } : {}), | |
| ...(publishedAt !== undefined ? { published_at: publishedAt } : {}), | |
| ...(lastUpdatedAt !== undefined ? { last_updated_at: lastUpdatedAt } : {}), | |
| }; | |
| } | |
| function projectNormalizedResearchUsage( | |
| input: unknown, | |
| ): NormalizedResearchUsage | undefined { | |
| if (!isRecord(input)) return undefined; | |
| const projected: NormalizedResearchUsage = { | |
| ...(cleanNonNegativeInteger(input.input_tokens) !== undefined | |
| ? { input_tokens: cleanNonNegativeInteger(input.input_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.output_tokens) !== undefined | |
| ? { output_tokens: cleanNonNegativeInteger(input.output_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.total_tokens) !== undefined | |
| ? { total_tokens: cleanNonNegativeInteger(input.total_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.reasoning_tokens) !== undefined | |
| ? { reasoning_tokens: cleanNonNegativeInteger(input.reasoning_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.cached_tokens) !== undefined | |
| ? { cached_tokens: cleanNonNegativeInteger(input.cached_tokens) } | |
| : {}), | |
| ...(cleanNonNegativeInteger(input.search_queries) !== undefined | |
| ? { search_queries: cleanNonNegativeInteger(input.search_queries) } | |
| : {}), | |
| }; | |
| return Object.keys(projected).length > 0 ? projected : undefined; | |
| } | |
| function projectNormalizedProviderEvidence( | |
| input: unknown, | |
| ): NormalizedResearchProviderEvidence { | |
| const record = isRecord(input) ? input : {}; | |
| const provider = record.provider === "openai" || record.provider === "perplexity" | |
| ? record.provider | |
| : "invalid" as ResearchProvider; | |
| const apiSurface = record.api_surface === "openai.responses.web_search" | |
| || record.api_surface === "perplexity.search" | |
| ? record.api_surface | |
| : "invalid" as NormalizedResearchProviderEvidence["api_surface"]; | |
| const tool = record.tool === "web_search" || record.tool === "search" | |
| ? record.tool | |
| : "invalid" as NormalizedResearchProviderEvidence["tool"]; | |
| const responseId = cleanOptionalText(record.response_id); | |
| const model = cleanOptionalText(record.model); | |
| const httpStatus = cleanHttpStatus(record.http_status); | |
| const latencyMs = cleanNonNegativeNumber(record.caller_observed_latency_ms); | |
| const usage = projectNormalizedResearchUsage(record.usage); | |
| const costUsd = cleanNonNegativeNumber(record.provider_reported_cost_usd); | |
| const suppliedSources = Array.isArray(record.sources) ? record.sources : []; | |
| const sources = suppliedSources.map(projectNormalizedResearchSource); | |
| return { | |
| schema_version: record.schema_version === "a11oy.research_provider_evidence/v0" | |
| ? record.schema_version | |
| : "invalid" as NormalizedResearchProviderEvidence["schema_version"], | |
| provider, | |
| api_surface: apiSurface, | |
| tool, | |
| status: cleanStatus(record.status), | |
| query_sha256: cleanSha256(record.query_sha256), | |
| policy_sha256: cleanSha256(record.policy_sha256), | |
| ...(responseId ? { response_id: responseId } : {}), | |
| ...(model ? { model } : {}), | |
| ...(httpStatus !== undefined ? { http_status: httpStatus } : {}), | |
| ...(latencyMs !== undefined ? { caller_observed_latency_ms: latencyMs } : {}), | |
| ...(usage ? { usage } : {}), | |
| ...(costUsd !== undefined ? { provider_reported_cost_usd: costUsd } : {}), | |
| sources, | |
| source_count: cleanNonNegativeInteger(record.source_count) ?? -1, | |
| source_list_sha256: cleanSha256(record.source_list_sha256), | |
| }; | |
| } | |
| function structuralResearchEvidenceErrors( | |
| providers: readonly unknown[], | |
| ): readonly string[] { | |
| const errors: string[] = []; | |
| for (const [providerIndex, input] of providers.entries()) { | |
| if (!isRecord(input)) { | |
| errors.push(`provider ${providerIndex}: expected an object`); | |
| continue; | |
| } | |
| const providerLabel = input.provider === "openai" || input.provider === "perplexity" | |
| ? input.provider | |
| : `provider ${providerIndex}`; | |
| for (const key of unexpectedKeys(input, NORMALIZED_PROVIDER_KEYS)) { | |
| errors.push(`${providerLabel}: unexpected provider field ${key}`); | |
| } | |
| if (input.usage !== undefined) { | |
| if (!isRecord(input.usage)) { | |
| errors.push(`${providerLabel}: usage must be an object`); | |
| } else { | |
| for (const key of unexpectedKeys(input.usage, NORMALIZED_USAGE_KEYS)) { | |
| errors.push(`${providerLabel}: unexpected usage field ${key}`); | |
| } | |
| } | |
| } | |
| if (!Array.isArray(input.sources)) { | |
| errors.push(`${providerLabel}: sources must be an array`); | |
| continue; | |
| } | |
| for (const [sourceIndex, source] of input.sources.entries()) { | |
| if (!isRecord(source)) { | |
| errors.push(`${providerLabel}: source ${sourceIndex} must be an object`); | |
| continue; | |
| } | |
| for (const key of unexpectedKeys(source, NORMALIZED_SOURCE_KEYS)) { | |
| errors.push(`${providerLabel}: source ${sourceIndex} unexpected field ${key}`); | |
| } | |
| } | |
| } | |
| return errors; | |
| } | |
| function normalizeProviderEvidence(input: { | |
| readonly provider: ResearchProvider; | |
| readonly api_surface: NormalizedResearchProviderEvidence["api_surface"]; | |
| readonly tool: NormalizedResearchProviderEvidence["tool"]; | |
| readonly status: ResearchProviderStatus; | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly response_id?: string; | |
| readonly model?: string; | |
| readonly http_status?: number; | |
| readonly latency_ms?: number; | |
| readonly usage?: ResearchUsageInput; | |
| readonly cost_usd?: number; | |
| readonly sources: readonly NormalizedResearchSource[]; | |
| }): NormalizedResearchProviderEvidence { | |
| const responseId = cleanOptionalText(input.response_id); | |
| const model = cleanOptionalText(input.model); | |
| const httpStatus = cleanHttpStatus(input.http_status); | |
| const latencyMs = cleanNonNegativeNumber(input.latency_ms); | |
| const usage = normalizeResearchUsage(input.usage); | |
| const costUsd = cleanNonNegativeNumber(input.cost_usd); | |
| return { | |
| schema_version: "a11oy.research_provider_evidence/v0", | |
| provider: input.provider, | |
| api_surface: input.api_surface, | |
| tool: input.tool, | |
| status: cleanStatus(input.status), | |
| query_sha256: cleanSha256(input.query_sha256), | |
| policy_sha256: cleanSha256(input.policy_sha256), | |
| ...(responseId ? { response_id: responseId } : {}), | |
| ...(model ? { model } : {}), | |
| ...(httpStatus !== undefined ? { http_status: httpStatus } : {}), | |
| ...(latencyMs !== undefined ? { caller_observed_latency_ms: latencyMs } : {}), | |
| ...(usage ? { usage } : {}), | |
| ...(costUsd !== undefined ? { provider_reported_cost_usd: costUsd } : {}), | |
| sources: input.sources, | |
| source_count: input.sources.length, | |
| source_list_sha256: hashHex(input.sources, "SHA-256"), | |
| }; | |
| } | |
| export function normalizeOpenAIWebSearchResult( | |
| input: OpenAIWebSearchResultInput, | |
| ): NormalizedResearchProviderEvidence { | |
| return normalizeProviderEvidence({ | |
| provider: "openai", | |
| api_surface: "openai.responses.web_search", | |
| tool: "web_search", | |
| status: input.status, | |
| query_sha256: input.query_sha256, | |
| policy_sha256: input.policy_sha256, | |
| response_id: input.response_id, | |
| model: input.model, | |
| http_status: input.http_status, | |
| latency_ms: input.latency_ms, | |
| usage: input.usage, | |
| cost_usd: input.cost_usd, | |
| sources: normalizeResearchSources(input.sources ?? []), | |
| }); | |
| } | |
| export function normalizePerplexitySearchResult( | |
| input: PerplexitySearchResultInput, | |
| ): NormalizedResearchProviderEvidence { | |
| return normalizeProviderEvidence({ | |
| provider: "perplexity", | |
| api_surface: "perplexity.search", | |
| tool: "search", | |
| status: input.status, | |
| query_sha256: input.query_sha256, | |
| policy_sha256: input.policy_sha256, | |
| response_id: input.response_id, | |
| model: input.model, | |
| http_status: input.http_status, | |
| latency_ms: input.latency_ms, | |
| usage: input.usage, | |
| cost_usd: input.cost_usd, | |
| sources: normalizeResearchSources( | |
| (input.results ?? []).map((result) => ({ | |
| url: result.url, | |
| title: result.title, | |
| published_at: result.date, | |
| last_updated_at: result.last_updated, | |
| })), | |
| ), | |
| }); | |
| } | |
| function intersectionSize(left: ReadonlySet<string>, right: ReadonlySet<string>): number { | |
| let count = 0; | |
| for (const value of left) { | |
| if (right.has(value)) count += 1; | |
| } | |
| return count; | |
| } | |
| export function compareResearchEvidence(input: { | |
| readonly query_sha256: string; | |
| readonly policy_sha256: string; | |
| readonly providers: readonly NormalizedResearchProviderEvidence[]; | |
| }): ResearchEvidenceComparison { | |
| const querySha256 = cleanSha256(input.query_sha256); | |
| const policySha256 = cleanSha256(input.policy_sha256); | |
| const suppliedProviders = Array.isArray(input.providers) ? input.providers : []; | |
| const errors = Array.isArray(input.providers) | |
| ? [...structuralResearchEvidenceErrors(suppliedProviders)] | |
| : ["providers must be an array"]; | |
| const providers = suppliedProviders | |
| .map(projectNormalizedProviderEvidence) | |
| .sort((left, right) => left.provider.localeCompare(right.provider)); | |
| if (!SHA256_PATTERN.test(querySha256)) errors.push("expected query_sha256 is not a SHA-256 digest"); | |
| if (!SHA256_PATTERN.test(policySha256)) errors.push("expected policy_sha256 is not a SHA-256 digest"); | |
| const expectedProviders = new Set<ResearchProvider>(["openai", "perplexity"]); | |
| const seenProviders = new Set<ResearchProvider>(); | |
| for (const provider of providers) { | |
| if (seenProviders.has(provider.provider)) errors.push(`duplicate provider: ${provider.provider}`); | |
| seenProviders.add(provider.provider); | |
| if (provider.schema_version !== "a11oy.research_provider_evidence/v0") { | |
| errors.push(`${provider.provider}: schema_version mismatch`); | |
| } | |
| const expectedSurface = provider.provider === "openai" | |
| ? "openai.responses.web_search" | |
| : "perplexity.search"; | |
| const expectedTool = provider.provider === "openai" ? "web_search" : "search"; | |
| if (provider.api_surface !== expectedSurface) { | |
| errors.push(`${provider.provider}: api_surface mismatch`); | |
| } | |
| if (provider.tool !== expectedTool) errors.push(`${provider.provider}: tool mismatch`); | |
| if (!SHA256_PATTERN.test(provider.query_sha256)) { | |
| errors.push(`${provider.provider}: query_sha256 is not a SHA-256 digest`); | |
| } else if (provider.query_sha256 !== querySha256) { | |
| errors.push(`${provider.provider}: query_sha256 mismatch`); | |
| } | |
| if (!SHA256_PATTERN.test(provider.policy_sha256)) { | |
| errors.push(`${provider.provider}: policy_sha256 is not a SHA-256 digest`); | |
| } else if (provider.policy_sha256 !== policySha256) { | |
| errors.push(`${provider.provider}: policy_sha256 mismatch`); | |
| } | |
| if (provider.source_count !== provider.sources.length) { | |
| errors.push(`${provider.provider}: source_count mismatch`); | |
| } | |
| if (provider.source_list_sha256 !== hashHex(provider.sources, "SHA-256")) { | |
| errors.push(`${provider.provider}: source_list_sha256 mismatch`); | |
| } | |
| if (provider.status !== "SUCCESS" && provider.sources.length > 0) { | |
| errors.push(`${provider.provider}: non-success status carries evidence`); | |
| } | |
| if ( | |
| provider.status === "SUCCESS" | |
| && provider.http_status !== undefined | |
| && (provider.http_status < 200 || provider.http_status >= 300) | |
| ) { | |
| errors.push(`${provider.provider}: success status conflicts with http_status`); | |
| } | |
| for (const [index, source] of provider.sources.entries()) { | |
| if (cleanSourceUrl(source.url) !== source.url) { | |
| errors.push(`${provider.provider}: source ${index} URL is not canonical`); | |
| } | |
| try { | |
| if (new URL(source.url).hostname !== source.domain) { | |
| errors.push(`${provider.provider}: source ${index} domain mismatch`); | |
| } | |
| } catch { | |
| errors.push(`${provider.provider}: source ${index} URL is invalid`); | |
| } | |
| if (source.title_sha256 !== undefined && !SHA256_PATTERN.test(source.title_sha256)) { | |
| errors.push(`${provider.provider}: source ${index} title_sha256 is invalid`); | |
| } | |
| } | |
| } | |
| for (const provider of expectedProviders) { | |
| if (!seenProviders.has(provider)) errors.push(`missing provider: ${provider}`); | |
| } | |
| const openai = providers.find((provider) => provider.provider === "openai"); | |
| const perplexity = providers.find((provider) => provider.provider === "perplexity"); | |
| const openaiUrls = new Set(openai?.sources.map((source) => source.url) ?? []); | |
| const perplexityUrls = new Set(perplexity?.sources.map((source) => source.url) ?? []); | |
| const openaiDomains = new Set(openai?.sources.map((source) => source.domain) ?? []); | |
| const perplexityDomains = new Set(perplexity?.sources.map((source) => source.domain) ?? []); | |
| const sourceUrlOverlapCount = intersectionSize(openaiUrls, perplexityUrls); | |
| const sourceDomainOverlapCount = intersectionSize(openaiDomains, perplexityDomains); | |
| const sourceUnion = new Set([...openaiUrls, ...perplexityUrls]); | |
| const successfulProviders = providers.filter((provider) => provider.status === "SUCCESS"); | |
| const evidenceProviders = successfulProviders.filter((provider) => provider.source_count > 0); | |
| let label: ResearchEvidenceLabel; | |
| if (errors.length > 0) { | |
| label = "INSUFFICIENT"; | |
| } else if (successfulProviders.length === 0) { | |
| label = "UNAVAILABLE"; | |
| } else if (evidenceProviders.length === 0 || evidenceProviders.length !== successfulProviders.length) { | |
| label = "INSUFFICIENT"; | |
| } else if (successfulProviders.length === 1) { | |
| label = "SINGLE_PROVIDER"; | |
| } else if (sourceUrlOverlapCount > 0) { | |
| label = "CORROBORATED"; | |
| } else { | |
| label = "DIVERGENT"; | |
| } | |
| return { | |
| schema_version: "a11oy.research_evidence_comparison/v0", | |
| label, | |
| query_sha256: querySha256, | |
| policy_sha256: policySha256, | |
| providers, | |
| successful_provider_count: successfulProviders.length, | |
| evidence_provider_count: evidenceProviders.length, | |
| source_union_count: sourceUnion.size, | |
| source_url_overlap_count: sourceUrlOverlapCount, | |
| source_domain_overlap_count: sourceDomainOverlapCount, | |
| source_url_jaccard: sourceUnion.size === 0 | |
| ? 0 | |
| : Number((sourceUrlOverlapCount / sourceUnion.size).toFixed(6)), | |
| integrity_valid: errors.length === 0, | |
| integrity_errors: errors.sort(), | |
| action_authorized: false, | |
| }; | |
| } | |
| export function emitTwoWitnessResearchReceipt( | |
| comparison: ResearchEvidenceComparison, | |
| options: TwoWitnessResearchReceiptOptions, | |
| ): OperationalReceipt { | |
| const verifiedComparison = compareResearchEvidence({ | |
| query_sha256: comparison.query_sha256, | |
| policy_sha256: comparison.policy_sha256, | |
| providers: comparison.providers, | |
| }); | |
| if (canonicalJson(verifiedComparison) !== canonicalJson(comparison)) { | |
| throw new Error("research evidence comparison verification failed"); | |
| } | |
| const payload = { | |
| schema_version: "a11oy.two_witness_research_receipt/v0", | |
| live_adapters_enabled: TWO_WITNESS_LIVE_ADAPTERS_ENABLED, | |
| evidence_class: "MODELED", | |
| signature_state: "UNSIGNED_LOCAL", | |
| external_attestation_state: "EXTERNAL_ATTESTATION_FALSE", | |
| external_attestation: false, | |
| action_authorization_state: "ACTION_AUTHORIZED_FALSE", | |
| action_authorized: false, | |
| query_sha256: comparison.query_sha256, | |
| policy_sha256: comparison.policy_sha256, | |
| label: comparison.label, | |
| integrity_valid: comparison.integrity_valid, | |
| integrity_errors: comparison.integrity_errors, | |
| successful_provider_count: comparison.successful_provider_count, | |
| evidence_provider_count: comparison.evidence_provider_count, | |
| source_union_count: comparison.source_union_count, | |
| source_url_overlap_count: comparison.source_url_overlap_count, | |
| source_domain_overlap_count: comparison.source_domain_overlap_count, | |
| source_url_jaccard: comparison.source_url_jaccard, | |
| providers: comparison.providers, | |
| }; | |
| const envelope = createToolEnvelope({ | |
| protocol: "a11oy", | |
| actor_id: options.actor_id, | |
| tool_name: "two_witness_research_compare_v0", | |
| tool_version: "0.1.0", | |
| invocation_id: options.invocation_id, | |
| lambda_axes: ["provenance", "restraint"], | |
| payload, | |
| metadata: { | |
| network_access: "DISABLED", | |
| provider_credentials: "NOT_ACCEPTED", | |
| live_adapter_feature_flag: "OFF", | |
| }, | |
| }); | |
| return emitReceipt(envelope, { | |
| previousReceipt: options.previousReceipt, | |
| policy: options.policy, | |
| quorumSignatures: options.quorumSignatures, | |
| timestamp: options.timestamp, | |
| sequence: options.sequence, | |
| eventType: "A11OY_OPERATION", | |
| }); | |
| } | |