@usekaval/kaval
Advanced tools
| /** Public REST request and response types for the current review-only Offer Search surface. */ | ||
| export type ProductIdentifierScheme = "gtin" | "upc" | "ean" | "isbn" | "mpn" | "manufacturer_sku" | "model"; | ||
| export interface ProductIdentifier { | ||
| scheme: ProductIdentifierScheme; | ||
| value: string; | ||
| issuer?: string; | ||
| } | ||
| export interface ProductAttribute { | ||
| key: string; | ||
| value: string | number | boolean; | ||
| unit?: string; | ||
| } | ||
| export interface PackSpec { | ||
| count: number; | ||
| units_per_item?: number; | ||
| unit?: string; | ||
| } | ||
| export interface ProductTarget { | ||
| schema_revision: number; | ||
| family?: { | ||
| brand?: string; | ||
| name?: string; | ||
| category?: string; | ||
| }; | ||
| name?: string; | ||
| identifiers: ProductIdentifier[]; | ||
| attributes: ProductAttribute[]; | ||
| pack?: PackSpec; | ||
| } | ||
| export interface ProductFamily { | ||
| schema_revision: number; | ||
| family_id: string; | ||
| brand: string; | ||
| name: string; | ||
| category?: string; | ||
| identifiers: ProductIdentifier[]; | ||
| } | ||
| export interface ProductVariant { | ||
| schema_revision: number; | ||
| variant_id: string; | ||
| family: ProductFamily; | ||
| name: string; | ||
| identifiers: ProductIdentifier[]; | ||
| attributes: ProductAttribute[]; | ||
| pack: PackSpec; | ||
| } | ||
| export type ProductCondition = "new" | "open_box" | "refurbished" | "used_like_new" | "used_good" | "used_acceptable" | "unknown"; | ||
| export type SellerKind = "brand_direct" | "authorized_retailer" | "marketplace" | "independent_retailer" | "unknown"; | ||
| interface SubstitutionBase { | ||
| rule_id: string; | ||
| rationale: string; | ||
| maximum_materiality: "low" | "medium" | "high" | "critical"; | ||
| } | ||
| export type PermittedSubstitution = (SubstitutionBase & { | ||
| kind: "attribute"; | ||
| key: string; | ||
| requested_value: string | number | boolean; | ||
| permitted_value: string | number | boolean; | ||
| requested_unit?: string; | ||
| permitted_unit?: string; | ||
| }) | (SubstitutionBase & { | ||
| kind: "pack"; | ||
| requested: PackSpec; | ||
| permitted: PackSpec; | ||
| }) | (SubstitutionBase & { | ||
| kind: "condition"; | ||
| requested: ProductCondition; | ||
| permitted: ProductCondition; | ||
| }) | (SubstitutionBase & { | ||
| kind: "variant"; | ||
| requested_identifiers: ProductIdentifier[]; | ||
| permitted_variant_id: string; | ||
| permitted_identifiers: ProductIdentifier[]; | ||
| }); | ||
| export interface OfferSearchInput { | ||
| schema_revision: number; | ||
| request_id: string; | ||
| raw_description: string; | ||
| target: ProductTarget; | ||
| requested_condition: ProductCondition; | ||
| destination: { | ||
| country_code: string; | ||
| region?: string; | ||
| postal_code?: string; | ||
| }; | ||
| match_policy: { | ||
| identity_requirement: "shared_identifier" | "shared_identifier_or_complete_attributes"; | ||
| required_identifier_schemes: ProductIdentifierScheme[]; | ||
| required_attribute_keys: string[]; | ||
| permitted_substitutions: PermittedSubstitution[]; | ||
| }; | ||
| seller_policy: { | ||
| allowed_seller_ids: string[]; | ||
| blocked_seller_ids: string[]; | ||
| allowed_kinds: SellerKind[]; | ||
| require_authorized: boolean; | ||
| }; | ||
| destination_policy: { | ||
| require_eligible: boolean; | ||
| require_exact_region: boolean; | ||
| require_exact_postal_code: boolean; | ||
| }; | ||
| price_policy: { | ||
| currency: string; | ||
| maximum_landed_total_minor?: number; | ||
| require_complete_landed_total: boolean; | ||
| allow_estimated_components: boolean; | ||
| allow_member_price: boolean; | ||
| allow_subscription_price: boolean; | ||
| allow_coupon_price: boolean; | ||
| allow_installment_display: boolean; | ||
| allow_trade_in_price: boolean; | ||
| }; | ||
| source_policy: { | ||
| allowed_source_ids: string[]; | ||
| blocked_source_ids: string[]; | ||
| require_origin_evidence: boolean; | ||
| }; | ||
| intended_action: { | ||
| description: string; | ||
| materiality: "low" | "medium" | "high" | "critical"; | ||
| reversibility: "reversible" | "partially_reversible" | "irreversible"; | ||
| }; | ||
| freshness_maximum_age_ms: number; | ||
| max_results: number; | ||
| minimum_unique_sellers: number; | ||
| deadline_ms: number; | ||
| maximum_cost_micro_usd: number; | ||
| maximum_search_calls: number; | ||
| maximum_fetches: number; | ||
| } | ||
| export interface Money { | ||
| amount_minor: number; | ||
| currency: string; | ||
| } | ||
| export interface ExtractedOriginOffer { | ||
| evidence_kind: "json_ld" | "embedded_product_json" | "product_meta"; | ||
| source_block_index: number; | ||
| jsonld_product_index: number; | ||
| jsonld_offer_index: number | null; | ||
| variant: ProductVariant; | ||
| title: string; | ||
| purchase_url: string; | ||
| seller_name: string | null; | ||
| condition: ProductCondition; | ||
| availability: "in_stock" | "out_of_stock" | "preorder" | "unknown"; | ||
| item_price: Money | null; | ||
| destination_eligibility: "unknown"; | ||
| landed_price_complete: false; | ||
| extraction_gaps: string[]; | ||
| } | ||
| export type OfferConflictCode = "FAMILY_BRAND_CONFLICT" | "FAMILY_NAME_CONFLICT" | "IDENTIFIER_CONFLICT" | "IDENTIFIER_AMBIGUOUS" | "IDENTIFIER_MISSING" | "ATTRIBUTE_CONFLICT" | "ATTRIBUTE_MISSING" | "PACK_CONFLICT" | "PACK_INCOMPLETE" | "CONDITION_CONFLICT" | "SELLER_BLOCKED" | "SELLER_NOT_ALLOWED" | "SELLER_KIND_NOT_ALLOWED" | "SELLER_AUTHORIZATION_REQUIRED" | "DESTINATION_CONFLICT" | "DESTINATION_INELIGIBLE" | "DESTINATION_UNKNOWN" | "CURRENCY_CONFLICT" | "PRICE_LIMIT_EXCEEDED" | "PRICE_INCOMPLETE" | "MATERIAL_EVIDENCE_MISSING" | "OBSERVATION_EXPIRED"; | ||
| export interface OfferMatchAssessment { | ||
| state: "exact" | "permitted_substitute" | "ambiguous" | "conflict" | "insufficient_identity"; | ||
| conflict_codes: OfferConflictCode[]; | ||
| matched_identifier_schemes: ProductIdentifierScheme[]; | ||
| matched_attribute_keys: string[]; | ||
| applied_substitutions: PermittedSubstitution[]; | ||
| explanation: string; | ||
| } | ||
| export interface LiveOfferSearchCandidate { | ||
| candidate_id: `sha256:${string}`; | ||
| origin_url: string; | ||
| source_id: string; | ||
| discovered_by: string[]; | ||
| discovery_metadata: Array<{ | ||
| provider: string; | ||
| title: string | null; | ||
| }>; | ||
| origin_evidence: { | ||
| kind: ExtractedOriginOffer["evidence_kind"]; | ||
| content_digest: `sha256:${string}`; | ||
| source_block_index: number; | ||
| jsonld_product_index: number; | ||
| jsonld_offer_index: number | null; | ||
| }; | ||
| origin_offer: ExtractedOriginOffer; | ||
| identity: OfferMatchAssessment; | ||
| /** Current shadow output can only be queued for review or rejected. */ | ||
| disposition: "review" | "rejected"; | ||
| gaps: string[]; | ||
| reason_codes: string[]; | ||
| /** Destination-aware checkout evidence. Its action remains REVIEW-only. */ | ||
| checkout?: CommerceCheckoutVerification; | ||
| } | ||
| export type CommerceSourceFamily = "catalog" | "merchant_feed" | "retailer_origin" | "shopping_search" | "open_web"; | ||
| export interface CommerceCheckoutResolverDescriptor { | ||
| schema_revision: 1; | ||
| source_id: string; | ||
| adapter_revision: string; | ||
| execution_mode: "recorded_fixture" | "live"; | ||
| estimated_cost_micro_usd: number; | ||
| } | ||
| export interface CommerceCheckoutObservation { | ||
| destination_eligibility: "eligible" | "ineligible" | "unknown"; | ||
| availability: "in_stock" | "out_of_stock" | "preorder" | "unknown"; | ||
| seller_authorized: boolean | null; | ||
| item_price: Money | null; | ||
| shipping_price: Money | null; | ||
| tax_price: Money | null; | ||
| mandatory_fees: Money | null; | ||
| declared_landed_total: Money | null; | ||
| quote_id: string | null; | ||
| evidence_digest: `sha256:${string}`; | ||
| observed_at: string; | ||
| expires_at: string; | ||
| } | ||
| export type LandedPriceValidationReason = "EXPECTED_CURRENCY_INVALID" | "ITEM_PRICE_MISSING" | "SHIPPING_PRICE_MISSING" | "TAX_PRICE_MISSING" | "MANDATORY_FEES_MISSING" | "DECLARED_LANDED_TOTAL_MISSING" | "MONEY_VALUE_INVALID" | "PRICE_CURRENCY_CONFLICT" | "LANDED_TOTAL_OVERFLOW" | "LANDED_TOTAL_ARITHMETIC_MISMATCH"; | ||
| export interface LandedPriceValidation { | ||
| state: "complete" | "incomplete" | "invalid" | "inconsistent"; | ||
| expected_currency: string; | ||
| calculated_landed_total: Money | null; | ||
| reason_codes: LandedPriceValidationReason[]; | ||
| } | ||
| export interface CommerceCheckoutVerification { | ||
| status: "verified" | "review_required" | "rejected" | "operational_failure"; | ||
| resolver: CommerceCheckoutResolverDescriptor | null; | ||
| request_digest: `sha256:${string}`; | ||
| observation: CommerceCheckoutObservation | null; | ||
| landed_price_validation: LandedPriceValidation; | ||
| action: { | ||
| state: "REVIEW"; | ||
| action_authorized: false; | ||
| reason_codes: string[]; | ||
| }; | ||
| actual_cost_micro_usd: number; | ||
| version_receipt: string | null; | ||
| operational_error_code: "UPSTREAM_UNAVAILABLE" | "DESTINATION_UNSUPPORTED" | "MALFORMED_RESPONSE" | "RIGHTS_REVOKED" | "CANCELLED" | null; | ||
| } | ||
| export interface CommercePlannedSource { | ||
| source_id: string; | ||
| family: CommerceSourceFamily; | ||
| call_kind: "search" | "fetch"; | ||
| independence_group: string; | ||
| estimated_cost_micro_usd: number; | ||
| field_guarantees: string[]; | ||
| health_state: "healthy" | "degraded"; | ||
| concurrency_limit: number; | ||
| supports_cancellation: boolean; | ||
| role: "structured_acquisition" | "origin_verification" | "discovery_tail"; | ||
| winner_must_be_origin_verified: boolean; | ||
| } | ||
| export interface CommerceSourcePlan { | ||
| schema_revision: number; | ||
| request_id: string; | ||
| request_digest: `sha256:${string}`; | ||
| supplier_registry_schema_revision: number; | ||
| supplier_registry_digest: `sha256:${string}`; | ||
| waves: Array<{ | ||
| wave: number; | ||
| purpose: "structured_authoritative" | "retailer_origin" | "unresolved_identity_and_coverage"; | ||
| sources: CommercePlannedSource[]; | ||
| }>; | ||
| receipt: { | ||
| schema_revision: number; | ||
| request_id: string; | ||
| coverage_claim: "bounded_not_comprehensive"; | ||
| name_only_target: boolean; | ||
| minimum_independent_families_required: number; | ||
| planned_independent_families: CommerceSourceFamily[]; | ||
| planned_independence_groups: string[]; | ||
| independence_requirement_met: boolean; | ||
| origin_verification_required: true; | ||
| origin_verification_planned: boolean; | ||
| origin_verification_source_ids: string[]; | ||
| eligible_supplier_count_before_budget: number; | ||
| total_planned_cost_micro_usd: number; | ||
| total_planned_search_calls: number; | ||
| total_planned_fetches: number; | ||
| exclusions: Array<{ | ||
| source_id: string; | ||
| family: CommerceSourceFamily; | ||
| call_kind: "search" | "fetch"; | ||
| estimated_cost_micro_usd: number; | ||
| reason: string; | ||
| }>; | ||
| }; | ||
| } | ||
| export interface CommerceAcquisitionSourceLedgerEntry { | ||
| source_id: string; | ||
| family: CommerceSourceFamily; | ||
| disposition: "succeeded" | "failed" | "cancelled" | "prohibited" | "deferred" | "unsearched"; | ||
| reason_code: string; | ||
| } | ||
| export interface CommerceAcquisitionRunReport { | ||
| schema_revision: 1; | ||
| request_digest: `sha256:${string}`; | ||
| plan: CommerceSourcePlan; | ||
| /** Full planner state is retained for audit/replay and may add fields within schema revision 1. */ | ||
| state: Readonly<Record<string, unknown>>; | ||
| stop: { | ||
| reason: Exclude<OfferSearchStopReason, "sufficient_offers">; | ||
| explanation: string; | ||
| }; | ||
| calls: Array<Readonly<Record<string, unknown>>>; | ||
| records: Array<Readonly<Record<string, unknown>>>; | ||
| source_ledger: CommerceAcquisitionSourceLedgerEntry[]; | ||
| coverage: { | ||
| claim: "bounded_not_comprehensive"; | ||
| attempted_source_families: CommerceSourceFamily[]; | ||
| unique_candidate_keys: number; | ||
| unique_sellers: number; | ||
| unsearched_source_count: number; | ||
| prohibited_source_count: number; | ||
| failed_source_count: number; | ||
| }; | ||
| deduplication: { | ||
| source_records: number; | ||
| unique_urls: number; | ||
| unique_variants: number; | ||
| unique_sellers: number; | ||
| unique_listings: number; | ||
| unique_offers: number; | ||
| independent_information_origins: number; | ||
| }; | ||
| replay_digest: `sha256:${string}`; | ||
| } | ||
| export interface LiveOfferSearchAcquisitionTrace { | ||
| coverage_claim: "bounded_not_comprehensive"; | ||
| plan: CommerceSourcePlan; | ||
| plan_digest: `sha256:${string}`; | ||
| source_ledger: CommerceAcquisitionSourceLedgerEntry[]; | ||
| adapter_run?: CommerceAcquisitionRunReport; | ||
| } | ||
| /** Digests that bind one persisted evidence generation to one exact downstream action slot. */ | ||
| export interface CommerceActionBinding { | ||
| action_slot_key: string; | ||
| action_input_digest: `sha256:${string}`; | ||
| action_consequence_digest: `sha256:${string}`; | ||
| } | ||
| export type CommerceActionTimeGateState = "current_review_only" | "not_found" | "stale_generation" | "binding_mismatch" | "expired" | "invalidated" | "refresh_required" | "source_revoked" | "retention_unavailable" | "integrity_failed" | "operational_failure"; | ||
| /** Exact body accepted by POST /v1/search-offers/gate. Tenant identity is server-derived. */ | ||
| export interface CommerceActionTimeGateInput { | ||
| dependency_id: string; | ||
| generation_id: string; | ||
| generation_number: number; | ||
| generation_digest: `sha256:${string}`; | ||
| action_binding: CommerceActionBinding; | ||
| } | ||
| /** | ||
| * A final-fence read of one persisted offer generation. Commerce remains review-only: even a | ||
| * current generation returns REVIEW with permission withheld. | ||
| */ | ||
| export interface CommerceActionTimeGateResult { | ||
| state: CommerceActionTimeGateState; | ||
| disposition: "REVIEW"; | ||
| permission: "withheld"; | ||
| reason_codes: string[]; | ||
| checked_at: string; | ||
| final_fence_checked: boolean; | ||
| generation_id?: string; | ||
| generation_number?: number; | ||
| generation_digest?: `sha256:${string}`; | ||
| expires_at?: string; | ||
| } | ||
| export type CommerceOfferSearchLifecycle = { | ||
| persistence: "persisted"; | ||
| dependency_id: string; | ||
| generation_id: string; | ||
| generation_number: number; | ||
| generation_digest: `sha256:${string}`; | ||
| selected_candidate_id: `sha256:${string}`; | ||
| expires_at: string; | ||
| action_binding: CommerceActionBinding; | ||
| action_time_gate: CommerceActionTimeGateResult; | ||
| } | { | ||
| persistence: "not_created"; | ||
| reason_codes: string[]; | ||
| action_time_gate: Pick<CommerceActionTimeGateResult, "disposition" | "permission" | "reason_codes" | "checked_at" | "final_fence_checked"> & { | ||
| state: "not_found"; | ||
| }; | ||
| }; | ||
| export type CommerceSourceAttemptErrorCode = "INVALID_DISCOVERY_URL" | "DISCOVERY_IDENTIFIER_MISMATCH" | "ORIGIN_BLOCKED" | "ORIGIN_HTTP_ERROR" | "ORIGIN_JSONLD_INVALID" | "ORIGIN_TIMEOUT" | "ORIGIN_UNAVAILABLE" | "SEARCH_UNAVAILABLE" | "BUDGET_EXHAUSTED" | "DEADLINE_REACHED" | "CANCELLED" | "COVERAGE_SATISFIED"; | ||
| export interface CommerceLiveSourceAttempt { | ||
| sequence: number; | ||
| kind: "search" | "origin_fetch"; | ||
| call_attempted: boolean; | ||
| source_id: string; | ||
| provider: string | null; | ||
| query: string | null; | ||
| url: string | null; | ||
| outcome: "succeeded" | "empty" | "failed" | "blocked" | "skipped" | "cancelled"; | ||
| error_code: CommerceSourceAttemptErrorCode | null; | ||
| latency_ms: number; | ||
| cost_micro_usd: number; | ||
| reuse: "executed" | "tenant_private_cache"; | ||
| avoided_cost_micro_usd: number; | ||
| result_count: number | null; | ||
| http_status: number | null; | ||
| bytes_received: number | null; | ||
| } | ||
| export type OfferSearchStopReason = "coverage_satisfied" | "sufficient_offers" | "source_exhausted" | "budget_exhausted" | "deadline_reached" | "cancelled" | "upstream_unavailable" | "policy_blocked"; | ||
| export interface LiveOfferSearchResult { | ||
| schema_revision: 2; | ||
| request_id: string; | ||
| request_digest: `sha256:${string}`; | ||
| status: "complete" | "partial" | "failed"; | ||
| /** Offer Search is shadow-only and cannot authorize a quote or purchase. */ | ||
| action: { | ||
| state: "NEEDS_REVIEW" | "NO_RELIABLE_OFFER"; | ||
| reason_codes: string[]; | ||
| }; | ||
| stop_reason: OfferSearchStopReason; | ||
| query: string | null; | ||
| candidates: LiveOfferSearchCandidate[]; | ||
| source_attempts: CommerceLiveSourceAttempt[]; | ||
| receipt: { | ||
| search_calls: number; | ||
| fetch_calls: number; | ||
| providers_configured: number; | ||
| providers_succeeded: number; | ||
| cost_micro_usd: number; | ||
| cost_basis: "reserved_ceiling"; | ||
| provider_estimated_cost_micro_usd: number | null; | ||
| provider_estimated_cost_reported_search_calls: number; | ||
| discovery_cache_hits: number; | ||
| cost_avoided_micro_usd: number; | ||
| elapsed_ms: number; | ||
| }; | ||
| started_at: string; | ||
| completed_at: string; | ||
| /** Auditable rights, coverage, and attempted-source trace. */ | ||
| acquisition?: LiveOfferSearchAcquisitionTrace; | ||
| /** Present only when the hosted server has a configured durable commerce lifecycle. */ | ||
| lifecycle?: CommerceOfferSearchLifecycle; | ||
| } | ||
| export type OfferSearchProgressStage = "accepted" | "acquisition" | "verification" | "coverage" | "candidate_provisional" | "candidate" | "warning"; | ||
| interface OfferSearchProgressEventBase { | ||
| sequence: number; | ||
| at: string; | ||
| request_id: string; | ||
| message: string; | ||
| authority: "research_only"; | ||
| action_state: "REVIEW"; | ||
| details: Readonly<Record<string, unknown>>; | ||
| } | ||
| export interface OfferSearchStageEvent extends OfferSearchProgressEventBase { | ||
| type: Exclude<OfferSearchProgressStage, "candidate_provisional">; | ||
| } | ||
| /** Origin-verified research observed before final selection and lifecycle persistence. */ | ||
| export interface OfferSearchProvisionalCandidateEvent extends OfferSearchProgressEventBase { | ||
| type: "candidate_provisional"; | ||
| details: Readonly<{ | ||
| request_digest: `sha256:${string}`; | ||
| origin_sequence: number; | ||
| publication_state: "provisional"; | ||
| durable: false; | ||
| actionable: false; | ||
| permission: "withheld"; | ||
| final_inclusion: "not_yet_determined"; | ||
| candidate: LiveOfferSearchCandidate; | ||
| }>; | ||
| } | ||
| export type OfferSearchProgressEvent = OfferSearchStageEvent | OfferSearchProvisionalCandidateEvent; | ||
| /** A same-key completed-operation replay performs no new provider work. */ | ||
| export interface OfferSearchReplayEvent { | ||
| type: "replay"; | ||
| sequence: number; | ||
| replayed_at: string; | ||
| request_id: string; | ||
| request_digest: `sha256:${string}`; | ||
| authority: "research_only"; | ||
| action_state: "REVIEW"; | ||
| } | ||
| export type OfferSearchStreamEvent = OfferSearchProgressEvent | OfferSearchReplayEvent | { | ||
| type: "final"; | ||
| sequence: number; | ||
| result: LiveOfferSearchResult; | ||
| }; | ||
| /** Validate a public progressive event before exposing it to an agent. */ | ||
| export declare function reviewOnlyOfferSearchProgressEvent(value: unknown): OfferSearchProgressEvent; | ||
| /** Validate the content-free event emitted for a durable same-key replay. */ | ||
| export declare function reviewOnlyOfferSearchReplayEvent(value: unknown, expectedRequestId?: string): OfferSearchReplayEvent; | ||
| /** Reject authority drift and validate the exact public action-time commerce gate response. */ | ||
| export declare function reviewOnlyCommerceActionTimeGateResult(value: unknown, expectedGeneration?: Pick<CommerceActionTimeGateInput, "generation_id" | "generation_number" | "generation_digest">): CommerceActionTimeGateResult; | ||
| /** Reject a drifted commerce response before a caller can mistake shadow research for permission. */ | ||
| export declare function reviewOnlyOfferSearchResult(value: unknown, expectedRequestId?: string): LiveOfferSearchResult; | ||
| export {}; |
| /** Public REST request and response types for the current review-only Offer Search surface. */ | ||
| function record(value) { | ||
| return typeof value === "object" && value !== null && !Array.isArray(value) | ||
| ? value | ||
| : null; | ||
| } | ||
| const COMMERCE_DIGEST = /^sha256:[0-9a-f]{64}$/u; | ||
| const ACTION_TIME_GATE_STATES = new Set([ | ||
| "current_review_only", | ||
| "not_found", | ||
| "stale_generation", | ||
| "binding_mismatch", | ||
| "expired", | ||
| "invalidated", | ||
| "refresh_required", | ||
| "source_revoked", | ||
| "retention_unavailable", | ||
| "integrity_failed", | ||
| "operational_failure", | ||
| ]); | ||
| function stringArray(value) { | ||
| return (Array.isArray(value) && value.every((item) => typeof item === "string")); | ||
| } | ||
| function digest(value) { | ||
| return typeof value === "string" && COMMERCE_DIGEST.test(value); | ||
| } | ||
| function actionBinding(value) { | ||
| const binding = record(value); | ||
| return (typeof binding?.["action_slot_key"] === "string" && | ||
| binding["action_slot_key"].length > 0 && | ||
| digest(binding["action_input_digest"]) && | ||
| digest(binding["action_consequence_digest"]) && | ||
| binding["action_input_digest"] !== binding["action_consequence_digest"]); | ||
| } | ||
| /** Detect permission-shaped fields anywhere in a commerce response, including future extensions. */ | ||
| function containsCommerceAuthority(value) { | ||
| if (Array.isArray(value)) | ||
| return value.some(containsCommerceAuthority); | ||
| const current = record(value); | ||
| if (!current) | ||
| return false; | ||
| for (const [key, nested] of Object.entries(current)) { | ||
| const authorityToken = typeof nested === "string" ? nested.toUpperCase() : undefined; | ||
| if (((key === "safe_to_quote" || | ||
| key === "action_authorized" || | ||
| key === "execution_allowed" || | ||
| key === "executionAllowed" || | ||
| key === "act") && | ||
| nested === true) || | ||
| (key === "permission" && nested !== "withheld") || | ||
| ((key === "decision" || key === "disposition" || key === "state") && | ||
| (authorityToken === "ALLOW" || | ||
| authorityToken === "BLOCK" || | ||
| authorityToken === "SAFE_TO_QUOTE")) || | ||
| containsCommerceAuthority(nested)) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| const OFFER_SEARCH_PROGRESS_STAGES = new Set([ | ||
| "accepted", | ||
| "acquisition", | ||
| "verification", | ||
| "coverage", | ||
| "candidate_provisional", | ||
| "candidate", | ||
| "warning", | ||
| ]); | ||
| /** Validate a public progressive event before exposing it to an agent. */ | ||
| export function reviewOnlyOfferSearchProgressEvent(value) { | ||
| const event = record(value); | ||
| if (!event || | ||
| !OFFER_SEARCH_PROGRESS_STAGES.has(event["type"]) || | ||
| !Number.isInteger(event["sequence"]) || | ||
| event["sequence"] < 0 || | ||
| typeof event["at"] !== "string" || | ||
| typeof event["request_id"] !== "string" || | ||
| typeof event["message"] !== "string" || | ||
| event["authority"] !== "research_only" || | ||
| event["action_state"] !== "REVIEW" || | ||
| record(event["details"]) === null || | ||
| containsCommerceAuthority(event)) { | ||
| throw new TypeError("Offer Search stream returned an invalid or authority-bearing progress event"); | ||
| } | ||
| if (event["type"] === "candidate_provisional") { | ||
| const details = record(event["details"]); | ||
| const candidate = record(details?.["candidate"]); | ||
| if (!details || | ||
| !digest(details["request_digest"]) || | ||
| !Number.isInteger(details["origin_sequence"]) || | ||
| details["origin_sequence"] < 0 || | ||
| details["publication_state"] !== "provisional" || | ||
| details["durable"] !== false || | ||
| details["actionable"] !== false || | ||
| details["permission"] !== "withheld" || | ||
| details["final_inclusion"] !== "not_yet_determined" || | ||
| !candidate || | ||
| !digest(candidate["candidate_id"]) || | ||
| typeof candidate["origin_url"] !== "string" || | ||
| typeof candidate["source_id"] !== "string" || | ||
| (candidate["disposition"] !== "review" && | ||
| candidate["disposition"] !== "rejected")) { | ||
| throw new TypeError("Offer Search stream returned an invalid provisional candidate event"); | ||
| } | ||
| } | ||
| return value; | ||
| } | ||
| /** Validate the content-free event emitted for a durable same-key replay. */ | ||
| export function reviewOnlyOfferSearchReplayEvent(value, expectedRequestId) { | ||
| const event = record(value); | ||
| if (!event || | ||
| event["type"] !== "replay" || | ||
| !Number.isInteger(event["sequence"]) || | ||
| event["sequence"] < 0 || | ||
| typeof event["replayed_at"] !== "string" || | ||
| typeof event["request_id"] !== "string" || | ||
| event["request_id"].length === 0 || | ||
| !digest(event["request_digest"]) || | ||
| (expectedRequestId !== undefined && | ||
| event["request_id"] !== expectedRequestId) || | ||
| event["authority"] !== "research_only" || | ||
| event["action_state"] !== "REVIEW" || | ||
| containsCommerceAuthority(event)) { | ||
| throw new TypeError("Offer Search stream returned an invalid or authority-bearing replay event"); | ||
| } | ||
| return value; | ||
| } | ||
| /** Reject authority drift and validate the exact public action-time commerce gate response. */ | ||
| export function reviewOnlyCommerceActionTimeGateResult(value, expectedGeneration) { | ||
| const gate = record(value); | ||
| if (!gate || | ||
| !ACTION_TIME_GATE_STATES.has(gate["state"]) || | ||
| gate["disposition"] !== "REVIEW" || | ||
| gate["permission"] !== "withheld" || | ||
| !stringArray(gate["reason_codes"]) || | ||
| typeof gate["checked_at"] !== "string" || | ||
| typeof gate["final_fence_checked"] !== "boolean" || | ||
| (gate["generation_id"] !== undefined && | ||
| typeof gate["generation_id"] !== "string") || | ||
| (gate["generation_number"] !== undefined && | ||
| (!Number.isInteger(gate["generation_number"]) || | ||
| gate["generation_number"] <= 0)) || | ||
| (gate["generation_digest"] !== undefined && | ||
| !digest(gate["generation_digest"])) || | ||
| (gate["expires_at"] !== undefined && | ||
| typeof gate["expires_at"] !== "string") || | ||
| containsCommerceAuthority(gate)) { | ||
| throw new TypeError("Offer Search action-time gate returned an invalid or authority-bearing response; commerce permission must remain withheld"); | ||
| } | ||
| if (gate["state"] === "current_review_only" && | ||
| (gate["final_fence_checked"] !== true || | ||
| typeof gate["generation_id"] !== "string" || | ||
| gate["generation_id"].length === 0 || | ||
| !Number.isInteger(gate["generation_number"]) || | ||
| gate["generation_number"] <= 0 || | ||
| !digest(gate["generation_digest"]) || | ||
| (expectedGeneration !== undefined && | ||
| (gate["generation_id"] !== expectedGeneration.generation_id || | ||
| gate["generation_number"] !== expectedGeneration.generation_number || | ||
| gate["generation_digest"] !== expectedGeneration.generation_digest)))) { | ||
| throw new TypeError("Offer Search action-time gate returned an invalid or authority-bearing response; commerce permission must remain withheld"); | ||
| } | ||
| return value; | ||
| } | ||
| function commerceLifecycle(value, candidates) { | ||
| const lifecycle = record(value); | ||
| if (lifecycle?.["persistence"] === "persisted") { | ||
| if (typeof lifecycle["dependency_id"] !== "string" || | ||
| typeof lifecycle["generation_id"] !== "string" || | ||
| !Number.isInteger(lifecycle["generation_number"]) || | ||
| lifecycle["generation_number"] <= 0 || | ||
| !digest(lifecycle["generation_digest"]) || | ||
| !digest(lifecycle["selected_candidate_id"]) || | ||
| typeof lifecycle["expires_at"] !== "string" || | ||
| !actionBinding(lifecycle["action_binding"])) { | ||
| throw new TypeError("Offer Search returned invalid lifecycle metadata"); | ||
| } | ||
| const selectedCandidateMatches = candidates.filter((candidate) => record(candidate)?.["candidate_id"] === | ||
| lifecycle["selected_candidate_id"]).length; | ||
| if (selectedCandidateMatches !== 1) { | ||
| throw new TypeError("Offer Search returned invalid lifecycle metadata"); | ||
| } | ||
| const expectedGeneration = { | ||
| generation_id: lifecycle["generation_id"], | ||
| generation_number: lifecycle["generation_number"], | ||
| generation_digest: lifecycle["generation_digest"], | ||
| }; | ||
| const gate = reviewOnlyCommerceActionTimeGateResult(lifecycle["action_time_gate"], expectedGeneration); | ||
| if ((gate.generation_id !== undefined && | ||
| gate.generation_id !== expectedGeneration.generation_id) || | ||
| (gate.generation_number !== undefined && | ||
| gate.generation_number !== expectedGeneration.generation_number) || | ||
| (gate.generation_digest !== undefined && | ||
| gate.generation_digest !== expectedGeneration.generation_digest)) { | ||
| throw new TypeError("Offer Search returned invalid lifecycle metadata"); | ||
| } | ||
| return value; | ||
| } | ||
| if (lifecycle?.["persistence"] === "not_created" && | ||
| stringArray(lifecycle["reason_codes"])) { | ||
| const gate = reviewOnlyCommerceActionTimeGateResult(lifecycle["action_time_gate"]); | ||
| if (gate.state === "not_found") { | ||
| return value; | ||
| } | ||
| } | ||
| throw new TypeError("Offer Search returned invalid lifecycle metadata"); | ||
| } | ||
| /** Reject a drifted commerce response before a caller can mistake shadow research for permission. */ | ||
| export function reviewOnlyOfferSearchResult(value, expectedRequestId) { | ||
| const result = record(value); | ||
| if (typeof result?.["request_id"] !== "string" || | ||
| result["request_id"].length === 0 || | ||
| !digest(result["request_digest"])) { | ||
| throw new TypeError("Offer Search returned an invalid request ID or digest binding"); | ||
| } | ||
| if (expectedRequestId !== undefined && | ||
| result["request_id"] !== expectedRequestId) { | ||
| throw new TypeError("Offer Search result is bound to another request"); | ||
| } | ||
| const action = record(result?.["action"]); | ||
| const candidates = result?.["candidates"]; | ||
| if (result?.["schema_revision"] !== 2 || | ||
| result?.["decision"] === "ALLOW" || | ||
| result?.["safe_to_quote"] === true || | ||
| (action?.["state"] !== "NEEDS_REVIEW" && | ||
| action?.["state"] !== "NO_RELIABLE_OFFER") || | ||
| action?.["decision"] === "ALLOW" || | ||
| action?.["safe_to_quote"] === true || | ||
| containsCommerceAuthority(result) || | ||
| !Array.isArray(candidates) || | ||
| candidates.some((candidate) => { | ||
| const candidateRecord = record(candidate); | ||
| const disposition = candidateRecord?.["disposition"]; | ||
| return ((disposition !== "review" && disposition !== "rejected") || | ||
| candidateRecord?.["safe_to_quote"] === true); | ||
| })) { | ||
| throw new TypeError("Offer Search returned a non-review-only response; shadow results cannot authorize an action"); | ||
| } | ||
| if (result["lifecycle"] !== undefined) { | ||
| commerceLifecycle(result["lifecycle"], candidates); | ||
| } | ||
| return value; | ||
| } |
+13
-2
| /** | ||
| * @usekaval/kaval — the freshness gate for AI. A typed, dependency-light HTTP client for the kaval API. | ||
| * @usekaval/kaval — the evidence gate for AI agents. A typed, dependency-light HTTP client for the Kaval API. | ||
| * Mirrors the Python SDK (`pip install kaval`). Uses the global `fetch` (Node 18+, browsers, edge). | ||
| */ | ||
| import type { AuditInput, ProofGateInput, ProofGateResult, ProofPacket } from "./proof.js"; | ||
| import { type CommerceActionTimeGateInput, type CommerceActionTimeGateResult, type LiveOfferSearchResult, type OfferSearchInput, type OfferSearchStreamEvent } from "./offer-search.js"; | ||
| export type * from "./proof.js"; | ||
| export type * from "./offer-search.js"; | ||
| export type VerdictStatus = "current" | "stale" | "contradicted" | "unsupported" | "conflicting" | "insufficient"; | ||
@@ -159,3 +161,3 @@ /** Speed/depth tier for a verify() call. */ | ||
| } | ||
| /** The kaval client: a belief your system holds in, a typed freshness verdict out. */ | ||
| /** The Kaval client: evidence in, an action-bound decision or review-only research result out. */ | ||
| export declare class Kaval { | ||
@@ -185,2 +187,11 @@ private readonly base; | ||
| monitor(input: MonitorInput, options?: RequestOptions): Promise<MonitorResult>; | ||
| /** Search the accessible configured web for exact or possible offers. Current results are | ||
| * research-only: action.state is NEEDS_REVIEW or NO_RELIABLE_OFFER, never permission to quote. */ | ||
| searchOffers(input: OfferSearchInput, options?: RequestOptions): Promise<LiveOfferSearchResult>; | ||
| /** Stream bounded, review-only acquisition progress followed by one canonical final result. | ||
| * Cancellation closes the response body and propagates to the hosted acquisition operation. */ | ||
| streamOfferSearch(input: OfferSearchInput, options?: RequestOptions): AsyncGenerator<OfferSearchStreamEvent, LiveOfferSearchResult, void>; | ||
| /** Re-read one persisted offer generation at the exact action boundary. This final fence always | ||
| * returns REVIEW with commerce permission withheld; it never authorizes quoting or purchasing. */ | ||
| gateOfferSearch(input: CommerceActionTimeGateInput, options?: Pick<RequestOptions, "signal" | "timeoutMs">): Promise<CommerceActionTimeGateResult>; | ||
| /** Build, sign, and persist a complete action-bound proof packet. */ | ||
@@ -187,0 +198,0 @@ audit(input: AuditInput, options?: RequestOptions): Promise<ProofPacket>; |
+202
-13
| /** | ||
| * @usekaval/kaval — the freshness gate for AI. A typed, dependency-light HTTP client for the kaval API. | ||
| * @usekaval/kaval — the evidence gate for AI agents. A typed, dependency-light HTTP client for the Kaval API. | ||
| * Mirrors the Python SDK (`pip install kaval`). Uses the global `fetch` (Node 18+, browsers, edge). | ||
| */ | ||
| import { reviewOnlyCommerceActionTimeGateResult, reviewOnlyOfferSearchProgressEvent, reviewOnlyOfferSearchReplayEvent, reviewOnlyOfferSearchResult, } from "./offer-search.js"; | ||
| /** Thrown on any non-2xx response. */ | ||
@@ -104,3 +105,3 @@ export class KavalError extends Error { | ||
| } | ||
| /** The kaval client: a belief your system holds in, a typed freshness verdict out. */ | ||
| /** The Kaval client: evidence in, an action-bound decision or review-only research result out. */ | ||
| export class Kaval { | ||
@@ -176,13 +177,20 @@ base; | ||
| } | ||
| async post(path, body) { | ||
| const res = await this.f(`${this.base}${path}`, { | ||
| method: "POST", | ||
| headers: this.headers, | ||
| // JSON.stringify omits `undefined` keys, so optional params drop out automatically. | ||
| body: JSON.stringify(body), | ||
| }); | ||
| const payload = await res.json().catch(() => null); | ||
| if (!res.ok) | ||
| throw new KavalError(res.status, payload); | ||
| return payload; | ||
| async post(path, body, options = {}) { | ||
| const request = requestSignal(options.signal, options.timeoutMs === undefined ? this.timeoutMs : options.timeoutMs); | ||
| try { | ||
| const res = await this.f(`${this.base}${path}`, { | ||
| method: "POST", | ||
| headers: this.headers, | ||
| signal: request.signal, | ||
| // JSON.stringify omits `undefined` keys, so optional params drop out automatically. | ||
| body: JSON.stringify(body), | ||
| }); | ||
| const payload = await res.json().catch(() => null); | ||
| if (!res.ok) | ||
| throw new KavalError(res.status, payload); | ||
| return payload; | ||
| } | ||
| finally { | ||
| request.cleanup(); | ||
| } | ||
| } | ||
@@ -209,2 +217,183 @@ /** Pre-action gate: the verdict plus `act`. Treat `act === false` as "re-fetch before relying on it". */ | ||
| } | ||
| /** Search the accessible configured web for exact or possible offers. Current results are | ||
| * research-only: action.state is NEEDS_REVIEW or NO_RELIABLE_OFFER, never permission to quote. */ | ||
| async searchOffers(input, options) { | ||
| const result = await this.billablePost("/v1/search-offers", input, options); | ||
| return reviewOnlyOfferSearchResult(result, input.request_id); | ||
| } | ||
| /** Stream bounded, review-only acquisition progress followed by one canonical final result. | ||
| * Cancellation closes the response body and propagates to the hosted acquisition operation. */ | ||
| async *streamOfferSearch(input, options = {}) { | ||
| const idempotencyKey = options.idempotencyKey ?? generatedIdempotencyKey(); | ||
| const headers = { | ||
| ...this.headers, | ||
| accept: "text/event-stream", | ||
| "idempotency-key": idempotencyKey, | ||
| }; | ||
| const request = requestSignal(options.signal, options.timeoutMs === undefined ? this.timeoutMs : options.timeoutMs); | ||
| let response; | ||
| let reader; | ||
| try { | ||
| for (let attempt = 0; attempt < MAX_BILLABLE_ATTEMPTS; attempt += 1) { | ||
| try { | ||
| response = await this.f(`${this.base}/v1/search-offers`, { | ||
| method: "POST", | ||
| headers, | ||
| signal: request.signal, | ||
| body: JSON.stringify(input), | ||
| }); | ||
| } | ||
| catch (error) { | ||
| if (request.signal?.aborted || attempt + 1 >= MAX_BILLABLE_ATTEMPTS) { | ||
| throw attachIdempotencyKey(error, idempotencyKey); | ||
| } | ||
| continue; | ||
| } | ||
| if (response.ok) | ||
| break; | ||
| const responseText = await response.text(); | ||
| let payload = responseText; | ||
| try { | ||
| payload = JSON.parse(responseText); | ||
| } | ||
| catch { | ||
| // A non-Kaval intermediary may return a plain-text error. | ||
| } | ||
| const code = apiErrorCode(payload); | ||
| if (attempt + 1 < MAX_BILLABLE_ATTEMPTS && | ||
| code !== undefined && | ||
| AMBIGUOUS_IDEMPOTENCY_CODES.has(code)) { | ||
| response = undefined; | ||
| continue; | ||
| } | ||
| throw new KavalError(response.status, payload, idempotencyKey); | ||
| } | ||
| if (!response?.ok) | ||
| throw new Error("unreachable Offer Search stream request state"); | ||
| if (!response.headers.get("content-type")?.includes("text/event-stream")) { | ||
| throw attachIdempotencyKey(new TypeError("Offer Search stream returned a non-SSE response"), idempotencyKey); | ||
| } | ||
| if (!response.body) { | ||
| throw attachIdempotencyKey(new TypeError("Offer Search stream response has no body"), idempotencyKey); | ||
| } | ||
| reader = response.body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| let buffer = ""; | ||
| let lastSequence = -1; | ||
| let finalResult; | ||
| let streamRequestDigest; | ||
| const consumeFrame = (frame) => { | ||
| const lines = frame.split("\n"); | ||
| const eventName = lines | ||
| .find((line) => line.startsWith("event:")) | ||
| ?.slice("event:".length) | ||
| .trim(); | ||
| const idText = lines | ||
| .find((line) => line.startsWith("id:")) | ||
| ?.slice("id:".length) | ||
| .trim(); | ||
| const dataText = lines | ||
| .filter((line) => line.startsWith("data:")) | ||
| .map((line) => line.slice("data:".length).trimStart()) | ||
| .join("\n"); | ||
| if (!eventName || !dataText) | ||
| return null; | ||
| let payload; | ||
| try { | ||
| payload = JSON.parse(dataText); | ||
| } | ||
| catch (error) { | ||
| throw attachIdempotencyKey(error, idempotencyKey); | ||
| } | ||
| const id = idText === undefined ? undefined : Number(idText); | ||
| if (idText !== undefined && (!Number.isInteger(id) || id < 0)) { | ||
| throw new TypeError("Offer Search stream event ID is invalid"); | ||
| } | ||
| if (eventName === "error") { | ||
| const error = payload; | ||
| throw new KavalError(typeof error?.status === "number" ? error.status : 500, payload, idempotencyKey); | ||
| } | ||
| if (eventName === "final") { | ||
| const result = reviewOnlyOfferSearchResult(payload, input.request_id); | ||
| if (streamRequestDigest !== undefined && | ||
| result.request_digest !== streamRequestDigest) { | ||
| throw new TypeError("Offer Search stream events are bound to another final result"); | ||
| } | ||
| const sequence = Number.isInteger(id) ? id : lastSequence + 1; | ||
| if (sequence <= lastSequence) { | ||
| throw new TypeError("Offer Search stream sequence is not monotonic"); | ||
| } | ||
| lastSequence = sequence; | ||
| finalResult = result; | ||
| return { type: "final", sequence, result }; | ||
| } | ||
| if (eventName === "replay") { | ||
| const event = reviewOnlyOfferSearchReplayEvent(payload, input.request_id); | ||
| if ((id !== undefined && id !== event.sequence) || | ||
| event.sequence <= lastSequence) { | ||
| throw new TypeError("Offer Search stream replay sequence is invalid"); | ||
| } | ||
| if (streamRequestDigest !== undefined && | ||
| event.request_digest !== streamRequestDigest) { | ||
| throw new TypeError("Offer Search replay request binding changed"); | ||
| } | ||
| streamRequestDigest = event.request_digest; | ||
| lastSequence = event.sequence; | ||
| return event; | ||
| } | ||
| const event = reviewOnlyOfferSearchProgressEvent(payload); | ||
| if (event.type !== eventName || | ||
| event.request_id !== input.request_id || | ||
| (id !== undefined && id !== event.sequence) || | ||
| event.sequence <= lastSequence) { | ||
| throw new TypeError("Offer Search stream event sequence or type is invalid"); | ||
| } | ||
| if (event.type === "candidate_provisional") { | ||
| if (streamRequestDigest !== undefined && | ||
| event.details.request_digest !== streamRequestDigest) { | ||
| throw new TypeError("Offer Search provisional candidate request binding changed"); | ||
| } | ||
| streamRequestDigest = event.details.request_digest; | ||
| } | ||
| lastSequence = event.sequence; | ||
| return event; | ||
| }; | ||
| while (true) { | ||
| const chunk = await reader.read(); | ||
| buffer = | ||
| `${buffer}${decoder.decode(chunk.value, { stream: !chunk.done })}`.replaceAll("\r\n", "\n"); | ||
| let boundary = buffer.indexOf("\n\n"); | ||
| while (boundary >= 0) { | ||
| const frame = buffer.slice(0, boundary); | ||
| buffer = buffer.slice(boundary + 2); | ||
| const event = consumeFrame(frame); | ||
| if (event) | ||
| yield event; | ||
| if (finalResult) | ||
| return finalResult; | ||
| boundary = buffer.indexOf("\n\n"); | ||
| } | ||
| if (chunk.done) | ||
| break; | ||
| } | ||
| if (buffer.trim().length > 0) { | ||
| const event = consumeFrame(buffer); | ||
| if (event) | ||
| yield event; | ||
| if (finalResult) | ||
| return finalResult; | ||
| } | ||
| throw attachIdempotencyKey(new TypeError("Offer Search stream ended before its final result"), idempotencyKey); | ||
| } | ||
| finally { | ||
| await reader?.cancel().catch(() => undefined); | ||
| request.cleanup(); | ||
| } | ||
| } | ||
| /** Re-read one persisted offer generation at the exact action boundary. This final fence always | ||
| * returns REVIEW with commerce permission withheld; it never authorizes quoting or purchasing. */ | ||
| async gateOfferSearch(input, options) { | ||
| const result = await this.post("/v1/search-offers/gate", input, options); | ||
| return reviewOnlyCommerceActionTimeGateResult(result, input); | ||
| } | ||
| /** Build, sign, and persist a complete action-bound proof packet. */ | ||
@@ -211,0 +400,0 @@ audit(input, options) { |
+4
-2
| { | ||
| "name": "@usekaval/kaval", | ||
| "version": "0.3.1", | ||
| "version": "0.4.0", | ||
| "license": "Apache-2.0", | ||
| "description": "Action-bound verification for AI agents — signed proof packets plus ALLOW/BLOCK/REVIEW gates.", | ||
| "description": "Evidence gates for AI agents: review-only offer research and action-bound ALLOW/REVIEW/BLOCK decisions.", | ||
| "type": "module", | ||
@@ -22,2 +22,4 @@ "main": "./dist/index.js", | ||
| "agents", | ||
| "evidence", | ||
| "commerce", | ||
| "freshness", | ||
@@ -24,0 +26,0 @@ "rag", |
+147
-6
| # @usekaval/kaval | ||
| The freshness gate for AI. Give kaval a belief your system already holds — a cached fact, a CRM | ||
| field, an agent memory — and it checks the live world and returns a typed verdict: `current`, | ||
| `stale`, `contradicted`, `unsupported`, `conflicting`, or `insufficient`. | ||
| The evidence gate for AI agents. Before an agent acts, Kaval checks that the current evidence still | ||
| supports that exact action. The full proof lifecycle returns `ALLOW`, `REVIEW`, or `BLOCK`; when the | ||
| evidence changes or expires, the permission does too. | ||
| **Search retrieves evidence. Kaval decides whether that evidence is sufficient for the action.** | ||
| ```bash | ||
@@ -24,2 +26,140 @@ npm install @usekaval/kaval | ||
| ## Find current offer evidence (review-only) | ||
| ```ts | ||
| import { Kaval, type OfferSearchInput } from "@usekaval/kaval"; | ||
| const request: OfferSearchInput = { | ||
| schema_revision: 1, | ||
| request_id: crypto.randomUUID(), | ||
| raw_description: "Makita XPH14Z hammer drill, tool only", | ||
| target: { | ||
| schema_revision: 1, | ||
| name: "Makita XPH14Z", | ||
| identifiers: [{ scheme: "model", value: "XPH14Z" }], | ||
| attributes: [{ key: "kit", value: false }], | ||
| }, | ||
| requested_condition: "new", | ||
| destination: { country_code: "US", region: "CA", postal_code: "94107" }, | ||
| match_policy: { | ||
| identity_requirement: "shared_identifier", | ||
| required_identifier_schemes: ["model"], | ||
| required_attribute_keys: ["kit"], | ||
| permitted_substitutions: [], | ||
| }, | ||
| seller_policy: { | ||
| allowed_seller_ids: [], | ||
| blocked_seller_ids: [], | ||
| allowed_kinds: ["brand_direct", "authorized_retailer"], | ||
| require_authorized: true, | ||
| }, | ||
| destination_policy: { | ||
| require_eligible: true, | ||
| require_exact_region: true, | ||
| require_exact_postal_code: true, | ||
| }, | ||
| price_policy: { | ||
| currency: "USD", | ||
| require_complete_landed_total: true, | ||
| allow_estimated_components: false, | ||
| allow_member_price: false, | ||
| allow_subscription_price: false, | ||
| allow_coupon_price: false, | ||
| allow_installment_display: false, | ||
| allow_trade_in_price: false, | ||
| }, | ||
| source_policy: { | ||
| allowed_source_ids: [], | ||
| blocked_source_ids: [], | ||
| require_origin_evidence: true, | ||
| }, | ||
| intended_action: { | ||
| description: "Quote this exact item to a customer", | ||
| materiality: "high", | ||
| reversibility: "partially_reversible", | ||
| }, | ||
| freshness_maximum_age_ms: 300_000, | ||
| max_results: 5, | ||
| minimum_unique_sellers: 2, | ||
| deadline_ms: 15_000, | ||
| maximum_cost_micro_usd: 50_000, | ||
| maximum_search_calls: 4, | ||
| maximum_fetches: 12, | ||
| }; | ||
| const kaval = new Kaval({ | ||
| apiKey: process.env.KAVAL_API_KEY, | ||
| }); | ||
| const result = await kaval.searchOffers(request); | ||
| if (result.action.state === "NEEDS_REVIEW") { | ||
| await queueForHumanReview(result.candidates); | ||
| } | ||
| // When durable lifecycle metadata is present, final-fence the exact generation at action time. | ||
| // Even current evidence remains REVIEW-only until commerce authorization is calibrated. | ||
| if (result.lifecycle?.persistence === "persisted") { | ||
| const finalFence = await kaval.gateOfferSearch({ | ||
| dependency_id: result.lifecycle.dependency_id, | ||
| generation_id: result.lifecycle.generation_id, | ||
| generation_number: result.lifecycle.generation_number, | ||
| generation_digest: result.lifecycle.generation_digest, | ||
| action_binding: result.lifecycle.action_binding, | ||
| }); | ||
| if (finalFence.state !== "current_review_only") { | ||
| await refreshOfferEvidence(result.lifecycle.dependency_id); | ||
| } | ||
| // finalFence.disposition === "REVIEW" and finalFence.permission === "withheld" in every state. | ||
| } | ||
| ``` | ||
| For progressive UI or agent feedback, consume the same operation as SSE. The last event contains the | ||
| same guarded result returned by `searchOffers()`; earlier events are explicitly `research_only` and | ||
| cannot authorize a quote or purchase: | ||
| ```ts | ||
| for await (const event of kaval.streamOfferSearch(request, { | ||
| idempotencyKey: crypto.randomUUID(), | ||
| })) { | ||
| if (event.type === "candidate_provisional") { | ||
| // Origin verification finished, but final selection and lifecycle persistence have not. | ||
| // durable=false, actionable=false, permission="withheld". | ||
| renderProvisionalOffer(event.details.candidate); | ||
| } else if (event.type === "final") { | ||
| await queueForHumanReview(event.result.candidates); | ||
| } else { | ||
| console.log( | ||
| event.type, | ||
| event.type === "replay" ? "completed operation replayed" : event.message, | ||
| ); | ||
| } | ||
| } | ||
| ``` | ||
| `candidate_provisional` is the only pre-completion candidate event. Its typed details always state | ||
| `publication_state: "provisional"`, `durable: false`, `actionable: false`, | ||
| `permission: "withheld"`, and `final_inclusion: "not_yet_determined"`. The SDK binds its request | ||
| ID and cryptographic digest across provisional, replay, and terminal results and rejects drift. The | ||
| later `candidate` event has crossed the current final publication boundary; only the exact | ||
| `lifecycle.selected_candidate_id` is durable, and every candidate remains review-only. | ||
| Offer Search researches the accessible configured web through configured structured source workers, | ||
| search discovery, direct origin re-fetches, serialized-DOM browser fallback, and optional | ||
| destination-aware checkout resolution. `candidate.checkout` contains the checkout receipt when one | ||
| was verified; `acquisition.source_ledger` states which planned sources succeeded, failed, were | ||
| prohibited, or remained unsearched. Coverage is explicitly bounded, not a claim to have searched the | ||
| literal entire internet. Its public output is deliberately shadow-grade: `action.state` is | ||
| `NEEDS_REVIEW` or `NO_RELIABLE_OFFER`, candidate dispositions are `review` or `rejected`, and the | ||
| SDK rejects any drifted response that claims `ALLOW`, `BLOCK`, `SAFE_TO_QUOTE`, or other commerce | ||
| authority. Do not quote or purchase from this result without review. `searchOffers()` accepts the same | ||
| `{ idempotencyKey?, signal?, timeoutMs? }` request options as other billable calls; | ||
| `streamOfferSearch()` also closes the response stream when its signal is aborted or iteration stops. | ||
| When the server has a durable commerce lifecycle configured, `result.lifecycle` identifies the | ||
| immutable evidence generation, exact selected candidate, and action binding. Call | ||
| `gateOfferSearch()` immediately before the action boundary. It re-reads that generation and the | ||
| latest stream head, but deliberately returns only `disposition: "REVIEW"` and | ||
| `permission: "withheld"`; stale, expired, invalidated, changed, revoked, unavailable, or mismatched | ||
| evidence must be refreshed or reviewed. | ||
| ## Build a proof, then gate the action | ||
@@ -72,3 +212,3 @@ | ||
| ## Gate a belief before you act on it | ||
| ## Legacy held-belief compatibility | ||
@@ -86,3 +226,4 @@ ```ts | ||
| `verify()` returns the verdict plus `act` — `true` only when the belief is `current` and confident | ||
| `verify()` preserves the original currentness API. It returns the verdict plus `act` — `true` only | ||
| when the belief is `current` and confident | ||
| (≥ 0.7 by default; override with `minConfidence`). | ||
@@ -148,3 +289,3 @@ | ||
| `audit` · `gateAction` (`gate` alias) · `verify` · `check` · `extractAndCheck` · `scanStore` · | ||
| `searchOffers` · `streamOfferSearch` · `gateOfferSearch` · `audit` · `gateAction` (`gate` alias) · `verify` · `check` · `extractAndCheck` · `scanStore` · | ||
| `monitor` · `reportOutcome` · `kaval` · `kavalBatch` · `health`. Billable methods accept a final | ||
@@ -151,0 +292,0 @@ `{ idempotencyKey?, signal?, timeoutMs? }` request-options argument (`kavalBatch` includes it alongside |
102930
84.03%9
28.57%1911
94.01%297
90.38%