From 6f5cfca353d4c3190532a226bc8dd3b211c8c9d2 Mon Sep 17 00:00:00 2001 From: "Constantine.mirin" Date: Thu, 25 Jun 2026 17:15:24 +0200 Subject: [PATCH] feat(proto): add TERMS_LIMIT_EXCEEDED + URI_UNAVAILABLE catalog reject reasons (RAMP-102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CatalogRejectionReason lacked honest values for two rejection paths: - TERMS_LIMIT_EXCEEDED (8): a single entry over the per-entry license-term cap (distinct from QUOTA_EXCEEDED, which is a per-caller push quota). - URI_UNAVAILABLE (9): the URI cannot be claimed by this caller. Named from the caller's own perspective ON PURPOSE — it must NOT disclose that another resource/contributor already owns the URI. Within one publisher, mutually- untrusting contributors share a catalog, so an 'owned by another' reason would be a confirmed-existence oracle for mapping a competitor's catalog. Regenerated Go/TS/Python. buf lint + canonical round-trip + doc-conformance pass. --- conformance/corpus/cases.json | 2 +- gen/descriptor.binpb | Bin 522476 -> 523332 bytes gen/go/ramp/v1/ramp.pb.go | 22 +- gen/python/wire/models.py | 59 +- gen/ts/wire/schemas.ts | 1764 +++------------------------------ proto/ramp/v1/ramp.proto | 12 +- 6 files changed, 193 insertions(+), 1666 deletions(-) diff --git a/conformance/corpus/cases.json b/conformance/corpus/cases.json index 42d537d8..f7b0ee1e 100644 --- a/conformance/corpus/cases.json +++ b/conformance/corpus/cases.json @@ -154,7 +154,7 @@ "enum.defined_only" ], "json": { - "reason": 8 + "reason": 10 } }, { diff --git a/gen/descriptor.binpb b/gen/descriptor.binpb index f2cda19766ee7574e4da0b3b9401d84b5758295b..877c7b7eb643332685dc2566a4d56e9a5d7703a4 100644 GIT binary patch delta 3512 zcmY+GYitzP6~}jn*}e8S5oC&(>ou&eAKoMMD?X`B- zAlK#)#nL7Qj z(wuYub6&qQGkbS9`@&fEulu}xTHk~|{kX5NLhIi-P+Z|_{FQffpsU}v-K(3kj52wz z-A%RcjkxUewsQxN-5MG_%wwlyCL>0+UuWtzF&Ru%@ADNy7_E8YiU> z_;>jRGmJBTPB)n8OZQygICjv7pMPNNAnT~{-WX{67khca#CeA9o1!sOziw&ex#jZm z4JMuygMEF6kP?Rb&A2UqVJrAssQe^^>o?u#NM5|PezQCrxZ$#{xmU4y&8Gsl}M+2z9CBjxIYH zK}!Yg34}`JFINa~Ya2Kx1qM!Ih`ttTLCjcZv?m4PM_r1-_3bedhPspOv3TSSyVam9 z3ELKJ-O(rw3J}^OT}AbB;cxttdpqnzBDBsH@mO~fTQv@iF5|W#)?=7BqYkJ&Oidt1 zsfe^;G(DJcsc4TyTgS)mZdy7v4Xae_Y+5=t?F*}{+*odCdbR1lX?gBiUK#igTl$lm zRvI@J7`k4qc})LpOB=Lg#u%&Vh3z+WI5h3W}X zuaEp0&pGM6N@fbzL^VTJUcT|J*U)^H>A!Dz9?QRd$@ZG}Enm)mNv`Lc%d1ws^%C~N(1YG^xtCD!3X1U zH1blMBBeoP59KSjSV{09o^B`31ZW=8!(XB_x~V^s`);wa;3G?$>1cFl9$B-i91Z#( zmES;ZSgs8uo3SOc^*rvE$3^MkCr%~qS=S!r3u^O$H2fqi0W-^;zX*}Ps{ zWzo&#M~hFaskyF+N}pKMXDFgL1<@x~!S@bPawwutU80T$6+Cr`IxZsml!%@u8r<~v zanJ9#H*m}=WZ89xy=V4uU#0@X3Cx>ZD^g%&^C0tQ!^~Gb;t(0$&jL1wNJq2EnhmVCu(vo~?A zz!4FeO}w~-hKEQ%vzg1^e#PboH*-zRio6hQ=9pED5`oPY`ELreg==|EAY!wH&nPe! zg3Vmh|2FshLDqcD7MO4Iv?+?sTw=48YwyTSUxN*%3jqa2O?y?txJGoZvXo$&9UR$S_WD=8I(sz$72=3y} zqX{Q8yEq=rIVuj2?3OL}SWR#@*XB8K2+3}~V4<-{Y4C#EBM0uW#=Je_8b369Nb@SN z)tdgj+;b2E`lVbr!~(MR8@9yU%hPfczgptAk81~rUv)1`goK?#`E;0=%08Z3=$dG5 zA1^6Y6mbfQ`*_&_hax!?#r-Zt$Ag0QyA&N4QQS`y@6!_&F#Ydy&tK${Z`rrz``nkU z_yvgH2LP6O-@!yRuuIf1U?T1Zyu8Aw$1nl}@Q~bopFJNuG_E0lhe$I`X98ds{66BvL z4FDdKhlknH;4v3+KqG+12y&_(O#8SDj3C!>ccB6r0X$9%B~%>z?K>e~8ezG?6YlFG zpfPAp(CcEl(%>)ONhwEILGUEkW;->fL35H<*EpJVQy-B3B+bBh@APRMpx$#-9K+O4 z$(#o)@8_quR^`MopgF~B-1%gf`e`J|ohS;rVqI(P*SjKdyR{(o?G9)e;DGZo=jIID ze45X!bP_yc>SyGikRbcac*D;?bB5Q=Gs4)|I@A9l_xxSnc)(iB4|&>DwWxKpsAsu$ zKDDSYF*?{&DUA*jSeWRiAfBaH7k%7eVo}fX!t>6el0z-(Id@SV59&DQE~?|A zMLkE0x{DSy%k+Q5J)imUJpCTu0`I%NfGmE*-Y`FMmo|%*_T&F8ElgAfJGHbhk>g`p zNcy;D(b8U&KRm+6^`Z*}PG~L?6#TD&2G%F?#ZlG}{KWkfz)4I%cs@jbgIA@R(JZSBM$?BdaH7SGo4NG{)Hc2(D7J;78k6 qU0N_v{Z*G1OwhW@3qN;gkwekC=F)OJsO6eV%W)B{Yx3wAtN$-%IOXR6 delta 2664 zcmYM0TWnQT8pqFB-FvUoRVTpGVSPP?ecIBSl(tyfp5CM~jwasmL8F<`2j*dt87HHU z^8}7ftBG8zvr;+_0|PY5b(B!lc!8P_zr4&%gW&YnfYo`xw&i`Bg z^X;|1-+%9YzWgaSc_p{0H{C0Gi+Y2jVO^6rF)*^SDSYG)>32t79Sr}L9{K%HI98%Y z|5>c04vSM?^}jeA;x8I`aajH-pZ;d#)H(S=(V`|5giD3g!PiDKR#w=?A7r`x4gQs^ zS0ZTB(YGe`gMIZ!`HcS7WJvX>AZQaQ9eroSIs2a<<=Tz-MCunRs@>+(cmPv^~wDz!VUL}+w$!K9{4Iy;aqx33I^ zrMBgw{HwlT!dR`sYz)pCT*Peb>l3oN3KH$0@8@>Bip^d$s~S{;Gh=bgE*XL)4WF2J5Qj4 z2|GU~8{!EgR(nqX&xBdi;yoC}q~Y{*R-vC4ZwcqUELYV|x2=XEq=QrGN`OY*sU zXH2F{J%|zK8S$(UbH<*ZlC8FBTt*wA%eV!v;-Ir;S!E)z-&s>#<3M8-pl40(oCnPX z2l{FP+E=iTs|jddV$fF^^hw4;I=X36uTwhvA@Cd>m!B-YX~I%RhM3H}u^T6Fx_Kk2 z-04t7*LhQ0=O#%48@*-h*$KHacMBN3iILng4>WV0LgL}yHukpL-&J7#4)j}4sw<*u>+os?_iyGFSAa22MzJfFNXfyySEpF*Qe zBv$xFOl1?*)T)O-rCmq8lzPiqfmQJ6qslj_aN}{o@#rUiUJKet#;Qm2Dg<&-g_9qR_g4+P`D3{wawn1mcNR(k!bTCCa{ekQmD#Rn{dE zryHdDRSp_f0d0^PhCDPbIA}W)Xug6S?Ii!S^d-KDb~3>kzKOCr+C`~<**&vzTJNGT z=fGtd+-?vnJlG(y8|ZpB4M_CeO^r>e3)9Fl!9Di!Rrz4NXTie+_poQV^FZyjkIl(k zyq83)@52Q5QhSH@;EelhAA9zZX!af^xQ|-fRF&%kg2Q%nPS#WpCzP{};4o8u!g)aO zJuGHx%GcZ1Zb-5<*JNV_N`;4RRxr)?n0R4tnEd;|XrG*uR2ktkP=%0X1w(zGLssru z<7K$t9=;|kb2M6r*HCY=UAhE_DBLmL?YRh|1u?`N}mh12w zTo^|&JO_Ck6|Rrc!6EyX>vBbWh(wF;qu@D2`DA1zIyh`UzK)R{Ceh$MCGZ@k)y=-o zJvumIZ=g@^$ilhqf#(QycB|hZterYKN~w=*#|^nbAEnGP7tv0R=rIz<3lW9HprDuW zWdcY{>KH9|&nT9_cZ{D={6dFBM2}J3aUW4GxQLD<5%m@9V@>GLGnHNknTQu^Z?@L?O}lGmam>Gs`%lpOfgc|DKSQRgeW;$RQ-m zpY!1H3kGJWFYG^lmh0j#$iK`{!WTu$-p4vmOf<9F{W)7GJ>*&LmiTiLsmk K7CYS-w*McSFXBr8 diff --git a/gen/go/ramp/v1/ramp.pb.go b/gen/go/ramp/v1/ramp.pb.go index 94083e44..09152886 100644 --- a/gen/go/ramp/v1/ramp.pb.go +++ b/gen/go/ramp/v1/ramp.pb.go @@ -1493,7 +1493,17 @@ const ( CatalogRejectionReason_CATALOG_REJECTION_REASON_SIGNATURE_INVALID CatalogRejectionReason = 4 // request signature missing or invalid CatalogRejectionReason_CATALOG_REJECTION_REASON_MALFORMED_ENTRY CatalogRejectionReason = 5 // a resource entry failed schema/validation CatalogRejectionReason_CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN CatalogRejectionReason = 6 // an unregistered vocab token in a restriction/term - CatalogRejectionReason_CATALOG_REJECTION_REASON_QUOTA_EXCEEDED CatalogRejectionReason = 7 // contributor push quota exceeded + CatalogRejectionReason_CATALOG_REJECTION_REASON_QUOTA_EXCEEDED CatalogRejectionReason = 7 // contributor push quota exceeded (per-caller) + CatalogRejectionReason_CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED CatalogRejectionReason = 8 // a single entry carries more license terms than allowed (per-entry cap) + // The URI cannot be claimed by this caller's entries. Named from the caller's + // own perspective ON PURPOSE: it MUST NOT disclose that another resource/ + // contributor already owns the URI. Within one publisher, mutually-untrusting + // contributors share a catalog, so an "owned by another" reason would be a + // confirmed-existence oracle a contributor could use to map a competitor's + // catalog. The conflict is resolvable only by the publisher (who is authorized + // to see full ownership); the human-readable message routes the caller there + // without confirming who, if anyone, holds the URI. + CatalogRejectionReason_CATALOG_REJECTION_REASON_URI_UNAVAILABLE CatalogRejectionReason = 9 ) // Enum value maps for CatalogRejectionReason. @@ -1507,6 +1517,8 @@ var ( 5: "CATALOG_REJECTION_REASON_MALFORMED_ENTRY", 6: "CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN", 7: "CATALOG_REJECTION_REASON_QUOTA_EXCEEDED", + 8: "CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED", + 9: "CATALOG_REJECTION_REASON_URI_UNAVAILABLE", } CatalogRejectionReason_value = map[string]int32{ "CATALOG_REJECTION_REASON_UNSPECIFIED": 0, @@ -1517,6 +1529,8 @@ var ( "CATALOG_REJECTION_REASON_MALFORMED_ENTRY": 5, "CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN": 6, "CATALOG_REJECTION_REASON_QUOTA_EXCEEDED": 7, + "CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED": 8, + "CATALOG_REJECTION_REASON_URI_UNAVAILABLE": 9, } ) @@ -9338,7 +9352,7 @@ const file_ramp_v1_ramp_proto_rawDesc = "" + "\x16RESOLUTION_TYPE_CREDIT\x10\x01\x12\x1e\n" + "\x1aRESOLUTION_TYPE_REDELIVERY\x10\x02\x12\x1c\n" + "\x18RESOLUTION_TYPE_REJECTED\x10\x03\x12!\n" + - "\x1dRESOLUTION_TYPE_INVESTIGATION\x10\x04*\x95\x03\n" + + "\x1dRESOLUTION_TYPE_INVESTIGATION\x10\x04*\xf6\x03\n" + "\x16CatalogRejectionReason\x12(\n" + "$CATALOG_REJECTION_REASON_UNSPECIFIED\x10\x00\x124\n" + "0CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR\x10\x01\x12,\n" + @@ -9347,7 +9361,9 @@ const file_ramp_v1_ramp_proto_rawDesc = "" + "*CATALOG_REJECTION_REASON_SIGNATURE_INVALID\x10\x04\x12,\n" + "(CATALOG_REJECTION_REASON_MALFORMED_ENTRY\x10\x05\x120\n" + ",CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN\x10\x06\x12+\n" + - "'CATALOG_REJECTION_REASON_QUOTA_EXCEEDED\x10\a*\xc1\x02\n" + + "'CATALOG_REJECTION_REASON_QUOTA_EXCEEDED\x10\a\x121\n" + + "-CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED\x10\b\x12,\n" + + "(CATALOG_REJECTION_REASON_URI_UNAVAILABLE\x10\t*\xc1\x02\n" + "\x19RegistrationFailureReason\x12+\n" + "'REGISTRATION_FAILURE_REASON_UNSPECIFIED\x10\x00\x123\n" + "/REGISTRATION_FAILURE_REASON_DOMAIN_NOT_VERIFIED\x10\x01\x12+\n" + diff --git a/gen/python/wire/models.py b/gen/python/wire/models.py index 2fed9541..df22c677 100644 --- a/gen/python/wire/models.py +++ b/gen/python/wire/models.py @@ -93,6 +93,12 @@ class CatalogRejectionReason(Enum): 'CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN' ) CATALOG_REJECTION_REASON_QUOTA_EXCEEDED = 'CATALOG_REJECTION_REASON_QUOTA_EXCEEDED' + CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED = ( + 'CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED' + ) + CATALOG_REJECTION_REASON_URI_UNAVAILABLE = ( + 'CATALOG_REJECTION_REASON_URI_UNAVAILABLE' + ) class CitationFormat(Enum): @@ -1297,18 +1303,7 @@ class TransactionResponse(WireModel): ) agentIdentityHash: str | None = Field( '', - description='Identity that retrieval_endpoint is bound to: the RFC 7638 JWK Thumbprint of\n the agent\'s Ed25519 request-signing key (see "Retrieval-URL identity binding"\n above). Empty string when absent; non-empty iff a signed retrieval_endpoint\n is present. Delivery-endpoint enforcement of the binding is OPTIONAL.', - ) - billingId: str | None = Field(None, description='Billing reference') - cost: Cost | None = Field(None, description='Transaction cost') - deliveryMethod: ( - constr(pattern=r'^DELIVERY_METHOD_UNSPECIFIED$') - | DeliveryMethod - | conint(ge=-2147483648, le=2147483647) - | None - ) = Field(0, description='How resource is delivered in this transaction.') - expiresAt: AwareDatetime | None = Field( - None, description='When retrieval_endpoint expires.' + description='Identity that a delivered retrieval_endpoint is bound to: the RFC 7638 JWK\n Thumbprint of the agent\'s Ed25519 request-signing key (see "Retrieval-URL\n identity binding" above). Shared across the request; set once.', ) ext: dict[str, Any] | None = Field(None, description='Extension point') extCritical: list[str] | None = Field( @@ -1316,37 +1311,14 @@ class TransactionResponse(WireModel): description='Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.', ) items: list[TransactionResultItem] | None = Field( - None, description='Batch mode: per-offer results.' - ) - reportingObligation: ReportingObligation | None = Field( - None, description='Reporting requirements attached to this delivery.' - ) - resourceTitle: str | None = Field( - None, description='Resource title echoed from the Offer (for logging/display).' - ) - retrievalEndpoint: str | None = Field( None, - description='Signed retrieval URL the agent uses to fetch the purchased resource.\n Bound to agent_identity_hash; expires at expires_at. Absent on denial\n and on transactions whose delivery_method is not signed-URL-based.', - ) - subscriptionId: str | None = Field( - None, - description='If set, this transaction was fulfilled under a subscription/deal.\n No per-request charge — usage tracked against subscription quota.', + description='Per-offer results (one entry per committed item, in original order).', ) subscriptionQuota: list[SubscriptionQuotaInfo] | None = Field( None, description='Post-transaction quota state. Tells the agent how much quota remains\n after this transaction. Enables proactive throttling ("1 access left").\n Multiple entries for multi-dimensional quotas.', ) - subscriptionUnitValue: Cost | None = Field( - None, - description='Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).', - ) - totalCost: Cost | None = Field( - None, description='Batch mode: aggregate cost across all items.' - ) - transactionId: str | None = Field( - None, - description='Single-offer result.\n For batch mode, these may be empty — check `items` instead.', - ) + totalCost: Cost | None = Field(None, description='Aggregate cost across all items.') ver: str | None = Field('', description='Protocol version') @@ -1837,10 +1809,6 @@ class TransactionRequest(WireModel): model_config = ConfigDict( extra='forbid', ) - agentAcceptance: AgentAcceptance | None = Field( - None, - description="Single-offer mode: the agent's detached acceptance signature over the\n accepted `offer` (RAMP-102 §1). Optional on the wire (additive,\n backward-compatible); the Exchange enforces presence at the service layer\n for relayed requests. Signed bytes = deterministic AgentAcceptancePayload\n {offer_sig=offer.signature, requester_id=requester.id,\n requester_domain=requester.domain, idempotency_key=idempotency_key}.", - ) ext: dict[str, Any] | None = Field(None, description='Extension point') extCritical: list[str] | None = Field( None, @@ -1852,15 +1820,12 @@ class TransactionRequest(WireModel): ) items: list[TransactionItem] | None = Field( None, - description='Batch mode: commit to multiple offers in one request, each carrying its\n own reflected signed Offer. Set this XOR `offer` (see message rule).', - ) - offer: Offer | None = Field( - None, - description='Single-offer mode: the FULL signed Offer, reflected back exactly as\n received at discovery. The Exchange verifies `offer.signature` (which\n covers pricing, terms, and expires_at) over these presented bytes against\n its own key — a stateless, self-contained bearer token, with no\n reconstruct-from-catalog. Set this XOR `items` (see message rule).', + description="The offers committed in this request (REQUIRED, min 1), each carrying its\n own reflected signed Offer + detached acceptance. A single offer is the\n degenerate 1-element list. The Exchange verifies each item's\n `offer.signature` (which covers pricing, terms, and expires_at) over the\n presented bytes against its own key — stateless, self-contained bearer\n tokens, with no reconstruct-from-catalog.", + min_length=1, ) offerId: str | None = Field( None, - description='Optional, non-authoritative correlation/audit key — the human-readable id\n of the committed offer. NOT used for verification: the Exchange verifies\n the reflected `offer.signature` over the presented Offer bytes, never this\n scalar. May be omitted; if set, it SHOULD match `offer.offer_id`.', + description="Optional, non-authoritative correlation/audit key — a human-readable offer\n id. NOT used for verification: the Exchange verifies each item's reflected\n `offer.signature` over the presented Offer bytes, never this scalar. May be\n omitted; if set, it SHOULD match an item's `offer.offer_id`.", ) requester: Requester | None = Field( None, description='Requester identity — forwarded for authorization and audit.' diff --git a/gen/ts/wire/schemas.ts b/gen/ts/wire/schemas.ts index 8263e9d0..688e1577 100644 --- a/gen/ts/wire/schemas.ts +++ b/gen/ts/wire/schemas.ts @@ -198,7 +198,7 @@ export const C2PAStatusSchema = z.enum(["C2PA_STATUS_TRUSTED","C2PA_STATUS_VALID export const CatalogContributorSchema = z.object({ "domain": z.string().describe("Canonical domain of the authorized contributor (e.g., \"doubleverify.com\").").default(""), "relationship": z.string().describe("Relationship of this contributor to the provider.\n Examples: \"verifier\" (resource intelligence vendor that attests to resource\n properties), \"exchange\" (an Exchange that enriches catalog entries).").default("") }).strict().describe("CatalogContributor — A third party authorized to push catalog metadata\n (including attestations) on behalf of a provider."); -export const CatalogRejectionSchema = z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { +export const CatalogRejectionSchema = z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED","CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED","CATALOG_REJECTION_REASON_URI_UNAVAILABLE"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["reason", "rejectedPaths"].includes(key) if (key.match(new RegExp("^(rejected_paths)$"))) { @@ -231,7 +231,7 @@ ctx.addIssue({ } }).describe("CatalogRejection — a CatalogService call could not be applied."); -export const CatalogRejectionReasonSchema = z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED"]); +export const CatalogRejectionReasonSchema = z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED","CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED","CATALOG_REJECTION_REASON_URI_UNAVAILABLE"]); export const CitationFormatSchema = z.enum(["CITATION_FORMAT_LINK","CITATION_FORMAT_FOOTNOTE","CITATION_FORMAT_INLINE"]); @@ -4998,7 +4998,7 @@ ctx.addIssue({ } }).describe("DomainVerificationResult — Exchange confirms verification."); -export const ErrorDetailSchema = z.object({ "catalogRejection": z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { +export const ErrorDetailSchema = z.object({ "catalogRejection": z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED","CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED","CATALOG_REJECTION_REASON_URI_UNAVAILABLE"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["reason", "rejectedPaths"].includes(key) if (key.match(new RegExp("^(rejected_paths)$"))) { @@ -5074,7 +5074,7 @@ ctx.addIssue({ } } } -}).describe("`reason` oneof — ExecuteTransaction denial").optional(), "usageReportRejection": z.object({ "reason": z.enum(["USAGE_REPORT_REJECTION_REASON_TRANSACTION_NOT_FOUND","USAGE_REPORT_REJECTION_REASON_DUPLICATE","USAGE_REPORT_REJECTION_REASON_WINDOW_EXPIRED","USAGE_REPORT_REJECTION_REASON_MISSING_REQUIRED_FIELDS","USAGE_REPORT_REJECTION_REASON_MALFORMED"]).describe("The rejection reason (defined-only, non-zero)") }).strict().describe("`reason` oneof — ReportUsage filing rejected").optional() }).catchall(z.union([z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { +}).describe("`reason` oneof — ExecuteTransaction denial").optional(), "usageReportRejection": z.object({ "reason": z.enum(["USAGE_REPORT_REJECTION_REASON_TRANSACTION_NOT_FOUND","USAGE_REPORT_REJECTION_REASON_DUPLICATE","USAGE_REPORT_REJECTION_REASON_WINDOW_EXPIRED","USAGE_REPORT_REJECTION_REASON_MISSING_REQUIRED_FIELDS","USAGE_REPORT_REJECTION_REASON_MALFORMED"]).describe("The rejection reason (defined-only, non-zero)") }).strict().describe("`reason` oneof — ReportUsage filing rejected").optional() }).catchall(z.union([z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED","CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED","CATALOG_REJECTION_REASON_URI_UNAVAILABLE"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["reason", "rejectedPaths"].includes(key) if (key.match(new RegExp("^(rejected_paths)$"))) { @@ -5155,7 +5155,7 @@ for (const key in value) { let evaluated = ["catalogRejection", "disputeFailure", "domain", "domainVerificationFailure", "message", "metadata", "registrationFailure", "retrievalAuthFailure", "transactionDenial", "usageReportRejection"].includes(key) if (key.match(new RegExp("^(catalog_rejection)$"))) { evaluated = true -const result = z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { +const result = z.object({ "reason": z.enum(["CATALOG_REJECTION_REASON_NOT_CATALOG_CONTRIBUTOR","CATALOG_REJECTION_REASON_TENANT_MISMATCH","CATALOG_REJECTION_REASON_DOMAIN_NOT_VERIFIED","CATALOG_REJECTION_REASON_SIGNATURE_INVALID","CATALOG_REJECTION_REASON_MALFORMED_ENTRY","CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN","CATALOG_REJECTION_REASON_QUOTA_EXCEEDED","CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED","CATALOG_REJECTION_REASON_URI_UNAVAILABLE"]).describe("The rejection reason (defined-only, non-zero)"), "rejectedPaths": z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected.").optional() }).catchall(z.union([z.array(z.string()).describe("For partial-batch failures: the entry paths that were rejected."), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["reason", "rejectedPaths"].includes(key) if (key.match(new RegExp("^(rejected_paths)$"))) { @@ -15455,38 +15455,7 @@ ctx.addIssue({ } }).describe("TransactionItem — A single offer commitment within a batch transaction."); -export const TransactionRequestSchema = z.object({ "agentAcceptance": z.object({ "signature": z.string().min(1).describe("Hex-encoded detached Ed25519 signature over the deterministic-marshaled\n AgentAcceptancePayload bytes."), "signatureAlgorithm": z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("") }).catchall(z.union([z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["signature", "signatureAlgorithm"].includes(key) -if (key.match(new RegExp("^(signature_algorithm)$"))) { -evaluated = true -const result = z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Single-offer mode: the agent's detached acceptance signature over the\n accepted `offer` (RAMP-102 §1). Optional on the wire (additive,\n backward-compatible); the Exchange enforces presence at the service layer\n for relayed requests. Signed bytes = deterministic AgentAcceptancePayload\n {offer_sig=offer.signature, requester_id=requester.id,\n requester_domain=requester.domain, idempotency_key=idempotency_key}.").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "idempotencyKey": z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response."), "items": z.array(z.object({ "agentAcceptance": z.object({ "signature": z.string().min(1).describe("Hex-encoded detached Ed25519 signature over the deterministic-marshaled\n AgentAcceptancePayload bytes."), "signatureAlgorithm": z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("") }).catchall(z.union([z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default(""), z.never()])).superRefine((value, ctx) => { +export const TransactionRequestSchema = z.object({ "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "idempotencyKey": z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response."), "items": z.array(z.object({ "agentAcceptance": z.object({ "signature": z.string().min(1).describe("Hex-encoded detached Ed25519 signature over the deterministic-marshaled\n AgentAcceptancePayload bytes."), "signatureAlgorithm": z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("") }).catchall(z.union([z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default(""), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["signature", "signatureAlgorithm"].includes(key) if (key.match(new RegExp("^(signature_algorithm)$"))) { @@ -16582,12 +16551,12 @@ ctx.addIssue({ } } } -}).describe("TransactionItem — A single offer commitment within a batch transaction.")).describe("Batch mode: commit to multiple offers in one request, each carrying its\n own reflected signed Offer. Set this XOR `offer` (see message rule).").optional(), "offer": z.object({ "attestations": z.array(z.object({ "attestedAt": z.string().datetime({ offset: true }).describe("When this attestation was created. Agents use this to assess freshness\n (e.g., \"I accept attestations up to N hours old for breaking news\").").optional(), "claims": z.record(z.string(), z.any()).describe("Signed claims about the resource (max 4KB). A JSON object containing\n whatever properties the attesting party can determine about the resource.\n Recommended claim names for interoperability:\n estimated_quantity (integer): estimated consumption quantity (e.g., token count for text)\n word_count (integer): word count (estimated_quantity ~ word_count * 1.32 for text)\n language (string): ISO 639-1 language code\n iab_categories (string[]): IAB Content Taxonomy 3.1 codes\n content_hash (string): hash of content in \"method:hexdigest\" format\n hash_method (string): algorithm used for content_hash\n Vendors MAY add vendor-specific claims (e.g., brand_safety, sentiment).\n The protocol does NOT define \"quality score\" — it is inherently subjective.\n If a vendor provides a proprietary score, the vendor defines what it means\n via their WellKnownManifest ext[\"ramp.attestation.claims_schema\"].").optional(), "kid": z.string().describe("Key ID from the verifier's WellKnownManifest.public_keys list\n (/.well-known/ramp.json). Identifies which Ed25519 key was used to\n sign this attestation. Enables key rotation: new keys are added with\n overlapping validity, new attestations use the new key, old attestations\n remain verifiable as long as the old key is published.").default(""), "signature": z.string().describe("Ed25519 signature over JCS-canonicalized (RFC 8785) representation of\n {verifier, kid, attested_at, uri, claims}. JCS (JSON Canonicalization\n Scheme) produces deterministic UTF-8 bytes: lexicographic key sorting,\n ECMAScript number serialization, strict string escaping, no whitespace.\n Each attestation is self-contained — new claim fields do not invalidate\n old attestations because the signature covers the specific claims instance.").default(""), "uri": z.string().describe("The resource URI this attestation covers. Must match the URI in the\n Offer or ResourceEntry this attestation is attached to.").default(""), "verifier": z.string().describe("Canonical domain of the attesting party (e.g., \"nytimes.com\" for\n self-attestation, \"doubleverify.com\" for third-party attestation).\n Used to look up the verifier's public keys at:\n https://{verifier}/.well-known/ramp.json (WellKnownManifest,\n role=ROLE_EXCHANGE or ROLE_PUBLISHER depending on operator).").default("") }).catchall(z.union([z.string().datetime({ offset: true }).describe("When this attestation was created. Agents use this to assess freshness\n (e.g., \"I accept attestations up to N hours old for breaking news\")."), z.never()])).superRefine((value, ctx) => { +}).describe("TransactionItem — A single offer commitment within a batch transaction.")).min(1).describe("The offers committed in this request (REQUIRED, min 1), each carrying its\n own reflected signed Offer + detached acceptance. A single offer is the\n degenerate 1-element list. The Exchange verifies each item's\n `offer.signature` (which covers pricing, terms, and expires_at) over the\n presented bytes against its own key — stateless, self-contained bearer\n tokens, with no reconstruct-from-catalog.").optional(), "offerId": z.string().describe("Optional, non-authoritative correlation/audit key — a human-readable offer\n id. NOT used for verification: the Exchange verifies each item's reflected\n `offer.signature` over the presented Offer bytes, never this scalar. May be\n omitted; if set, it SHOULD match an item's `offer.offer_id`.").optional(), "requester": z.object({ "billingRef": z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution.").optional(), "delegation": z.object({ "expiresAt": z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens.").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "issuer": z.string().describe("Token issuer. OIDC issuer URL or GNAP grant server URL.\n Exchange uses this for JWT validation (OIDC discovery → JWKS)\n or GNAP token introspection.").optional(), "maxAccesses": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling.").optional(), "maxSpendCents": z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap.").optional(), "principalDomain": z.string().describe("Who granted this delegation (domain for public key lookup).").default(""), "principalId": z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default(""), "quotaPeriod": z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at).").optional(), "revocationUri": z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff).").optional(), "scopes": z.array(z.string()).describe("Scopes granted by this delegation. MUST be a subset of the\n principal's own scopes (attenuation — can only narrow, not widen).").optional(), "token": z.string().regex(new RegExp("^[A-Za-z0-9+/]*={0,2}$")).describe("Token bytes. A JWT (base64url-encoded JWS) by default, or a Biscuit (binary,\n base64-encoded) when token_format is \"biscuit-v3\".").default(null), "tokenFormat": z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default("") }).catchall(z.union([z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling."), z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap."), z.string().describe("Who granted this delegation (domain for public key lookup).").default(""), z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default(""), z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at)."), z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff)."), z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default(""), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["attestedAt", "claims", "kid", "signature", "uri", "verifier"].includes(key) -if (key.match(new RegExp("^(attested_at)$"))) { +let evaluated = ["expiresAt", "ext", "extCritical", "issuer", "maxAccesses", "maxSpendCents", "principalDomain", "principalId", "quotaPeriod", "revocationUri", "scopes", "token", "tokenFormat"].includes(key) +if (key.match(new RegExp("^(expires_at)$"))) { evaluated = true -const result = z.string().datetime({ offset: true }).describe("When this attestation was created. Agents use this to assess freshness\n (e.g., \"I accept attestations up to N hours old for breaking news\").").safeParse(value[key]) +const result = z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16599,26 +16568,23 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) +if (key.match(new RegExp("^(ext_critical)$"))) { +evaluated = true +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: must match catchall schema`, + message: `Invalid input: Key matching regex /${key}/ must match schema`, params: { issues: result.error.issues } }) } } -} -}).describe("A provider or third-party verification vendor (GumGum, DoubleVerify, IAS)\n attests to properties of the resource at a specific URI at a specific time.\n The signature covers all fields, proving origin and integrity of the claims.\n\n Verification levels (determined by who the verifier is):\n Level 0: No attestation present. Resource may carry identifiers\n (DOI, IPTC GUID via ResourceIdentity) but nothing is cryptographically\n verifiable. Only CDN delivery failure is auto-disputable.\n Level 1 (self-attested): verifier == provider domain. Provider signs\n own claims with their Ed25519 key. Agent can independently verify\n content_hash by re-computing it from delivered bytes. Requires the\n provider to serve deterministic content at the delivery endpoint.\n Level 2 (third-party attested): verifier == verification vendor domain.\n Vendor independently crawled the resource and attested to its properties.\n Agent trusts the attestation — does NOT re-verify the content hash\n (agent lacks the vendor's extraction algorithm). The Ed25519 signature\n proves the vendor made the attestation; trust is binary (\"do I trust\n this vendor?\").\n\n Claims are limited to 4KB. Attestations are carried in-memory in the\n Exchange catalog and in Offer responses — strict size limits protect\n against payload poisoning and ensure catalog performance at scale.\n\n Verifiers MUST publish their keys at:\n https://{verifier-domain}/.well-known/ramp.json\n Verifier domain serves keys via WellKnownManifest (role=ROLE_EXCHANGE or\n ROLE_PUBLISHER depending on operator). Verifiers publish the claims-schema\n structure at WellKnownManifest.ext[\"ramp.attestation.claims_schema\"].")).describe("Three verification levels determine what is independently verifiable:\n Level 0 (no attestations): Resource may carry identifiers (DOI, IPTC GUID)\n for identification, but nothing is cryptographically verifiable.\n Only CDN delivery failure is auto-disputable.\n Level 1 (self-attested): Provider signs own claims with Ed25519 key.\n Agent can independently verify content hash and token count.\n CDN delivery failure + content hash mismatch are auto-disputable.\n Level 2 (third-party attested): Independent verification vendor crawled\n the resource and attested to its properties. Agent trusts the attestation\n (does not re-verify hash). Token count discrepancy is auto-disputable\n when corroborated by CDN response size.\n\n Multiple attestations may be present (e.g., provider self-attestation\n plus a third-party verification). Agents choose which to trust.").optional(), "dataAsOf": z.string().datetime({ offset: true }).describe("Not set for STATIC resources (content doesn't change) or LIVE\n resources (content doesn't exist yet).\n\n The Broker compares this against RequestConstraints.max_data_age\n to filter stale offers. Example: agent requests max_data_age = 7 days,\n Broker drops offers where now() - data_as_of > 7 days.").optional(), "deliveryMethod": z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource will be delivered.").default(0), "exchange": z.string().describe("Canonical domain of the Exchange that issued this offer (e.g.\n \"exchange.example.com\"). This is the execute-routing target: the agent (or\n a relaying Broker) sends the ExecuteTransaction call for this offer to this\n Exchange. Because it is an ordinary Offer field it falls inside the signed\n bytes (see `signature` below — the signature covers every field except\n `signature` / `signature_algorithm`), so an intermediary cannot redirect\n the execute call to a different Exchange without invalidating the offer.\n (RAMP-101: enables multi-Exchange fan-out routing from the offer itself,\n retiring the X-RAMP-Exchange-Endpoint transport header.)").default(""), "expiresAt": z.string().datetime({ offset: true }).describe("When this offer expires (ISO 8601).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "iabCategories": z.array(z.string()).describe("IAB Content Taxonomy category codes.\n Enables agents to filter offers by topic (e.g., \"only finance resources\").\n Uses IAB Content Taxonomy 3.1 codes.").optional(), "identity": z.object({ "c2paManifest": z.string().describe("Formats:\n Sidecar: HTTPS URI to a .c2pa manifest file\n Embedded: same URI as canonical_url (manifest is inside the asset)\n Content Credentials Cloud: https://contentcredentials.org/verify?uri=...").optional(), "c2paStatus": z.enum(["C2PA_STATUS_TRUSTED","C2PA_STATUS_VALID","C2PA_STATUS_INVALID","C2PA_STATUS_ABSENT"]).describe("The full C2PA validation details (signer identity, trust list,\n action history, training/mining status) are carried in a\n ResourceAttestation with c2pa.* claims — see ramp-c2pa-v1 profile.").optional(), "canonicalUrl": z.string().describe("Provider's authoritative URL for this resource (rel=\"canonical\").\n Always available. Different per provider for syndicated content.").optional(), "contentHash": z.string().describe("Level 1 (SimHash): computed by Exchange from extracted text.\n Agent verifies that fetched content is \"substantially similar.\"\n Tolerates dynamic page elements.\n\n Level 2 (SHA-256): computed by provider from deterministic payload.\n Agent verifies exact match. Requires provider to serve consistent\n content (e.g., API endpoint, static HTML, structured JSON).\n Mismatch = dispute. Commands premium pricing.").optional(), "doi": z.string().describe("Digital Object Identifier — persistent, never changes.").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "hashMethod": z.string().describe("Hash algorithm and verification level.\n Examples: \"simhash-v1\", \"minhash-v1\", \"sha256\", \"sha384\"").optional(), "iptcGuid": z.string().describe("IPTC NewsML-G2 globally unique identifier.\n Present when resource flows through news wire syndication (AP, Reuters).").optional(), "isni": z.string().describe("International Standard Name Identifier for the creator.").optional(), "resourceMutability": z.enum(["RESOURCE_MUTABILITY_STATIC","RESOURCE_MUTABILITY_DYNAMIC","RESOURCE_MUTABILITY_LIVE"]).describe("Drives hash verification behavior:\n STATIC: content_hash is stable. Agent SHOULD verify delivered content matches.\n DYNAMIC: content changes between offer and fetch (credit reports, drug databases).\n content_hash reflects state at offer generation time. Hash mismatch is\n expected and MUST NOT trigger automatic dispute.\n LIVE: content does not exist at offer time (streaming feeds, live broadcasts).\n content_hash is not applicable. The \"resource\" is the stream endpoint.\n\n Validated across 18 use cases: static content (articles, patents, legislation),\n dynamic data (credit reports, drug interactions, stock snapshots), and live\n streams (MarketData quotes, NPR broadcast, news monitoring feeds)."), "softBinding": z.string().describe("Algorithm specified in soft_binding_method. Values are algorithm-specific\n (e.g., perceptual hash hex string, watermark identifier).").optional(), "softBindingMethod": z.string().describe("Algorithm used for soft_binding.\n Examples: \"phash-v1\" (perceptual hash), \"c2pa-watermark\" (C2PA invisible\n watermark), \"chromaprint\" (audio fingerprint).").optional() }).catchall(z.union([z.string().describe("Formats:\n Sidecar: HTTPS URI to a .c2pa manifest file\n Embedded: same URI as canonical_url (manifest is inside the asset)\n Content Credentials Cloud: https://contentcredentials.org/verify?uri=..."), z.enum(["C2PA_STATUS_TRUSTED","C2PA_STATUS_VALID","C2PA_STATUS_INVALID","C2PA_STATUS_ABSENT"]).describe("The full C2PA validation details (signer identity, trust list,\n action history, training/mining status) are carried in a\n ResourceAttestation with c2pa.* claims — see ramp-c2pa-v1 profile."), z.string().describe("Provider's authoritative URL for this resource (rel=\"canonical\").\n Always available. Different per provider for syndicated content."), z.string().describe("Level 1 (SimHash): computed by Exchange from extracted text.\n Agent verifies that fetched content is \"substantially similar.\"\n Tolerates dynamic page elements.\n\n Level 2 (SHA-256): computed by provider from deterministic payload.\n Agent verifies exact match. Requires provider to serve consistent\n content (e.g., API endpoint, static HTML, structured JSON).\n Mismatch = dispute. Commands premium pricing."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.string().describe("Hash algorithm and verification level.\n Examples: \"simhash-v1\", \"minhash-v1\", \"sha256\", \"sha384\""), z.string().describe("IPTC NewsML-G2 globally unique identifier.\n Present when resource flows through news wire syndication (AP, Reuters)."), z.enum(["RESOURCE_MUTABILITY_STATIC","RESOURCE_MUTABILITY_DYNAMIC","RESOURCE_MUTABILITY_LIVE"]).describe("Drives hash verification behavior:\n STATIC: content_hash is stable. Agent SHOULD verify delivered content matches.\n DYNAMIC: content changes between offer and fetch (credit reports, drug databases).\n content_hash reflects state at offer generation time. Hash mismatch is\n expected and MUST NOT trigger automatic dispute.\n LIVE: content does not exist at offer time (streaming feeds, live broadcasts).\n content_hash is not applicable. The \"resource\" is the stream endpoint.\n\n Validated across 18 use cases: static content (articles, patents, legislation),\n dynamic data (credit reports, drug interactions, stock snapshots), and live\n streams (MarketData quotes, NPR broadcast, news monitoring feeds)."), z.string().describe("Algorithm specified in soft_binding_method. Values are algorithm-specific\n (e.g., perceptual hash hex string, watermark identifier)."), z.string().describe("Algorithm used for soft_binding.\n Examples: \"phash-v1\" (perceptual hash), \"c2pa-watermark\" (C2PA invisible\n watermark), \"chromaprint\" (audio fingerprint)."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["c2paManifest", "c2paStatus", "canonicalUrl", "contentHash", "doi", "ext", "extCritical", "hashMethod", "iptcGuid", "isni", "resourceMutability", "softBinding", "softBindingMethod"].includes(key) -if (key.match(new RegExp("^(c2pa_manifest)$"))) { +if (key.match(new RegExp("^(max_accesses)$"))) { evaluated = true -const result = z.string().describe("Formats:\n Sidecar: HTTPS URI to a .c2pa manifest file\n Embedded: same URI as canonical_url (manifest is inside the asset)\n Content Credentials Cloud: https://contentcredentials.org/verify?uri=...").safeParse(value[key]) +const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16630,9 +16596,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(c2pa_status)$"))) { +if (key.match(new RegExp("^(max_spend_cents)$"))) { evaluated = true -const result = z.enum(["C2PA_STATUS_TRUSTED","C2PA_STATUS_VALID","C2PA_STATUS_INVALID","C2PA_STATUS_ABSENT"]).describe("The full C2PA validation details (signer identity, trust list,\n action history, training/mining status) are carried in a\n ResourceAttestation with c2pa.* claims — see ramp-c2pa-v1 profile.").safeParse(value[key]) +const result = z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16644,9 +16610,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(canonical_url)$"))) { +if (key.match(new RegExp("^(principal_domain)$"))) { evaluated = true -const result = z.string().describe("Provider's authoritative URL for this resource (rel=\"canonical\").\n Always available. Different per provider for syndicated content.").safeParse(value[key]) +const result = z.string().describe("Who granted this delegation (domain for public key lookup).").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16658,9 +16624,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(content_hash)$"))) { +if (key.match(new RegExp("^(principal_id)$"))) { evaluated = true -const result = z.string().describe("Level 1 (SimHash): computed by Exchange from extracted text.\n Agent verifies that fetched content is \"substantially similar.\"\n Tolerates dynamic page elements.\n\n Level 2 (SHA-256): computed by provider from deterministic payload.\n Agent verifies exact match. Requires provider to serve consistent\n content (e.g., API endpoint, static HTML, structured JSON).\n Mismatch = dispute. Commands premium pricing.").safeParse(value[key]) +const result = z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16672,9 +16638,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(ext_critical)$"))) { +if (key.match(new RegExp("^(quota_period)$"))) { evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) +const result = z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at).").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16686,9 +16652,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(hash_method)$"))) { +if (key.match(new RegExp("^(revocation_uri)$"))) { evaluated = true -const result = z.string().describe("Hash algorithm and verification level.\n Examples: \"simhash-v1\", \"minhash-v1\", \"sha256\", \"sha384\"").safeParse(value[key]) +const result = z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff).").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16700,9 +16666,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(iptc_guid)$"))) { +if (key.match(new RegExp("^(token_format)$"))) { evaluated = true -const result = z.string().describe("IPTC NewsML-G2 globally unique identifier.\n Present when resource flows through news wire syndication (AP, Reuters).").safeParse(value[key]) +const result = z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16714,23 +16680,26 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(resource_mutability)$"))) { -evaluated = true -const result = z.enum(["RESOURCE_MUTABILITY_STATIC","RESOURCE_MUTABILITY_DYNAMIC","RESOURCE_MUTABILITY_LIVE"]).describe("Drives hash verification behavior:\n STATIC: content_hash is stable. Agent SHOULD verify delivered content matches.\n DYNAMIC: content changes between offer and fetch (credit reports, drug databases).\n content_hash reflects state at offer generation time. Hash mismatch is\n expected and MUST NOT trigger automatic dispute.\n LIVE: content does not exist at offer time (streaming feeds, live broadcasts).\n content_hash is not applicable. The \"resource\" is the stream endpoint.\n\n Validated across 18 use cases: static content (articles, patents, legislation),\n dynamic data (credit reports, drug interactions, stock snapshots), and live\n streams (MarketData quotes, NPR broadcast, news monitoring feeds).").safeParse(value[key]) +if (!evaluated) { +const result = z.never().safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, + message: `Invalid input: must match catchall schema`, params: { issues: result.error.issues } }) } } -if (key.match(new RegExp("^(soft_binding)$"))) { +} +}).describe("Optional delegation — present when the requester acts on behalf of\n another entity (user, organization, upstream agent).").optional(), "domain": z.string().describe("Domain the requester belongs to — used for public key lookup.\n Keys published at {domain}/.well-known/ramp.json (WellKnownManifest, role=ROLE_AGENT).").default(""), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "id": z.string().describe("Unique requester identifier (e.g., \"agent-research-bot-001\").").default(""), "name": z.string().describe("Human-readable name (e.g., \"Acme Research Assistant\").").optional(), "scopes": z.array(z.string()).max(64).describe("The Exchange filters its catalog to resources matching these scopes.\n Resources outside the scopes are not returned — the requester never\n learns they exist. This is the enforcement mechanism for both enterprise\n RBAC and open-market subscription entitlements.\n\n Scope format: colon-separated segments, \"{domain}:{permission}\" or\n \"{profile}:{permission}\", optionally multi-segment (\"dist:US:CA\");\n matching is segment-wise per the rule below (no implicit hierarchy).\n Examples:\n \"credit:read\" — can access credit reports\n \"subscription:marketdata-2026\" — has active MarketData subscription\n \"academic:*\" — full access to academic resources\n \"internal:reports\" — can access internal reports\n \"*\" — unrestricted (public Exchange default)\n\n Matching is SEGMENT-WISE (\":\" separated). A granted scope G covers a\n required scope R iff, segment by segment, each G segment equals the\n corresponding R segment or is \"*\"; a terminal \"*\" matches all remaining\n segments. There is NO implicit prefix match, and a grant NARROWER than\n the requirement does not cover it (G must be equal-to-or-broader than R).\n Examples: \"dist:*\" covers \"dist:US\" and \"dist:US:CA\"; \"dist:US:*\" covers\n \"dist:US:CA\" but not \"dist:EU\"; bare \"dist\" covers only \"dist\"; granted\n \"dist:US:CA\" does NOT cover required \"dist:US\"; \"*\" covers everything.\n This same rule governs LicenseTerm.scopes — one algorithm protocol-wide.\n\n When empty, Exchange applies its default access policy (typically\n returns all publicly available resources).").optional(), "type": z.enum(["REQUESTER_TYPE_AGENT","REQUESTER_TYPE_HUMAN_TOOL","REQUESTER_TYPE_SERVICE","REQUESTER_TYPE_DELEGATED","REQUESTER_TYPE_RESEARCH"]).describe("What kind of entity is making this request.") }).catchall(z.union([z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["billingRef", "delegation", "domain", "ext", "extCritical", "id", "name", "scopes", "type"].includes(key) +if (key.match(new RegExp("^(billing_ref)$"))) { evaluated = true -const result = z.string().describe("Algorithm specified in soft_binding_method. Values are algorithm-specific\n (e.g., perceptual hash hex string, watermark identifier).").safeParse(value[key]) +const result = z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16742,9 +16711,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(soft_binding_method)$"))) { +if (key.match(new RegExp("^(ext_critical)$"))) { evaluated = true -const result = z.string().describe("Algorithm used for soft_binding.\n Examples: \"phash-v1\" (perceptual hash), \"c2pa-watermark\" (C2PA invisible\n watermark), \"chromaprint\" (audio fingerprint).").safeParse(value[key]) +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16770,12 +16739,12 @@ ctx.addIssue({ } } } -}).describe("Resource identity for cross-exchange deduplication.\n Enables Brokers to recognize the same resource offered by\n different Exchanges and compare pricing.").optional(), "offerId": z.string().describe("Unique identifier for this offer, assigned by the Exchange.").default(""), "previews": z.array(z.object({ "duration": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Duration in seconds (for audio and video clips).").optional(), "height": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Height in pixels (images and video)").optional(), "mediaType": z.string().describe("MIME type of the preview.\n Examples: \"image/jpeg\", \"image/webp\", \"audio/mpeg\", \"video/mp4\",\n \"text/plain\", \"application/json\"").default(""), "size": z.string().describe("Size category hint. Agents use this to select the right preview\n without fetching all of them.\n Standard values:\n \"thumbnail\" — smallest useful preview (100–150px or 5–10s)\n \"preview\" — mid-size for evaluation (300–500px or 15–30s)\n \"sample\" — larger / more detailed (for data: 1–3 sample records)").optional(), "url": z.string().describe("URL to a preview asset (thumbnail, clip, snippet, sample).\n Served by the provider's CDN, not by the Exchange.").default(""), "width": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Dimensions in pixels (for images and video).").optional() }).catchall(z.union([z.string().describe("MIME type of the preview.\n Examples: \"image/jpeg\", \"image/webp\", \"audio/mpeg\", \"video/mp4\",\n \"text/plain\", \"application/json\"").default(""), z.never()])).superRefine((value, ctx) => { +}).describe("Requester identity — forwarded for authorization and audit.").optional(), "ver": z.string().describe("Protocol version").default("") }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response.").default(""), z.string().describe("Optional, non-authoritative correlation/audit key — a human-readable offer\n id. NOT used for verification: the Exchange verifies each item's reflected\n `offer.signature` over the presented Offer bytes, never this scalar. May be\n omitted; if set, it SHOULD match an item's `offer.offer_id`."), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["duration", "height", "mediaType", "size", "url", "width"].includes(key) -if (key.match(new RegExp("^(media_type)$"))) { +let evaluated = ["ext", "extCritical", "idempotencyKey", "items", "offerId", "requester", "ver"].includes(key) +if (key.match(new RegExp("^(ext_critical)$"))) { evaluated = true -const result = z.string().describe("MIME type of the preview.\n Examples: \"image/jpeg\", \"image/webp\", \"audio/mpeg\", \"video/mp4\",\n \"text/plain\", \"application/json\"").default("").safeParse(value[key]) +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16787,26 +16756,23 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) +if (key.match(new RegExp("^(idempotency_key)$"))) { +evaluated = true +const result = z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: must match catchall schema`, + message: `Invalid input: Key matching regex /${key}/ must match schema`, params: { issues: result.error.issues } }) } } -} -}).describe("The Exchange holds URLs (50–200 bytes per preview); the provider's\n CDN serves the actual bytes. This follows the universal pattern:\n Shutterstock (multi-size thumbnail URLs), Spotify (preview_url to\n 30s clip), IIIF (parameterized image URLs), OpenRTB (img.url + dims).\n\n Previews are free to fetch — no RAMP transaction required. They are\n the equivalent of looking at a book cover before buying. Providers\n MAY watermark visual previews or truncate text/audio previews.\n\n The Exchange populates preview URLs during catalog ingestion. Preview\n URLs MAY be signed with a short TTL to prevent hotlinking, or public\n (provider's choice). Agents fetch previews only when evaluating\n offers, not on every discovery query.")).describe("Per content type:\n Image: watermarked thumbnail (150–450px JPEG)\n Video: short clip (10–30s MP4, watermarked)\n Audio: short clip (15–30s MP3, low-bitrate or watermarked)\n Text: snippet or abstract (first 200 words as text/plain)\n Data: sample records (1–3 rows as application/json)\n Stream: optional frame capture or none (streams are priced by time)\n\n Modeled after Shutterstock (multi-size thumbnail URLs),\n Spotify (preview_url to 30s clip), IIIF (parameterized image URLs),\n and OpenRTB native (img.url + dimensions).").optional(), "pricing": z.object({ "currency": z.string().describe("ISO 4217 currency code (e.g. \"USD\", \"EUR\").").default(""), "estimatedQuantity": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count.").optional(), "licenseDurationMonths": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid.").optional(), "metering": z.enum(["PRICING_METERING_ONLINE","PRICING_METERING_NONE","PRICING_METERING_OFFLINE_SELF_REPORTED"]).describe("How usage is tracked for billing reconciliation.\n Absent = PRICING_METERING_ONLINE (default real-time tracking).\n NONE = one-time perpetual sale; no ReportUsage required after ExecuteTransaction.\n OFFLINE_SELF_REPORTED = agent self-reports physical-world consumption.").optional(), "model": z.enum(["PRICING_MODEL_FREE","PRICING_MODEL_PER_UNIT","PRICING_MODEL_FLAT"]).describe("Provider's pricing model."), "rate": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Price in the provider's model, as an exact decimal string — e.g. \"0.05\" =\n $0.05 per article. NOT a float: money is decimal to avoid binary rounding and\n to allow arbitrary sub-cent precision (e.g. \"0.0001234\"). Denominated in `currency`.").default(""), "unit": z.string().regex(new RegExp("^([a-z0-9-]+|[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)?$")).max(64).describe("The (ramp.v1.vocab) entries below are the SOLE authored source of the\n registered bare tokens. A buf plugin reads them structurally and emits the\n pricingunits constants + IsRegistered; ingest enforces membership from\n those. The CEL is STRUCTURE ONLY (empty / bare-form / vendor:namespaced) —\n it never lists the tokens, so it cannot drift from the registry.").optional(), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest).").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid."), z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest)."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["currency", "estimatedQuantity", "licenseDurationMonths", "metering", "model", "rate", "unit", "unitCost"].includes(key) -if (key.match(new RegExp("^(estimated_quantity)$"))) { +if (key.match(new RegExp("^(offer_id)$"))) { evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count.").safeParse(value[key]) +const result = z.string().describe("Optional, non-authoritative correlation/audit key — a human-readable offer\n id. NOT used for verification: the Exchange verifies each item's reflected\n `offer.signature` over the presented Offer bytes, never this scalar. May be\n omitted; if set, it SHOULD match an item's `offer.offer_id`.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16818,23 +16784,28 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(license_duration_months)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid.").safeParse(value[key]) +if (!evaluated) { +const result = z.never().safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, + message: `Invalid input: must match catchall schema`, params: { issues: result.error.issues } }) } } +} +}).describe("After selecting offers, the caller commits by sending this to the\n Exchange. Supports both single-offer and batch (multi-offer) modes.\n The Exchange validates eligibility, authorizes billing, creates\n delivery, and logs each transaction."); + +export const TransactionResponseSchema = z.object({ "agentIdentityHash": z.string().describe("Identity that a delivered retrieval_endpoint is bound to: the RFC 7638 JWK\n Thumbprint of the agent's Ed25519 request-signing key (see \"Retrieval-URL\n identity binding\" above). Shared across the request; set once.").default(""), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "items": z.array(z.object({ "billingId": z.string().describe("Billing reference.").default(""), "cost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["amount", "currency", "unitCost"].includes(key) if (key.match(new RegExp("^(unit_cost)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest).").safeParse(value[key]) +const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16860,7 +16831,7 @@ ctx.addIssue({ } } } -}).describe("Pricing for this offer. An offer represents a single licensing\n arrangement: each projected LicenseTerm yields its own offer, so this is\n that term's pricing (the authoritative copy lives in `terms[].pricing`).\n Used for cross-exchange comparison and Broker ranking. A resource with\n multiple alternative terms (e.g. dual-licensed) produces multiple separate\n offers, one per term — never one offer with a \"headline\" picked among them.").optional(), "reporting": z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { +}).describe("Cost for this item.").optional(), "deliveryMethod": z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0), "denialReason": z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed).").optional(), "expiresAt": z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").optional(), "offerId": z.string().describe("The offer_id this result is for.").default(""), "reportingObligation": z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) if (key.match(new RegExp("^(ext_critical)$"))) { @@ -16905,12 +16876,12 @@ ctx.addIssue({ } } } -}).describe("Post-usage reporting requirements for this offer.").optional(), "signature": z.string().describe("Because the signature covers `terms`, `pricing`, and `expires_at`, an\n intermediary (Broker) cannot tamper with price, restrictions, quotas,\n obligations, the expiry, or any licensing term without invalidating it.\n Agent SHOULD verify the signature (RFC 2119) against the Exchange's public\n key, and MUST reject an offer whose `expires_at` is in the past.").default(""), "signatureAlgorithm": z.string().describe("JWS algorithm. Always 'EdDSA' for Ed25519 via JWS Compact Serialization.").default(""), "subscriptionId": z.string().describe("If set, this offer is available under an existing subscription/deal.\n No per-request billing — usage tracked against subscription quota.\n Pricing.rate = 0 for subscription offers (zero marginal cost).\n The Broker SHOULD prefer subscription offers when available.").optional(), "subscriptionQuota": z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { +}).describe("Reporting requirements for this item.").optional(), "resourceTitle": z.string().describe("Resource title echoed from the Offer.").optional(), "restrictionMismatches": z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use.").optional(), "retrievalEndpoint": z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based.").optional(), "subscriptionId": z.string().describe("If under subscription, no per-request charge.").optional(), "subscriptionUnitValue": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) -if (key.match(new RegExp("^(quota_limit)$"))) { +let evaluated = ["amount", "currency", "unitCost"].includes(key) +if (key.match(new RegExp("^(unit_cost)$"))) { evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").safeParse(value[key]) +const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16922,37 +16893,26 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(quota_remaining)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").safeParse(value[key]) +if (!evaluated) { +const result = z.never().safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, + message: `Invalid input: must match catchall schema`, params: { issues: result.error.issues } }) } } -if (key.match(new RegExp("^(quota_used)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} } -if (key.match(new RegExp("^(resets_at)$"))) { +}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").optional(), "transactionId": z.string().describe("Exchange-assigned transaction identifier.").default("") }).catchall(z.union([z.string().describe("Billing reference.").default(""), z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0), z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed)."), z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires."), z.string().describe("The offer_id this result is for.").default(""), z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) +if (key.match(new RegExp("^(ext_critical)$"))) { evaluated = true -const result = z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").safeParse(value[key]) +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16964,9 +16924,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(subscription_id)$"))) { +if (key.match(new RegExp("^(required_fields)$"))) { evaluated = true -const result = z.string().describe("Subscription this quota applies to.").default("").safeParse(value[key]) +const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -16992,12 +16952,12 @@ ctx.addIssue({ } } } -}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Subscription quota state, when this offer is under a subscription.\n Enables the agent to see remaining quota before committing.\n Multiple entries when the subscription has independent quotas\n (e.g., access count + spend cap).").optional(), "terms": z.array(z.object({ "license": z.object({ "id": z.string().describe("Stable short identifier: SPDX short-id (\"GPL-3.0-only\"), TollBit cuid,\n or catalog doc-id. Used by agents and the vocab linter for known-license\n lookup; SHARE_ALIKE derivatives default their scope_license to this.").optional(), "immutable": z.boolean().describe("Data-labels TDL: the document at uri is versioned and will not change.").optional(), "name": z.string().describe("Human-readable name (licenseType, schema.org node name).").optional(), "uri": z.string().describe("\"MUST NOT URL-validate\" means do not REJECT non-URL schemes — it does NOT\n mean fetch blindly. A consumer that dereferences this URI MUST apply the\n SSRF countermeasures in the security threat model (T-LIC-1): scheme\n allowlist, block loopback/private/metadata addresses (resolve-then-check),\n fetch via an egress proxy, and treat the response as untrusted content.\n Verify the fetched bytes against `uri_digest` before use.").optional(), "uriDigest": z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").optional() }).catchall(z.union([z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest."), z.never()])).superRefine((value, ctx) => { +}).describe("Reporting requirements for this item."), z.string().describe("Resource title echoed from the Offer."), z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use."), z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based."), z.string().describe("If under subscription, no per-request charge."), z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["id", "immutable", "name", "uri", "uriDigest"].includes(key) -if (key.match(new RegExp("^(uri_digest)$"))) { +let evaluated = ["amount", "currency", "unitCost"].includes(key) +if (key.match(new RegExp("^(unit_cost)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").safeParse(value[key]) +const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17023,12 +16983,12 @@ ctx.addIssue({ } } } -}).describe("Governing license document. Authoritative for REFERENCE_ONLY terms, which\n MUST carry a License with a non-empty uri — a REFERENCE_ONLY term that\n references nothing is rejected at ingest.").optional(), "obligations": z.array(z.object({ "detail": z.string().describe("Free-form detail: attribution string, notice file URI, etc.\n OBLIGATION_KIND_OTHER without it → lint warning.").optional(), "kind": z.enum(["OBLIGATION_KIND_ATTRIBUTION","OBLIGATION_KIND_CONTRIBUTION","OBLIGATION_KIND_SHARE_ALIKE","OBLIGATION_KIND_NETWORK_COPYLEFT","OBLIGATION_KIND_NOTICE","OBLIGATION_KIND_OTHER"]).describe("What the agent must do."), "scopeLicense": z.object({ "id": z.string().describe("Stable short identifier: SPDX short-id (\"GPL-3.0-only\"), TollBit cuid,\n or catalog doc-id. Used by agents and the vocab linter for known-license\n lookup; SHARE_ALIKE derivatives default their scope_license to this.").optional(), "immutable": z.boolean().describe("Data-labels TDL: the document at uri is versioned and will not change.").optional(), "name": z.string().describe("Human-readable name (licenseType, schema.org node name).").optional(), "uri": z.string().describe("\"MUST NOT URL-validate\" means do not REJECT non-URL schemes — it does NOT\n mean fetch blindly. A consumer that dereferences this URI MUST apply the\n SSRF countermeasures in the security threat model (T-LIC-1): scheme\n allowlist, block loopback/private/metadata addresses (resolve-then-check),\n fetch via an egress proxy, and treat the response as untrusted content.\n Verify the fetched bytes against `uri_digest` before use.").optional(), "uriDigest": z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").optional() }).catchall(z.union([z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest."), z.never()])).superRefine((value, ctx) => { +}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown)."), z.string().describe("Exchange-assigned transaction identifier.").default(""), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["id", "immutable", "name", "uri", "uriDigest"].includes(key) -if (key.match(new RegExp("^(uri_digest)$"))) { +let evaluated = ["billingId", "cost", "deliveryMethod", "denialReason", "expiresAt", "offerId", "reportingObligation", "resourceTitle", "restrictionMismatches", "retrievalEndpoint", "subscriptionId", "subscriptionUnitValue", "transactionId"].includes(key) +if (key.match(new RegExp("^(billing_id)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").safeParse(value[key]) +const result = z.string().describe("Billing reference.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17040,26 +17000,23 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) +if (key.match(new RegExp("^(delivery_method)$"))) { +evaluated = true +const result = z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0).safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: must match catchall schema`, + message: `Invalid input: Key matching regex /${key}/ must match schema`, params: { issues: result.error.issues } }) } } -} -}).describe("The license that derivatives must be released under. REQUIRED for\n SHARE_ALIKE (rejected if absent), where it MUST identify a license — set\n `id` (SPDX short-id, the common copyleft case, often the term's own\n License.id) and/or `uri`. Because it is a License, a referenced `uri`\n inherits the uri_digest swap-protection rule: a uri without a digest is\n rejected, exactly as for any other license reference.").optional(), "trigger": z.enum(["OBLIGATION_TRIGGER_ON_USE","OBLIGATION_TRIGGER_ON_DISTRIBUTION","OBLIGATION_TRIGGER_ON_NETWORK_SERVICE","OBLIGATION_TRIGGER_ON_DERIVATIVE"]).describe("When the obligation activates.") }).catchall(z.union([z.object({ "id": z.string().describe("Stable short identifier: SPDX short-id (\"GPL-3.0-only\"), TollBit cuid,\n or catalog doc-id. Used by agents and the vocab linter for known-license\n lookup; SHARE_ALIKE derivatives default their scope_license to this.").optional(), "immutable": z.boolean().describe("Data-labels TDL: the document at uri is versioned and will not change.").optional(), "name": z.string().describe("Human-readable name (licenseType, schema.org node name).").optional(), "uri": z.string().describe("\"MUST NOT URL-validate\" means do not REJECT non-URL schemes — it does NOT\n mean fetch blindly. A consumer that dereferences this URI MUST apply the\n SSRF countermeasures in the security threat model (T-LIC-1): scheme\n allowlist, block loopback/private/metadata addresses (resolve-then-check),\n fetch via an egress proxy, and treat the response as untrusted content.\n Verify the fetched bytes against `uri_digest` before use.").optional(), "uriDigest": z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").optional() }).catchall(z.union([z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["id", "immutable", "name", "uri", "uriDigest"].includes(key) -if (key.match(new RegExp("^(uri_digest)$"))) { +if (key.match(new RegExp("^(denial_reason)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").safeParse(value[key]) +const result = z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed).").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17071,31 +17028,23 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) +if (key.match(new RegExp("^(expires_at)$"))) { +evaluated = true +const result = z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: must match catchall schema`, + message: `Invalid input: Key matching regex /${key}/ must match schema`, params: { issues: result.error.issues } }) } } -} -}).describe("The license that derivatives must be released under. REQUIRED for\n SHARE_ALIKE (rejected if absent), where it MUST identify a license — set\n `id` (SPDX short-id, the common copyleft case, often the term's own\n License.id) and/or `uri`. Because it is a License, a referenced `uri`\n inherits the uri_digest swap-protection rule: a uri without a digest is\n rejected, exactly as for any other license reference."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["detail", "kind", "scopeLicense", "trigger"].includes(key) -if (key.match(new RegExp("^(scope_license)$"))) { -evaluated = true -const result = z.object({ "id": z.string().describe("Stable short identifier: SPDX short-id (\"GPL-3.0-only\"), TollBit cuid,\n or catalog doc-id. Used by agents and the vocab linter for known-license\n lookup; SHARE_ALIKE derivatives default their scope_license to this.").optional(), "immutable": z.boolean().describe("Data-labels TDL: the document at uri is versioned and will not change.").optional(), "name": z.string().describe("Human-readable name (licenseType, schema.org node name).").optional(), "uri": z.string().describe("\"MUST NOT URL-validate\" means do not REJECT non-URL schemes — it does NOT\n mean fetch blindly. A consumer that dereferences this URI MUST apply the\n SSRF countermeasures in the security threat model (T-LIC-1): scheme\n allowlist, block loopback/private/metadata addresses (resolve-then-check),\n fetch via an egress proxy, and treat the response as untrusted content.\n Verify the fetched bytes against `uri_digest` before use.").optional(), "uriDigest": z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").optional() }).catchall(z.union([z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["id", "immutable", "name", "uri", "uriDigest"].includes(key) -if (key.match(new RegExp("^(uri_digest)$"))) { +if (key.match(new RegExp("^(offer_id)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^(sha256:[0-9a-f]{64}|sha384:[0-9a-f]{96}|sha512:[0-9a-f]{128})?$")).describe("The method MUST be a collision-resistant hash — sha256, sha384, or sha512.\n Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat\n the swap-protection this field exists for. The CEL is STRUCTURE ONLY\n (allowlisted prefix + matching hex length); presence (digest-when-uri) is\n enforced at ingest.").safeParse(value[key]) +const result = z.string().describe("The offer_id this result is for.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17107,21 +17056,28 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) +if (key.match(new RegExp("^(reporting_obligation)$"))) { +evaluated = true +const result = z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) +if (key.match(new RegExp("^(ext_critical)$"))) { +evaluated = true +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: must match catchall schema`, + message: `Invalid input: Key matching regex /${key}/ must match schema`, params: { issues: result.error.issues } }) } } -} -}).describe("The license that derivatives must be released under. REQUIRED for\n SHARE_ALIKE (rejected if absent), where it MUST identify a license — set\n `id` (SPDX short-id, the common copyleft case, often the term's own\n License.id) and/or `uri`. Because it is a License, a referenced `uri`\n inherits the uri_digest swap-protection rule: a uri without a digest is\n rejected, exactly as for any other license reference.").safeParse(value[key]) +if (key.match(new RegExp("^(required_fields)$"))) { +evaluated = true +const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17147,12 +17103,7 @@ ctx.addIssue({ } } } -}).describe("Examples:\n Attribution on display: cite the author whenever content is shown to a user.\n Share-alike on derivative: AI-generated content that incorporates this work\n must be released under the same license.\n Notice on distribution: include the copyright notice when distributing copies.")).describe("Post-use behavioral requirements.").optional(), "partLabel": z.string().describe("Informational human-readable name for this sub-part (sub-part terms).").optional(), "pricing": z.object({ "currency": z.string().describe("ISO 4217 currency code (e.g. \"USD\", \"EUR\").").default(""), "estimatedQuantity": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count.").optional(), "licenseDurationMonths": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid.").optional(), "metering": z.enum(["PRICING_METERING_ONLINE","PRICING_METERING_NONE","PRICING_METERING_OFFLINE_SELF_REPORTED"]).describe("How usage is tracked for billing reconciliation.\n Absent = PRICING_METERING_ONLINE (default real-time tracking).\n NONE = one-time perpetual sale; no ReportUsage required after ExecuteTransaction.\n OFFLINE_SELF_REPORTED = agent self-reports physical-world consumption.").optional(), "model": z.enum(["PRICING_MODEL_FREE","PRICING_MODEL_PER_UNIT","PRICING_MODEL_FLAT"]).describe("Provider's pricing model."), "rate": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Price in the provider's model, as an exact decimal string — e.g. \"0.05\" =\n $0.05 per article. NOT a float: money is decimal to avoid binary rounding and\n to allow arbitrary sub-cent precision (e.g. \"0.0001234\"). Denominated in `currency`.").default(""), "unit": z.string().regex(new RegExp("^([a-z0-9-]+|[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)?$")).max(64).describe("The (ramp.v1.vocab) entries below are the SOLE authored source of the\n registered bare tokens. A buf plugin reads them structurally and emits the\n pricingunits constants + IsRegistered; ingest enforces membership from\n those. The CEL is STRUCTURE ONLY (empty / bare-form / vendor:namespaced) —\n it never lists the tokens, so it cannot drift from the registry.").optional(), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest).").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid."), z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest)."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["currency", "estimatedQuantity", "licenseDurationMonths", "metering", "model", "rate", "unit", "unitCost"].includes(key) -if (key.match(new RegExp("^(estimated_quantity)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Estimated quantity in the metering unit.\n For text: token count. For video: duration in seconds.\n For documents: page count. For data: record count.").safeParse(value[key]) +}).describe("Reporting requirements for this item.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17164,9 +17115,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(license_duration_months)$"))) { +if (key.match(new RegExp("^(resource_title)$"))) { evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("License duration in months. How long the granted access remains valid.").safeParse(value[key]) +const result = z.string().describe("Resource title echoed from the Offer.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17178,9 +17129,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(unit_cost)$"))) { +if (key.match(new RegExp("^(restriction_mismatches)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Normalized cost per unit — the universal comparison metric, exact decimal string.\n For text: cost per token. For video: cost per second.\n For data: cost per record. For APIs: cost per call.\n Denominated in the Exchange's base_currency (from its WellKnownManifest).").safeParse(value[key]) +const result = z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -17192,1261 +17143,9 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Pricing for this term. REQUIRED for every term regardless of semantics —\n an agent cannot act on a priceless term, so absent Pricing is a validation\n error at ingest. model = FREE must be stated explicitly (absent Pricing is\n not free). A REFERENCE_ONLY term states its price here too; its License\n governs the human-readable terms but does not replace the machine-readable\n price."), "quotas": z.array(z.object({ "limit": z.coerce.number().int().gte(1).describe("Maximum allowed value in the given window. A quota of 0 grants\n nothing — express \"no access\" by omitting the term, not a zero quota."), "metric": z.string().regex(new RegExp("^([a-z0-9-]+|[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)$")).max(64).describe("The (ramp.v1.vocab) entries below are the SOLE authored source of the\n registered bare metric tokens. A buf plugin reads them structurally and\n emits the quotametrics constants + IsRegistered; ingest enforces membership\n from those. The CEL is STRUCTURE ONLY (non-empty bare token or\n vendor:namespaced) — it never lists the tokens, so it cannot drift.\n\n Token meanings:\n display-words Words of content text rendered to an end user.\n impressions Times the content is displayed to an end user.\n tokens LLM output tokens generated using this content.\n input-tokens LLM input tokens consumed from this content.\n units-manufactured Physical units manufactured from this design/pattern.\n accesses Distinct content access / retrieval events.\n copies Digital or physical copies produced.\n seats Distinct named users licensed to access the content."), "window": z.enum(["QUOTA_WINDOW_HOURLY","QUOTA_WINDOW_DAILY","QUOTA_WINDOW_MONTHLY","QUOTA_WINDOW_TOTAL"]).describe("Time window over which the limit accumulates.") }).strict().describe("Quotas limit how much a licensee may consume before the term expires or\n must be renegotiated. They are NOT billing quantities — billing is in Pricing.\n\n The metric vocabulary is authored ONLY in the (ramp.v1.vocab) entries on\n Quota.metric below; the quotametrics constants + IsRegistered derive from it.")).describe("Usage caps. The agent must not exceed any individual Quota.").optional(), "restrictions": z.array(z.object({ "advisory": z.boolean().describe("Fail-closed by default. When false (the default), this restriction is\n BINDING: an agent that cannot evaluate every token in it — including an\n unknown vendor token — MUST decline the term. Set advisory = true to\n downgrade an unverifiable restriction to non-blocking. This deliberately\n inverts the COSE-`crit` opt-in default: a license restriction a consumer\n does not understand should stop it, not be silently ignored.").default(false), "kind": z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"]).describe("Which dimension this restriction applies to."), "permitted": z.array(z.string().regex(new RegExp("^[A-Za-z0-9._:*-]+$")).min(1).max(64)).max(64).describe("Tokens allowed on this axis. Empty = all permitted.\n For FUNCTION: \"ai-input\", \"ai-train\", \"search\", \"editorial\", \"commercial\", …\n For GEOGRAPHY: \"US\", \"DE\", \"EU\", \"EEA\", \"*\", …\n For USER_TYPE: \"individual\", \"academic\", \"commercial_entity\", …").optional(), "prohibited": z.array(z.string().regex(new RegExp("^[A-Za-z0-9._:*-]+$")).min(1).max(64)).max(64).describe("Tokens blocked on this axis. Takes precedence over permitted[].").optional() }).strict().describe("Restrictions model allowed and prohibited values on one axis (function,\n geography, or user-type). They are validated and normalized at ingest and\n RIDE ON THE OFFER: the AGENT is the responsible party — it self-selects the\n term whose restrictions it can honour and bears compliance, and enforcement\n happens downstream at accept → report → reconcile. Restrictions are NOT an\n Exchange-side gate the requester must pass to see a term.\n\n An Exchange or Broker MAY, purely as a CONVENIENCE, pre-filter the offers it\n returns against the limits the query states in ResourceQuery.acceptable_restrictions\n (the same RestrictionKind axes/vocabulary the terms use) — e.g. an agent that\n only wants US-eligible content can ask the Exchange to skip the rest so it\n doesn't pay to discover offers it would never accept. That filter is advisory and\n optional: a different Broker may not apply it, and it is a recommendation\n matched to the request, never an enforcement verdict. When an Exchange does\n drop offers this way it MAY signal it via OfferAbsenceReason.RESTRICTION_FILTERED\n (with the axes in OfferGroup.restriction_filters). Term visibility is otherwise\n gated only by resource_id/URI and delegation scope coverage — see\n LicenseTerm.scopes.\n\n Reading a restriction:\n A value is in-scope when it matches at least one permitted[] token\n AND matches none of the prohibited[] tokens.\n Empty permitted[] = any value is permitted on this axis.\n Empty prohibited[] = nothing is explicitly prohibited.\n\n Vocabulary sources (authored on the RestrictionKind enum values via\n (ramp.v1.vocab_enum); the functiontokens / geographytokens / usertypes\n constants + IsRegistered derive from them):\n FUNCTION — RSL 1.0 AI-use vocabulary + established IP/copyright terms\n GEOGRAPHY — ISO 3166-1 alpha-2 (structural) + the specials *, EU, EEA\n USER_TYPE — RAMP user/organization categories")).describe("Usage restrictions (function, geography, user-type).\n Multiple restrictions are AND-combined — the agent must satisfy all of them.").optional(), "scopes": z.array(z.string()).max(64).describe("Coverage uses the SAME matching rule as Requester/delegation scopes:\n segment-wise (\":\" separated), each granted segment must equal the\n corresponding required segment or be \"*\", a terminal \"*\" matches all\n remaining segments, and there is NO implicit prefix match (a grant\n narrower than the requirement does not cover it). \"dist:*\" covers\n \"dist:US\" and \"dist:US:CA\"; \"dist\" covers only \"dist\". There is exactly\n one scope-matching algorithm across the protocol.").optional(), "semantics": z.enum(["TERM_SEMANTICS_ENUMERATED","TERM_SEMANTICS_REFERENCE_ONLY"]).describe("How to interpret the machine fields.") }).catchall(z.union([z.string().describe("Informational human-readable name for this sub-part (sub-part terms)."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["license", "obligations", "partLabel", "pricing", "quotas", "restrictions", "scopes", "semantics"].includes(key) -if (key.match(new RegExp("^(part_label)$"))) { -evaluated = true -const result = z.string().describe("Informational human-readable name for this sub-part (sub-part terms).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("One LicenseTerm describes one complete access arrangement for a resource.\n A resource carries zero or more terms; having multiple terms is the normal\n case (one per use category, user type, or commercial arrangement).\n\n The same LicenseTerm shape appears at ingestion (ResourceEntry.terms) and\n at emission (Offer.terms). The Exchange stores what the publisher pushed\n and surfaces it on discovery, so agents see the same terms the publisher\n declared — no translation or reformulation.\n\n Validation rules:\n - Pricing MUST be present on EVERY term, regardless of semantics.\n Absent Pricing → reject at ingest: an agent cannot act on a term with\n no price. This holds for REFERENCE_ONLY too — its License governs the\n human-readable terms, but the machine-readable price is still stated\n here, not deferred to the document.\n - model=FREE must be explicit. Absent Pricing ≠ free. A term may be FREE\n under an arbitrary license; the agent still needs the price stated so it\n knows the access is free rather than unpriced.\n - REFERENCE_ONLY terms MUST carry a License with a non-empty uri. A\n REFERENCE_ONLY term that references no document is meaningless → reject\n at ingest.\n - Restriction tokens are validated against the vocab registry.\n Unknown tokens produce a PushResourcesResponse.warnings[] entry\n but do NOT cause rejection (forward-compatible).")).describe("Licensing terms for this offer, sourced from the publisher's ResourceEntry.\n Multiple terms when the resource has different arrangements by use case.\n See: Universal Licensing Core section.").optional() }).catchall(z.union([z.string().datetime({ offset: true }).describe("Not set for STATIC resources (content doesn't change) or LIVE\n resources (content doesn't exist yet).\n\n The Broker compares this against RequestConstraints.max_data_age\n to filter stale offers. Example: agent requests max_data_age = 7 days,\n Broker drops offers where now() - data_as_of > 7 days."), z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource will be delivered.").default(0), z.string().datetime({ offset: true }).describe("When this offer expires (ISO 8601)."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("IAB Content Taxonomy category codes.\n Enables agents to filter offers by topic (e.g., \"only finance resources\").\n Uses IAB Content Taxonomy 3.1 codes."), z.string().describe("Unique identifier for this offer, assigned by the Exchange.").default(""), z.string().describe("JWS algorithm. Always 'EdDSA' for Ed25519 via JWS Compact Serialization.").default(""), z.string().describe("If set, this offer is available under an existing subscription/deal.\n No per-request billing — usage tracked against subscription quota.\n Pricing.rate = 0 for subscription offers (zero marginal cost).\n The Broker SHOULD prefer subscription offers when available."), z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) -if (key.match(new RegExp("^(quota_limit)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_remaining)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_used)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(resets_at)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_id)$"))) { -evaluated = true -const result = z.string().describe("Subscription this quota applies to.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Subscription quota state, when this offer is under a subscription.\n Enables the agent to see remaining quota before committing.\n Multiple entries when the subscription has independent quotas\n (e.g., access count + spend cap)."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["attestations", "dataAsOf", "deliveryMethod", "exchange", "expiresAt", "ext", "extCritical", "iabCategories", "identity", "offerId", "previews", "pricing", "reporting", "signature", "signatureAlgorithm", "subscriptionId", "subscriptionQuota", "terms"].includes(key) -if (key.match(new RegExp("^(data_as_of)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("Not set for STATIC resources (content doesn't change) or LIVE\n resources (content doesn't exist yet).\n\n The Broker compares this against RequestConstraints.max_data_age\n to filter stale offers. Example: agent requests max_data_age = 7 days,\n Broker drops offers where now() - data_as_of > 7 days.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(delivery_method)$"))) { -evaluated = true -const result = z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource will be delivered.").default(0).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(expires_at)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("When this offer expires (ISO 8601).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(iab_categories)$"))) { -evaluated = true -const result = z.array(z.string()).describe("IAB Content Taxonomy category codes.\n Enables agents to filter offers by topic (e.g., \"only finance resources\").\n Uses IAB Content Taxonomy 3.1 codes.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(offer_id)$"))) { -evaluated = true -const result = z.string().describe("Unique identifier for this offer, assigned by the Exchange.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(signature_algorithm)$"))) { -evaluated = true -const result = z.string().describe("JWS algorithm. Always 'EdDSA' for Ed25519 via JWS Compact Serialization.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_id)$"))) { -evaluated = true -const result = z.string().describe("If set, this offer is available under an existing subscription/deal.\n No per-request billing — usage tracked against subscription quota.\n Pricing.rate = 0 for subscription offers (zero marginal cost).\n The Broker SHOULD prefer subscription offers when available.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_quota)$"))) { -evaluated = true -const result = z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) -if (key.match(new RegExp("^(quota_limit)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_remaining)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_used)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(resets_at)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_id)$"))) { -evaluated = true -const result = z.string().describe("Subscription this quota applies to.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Subscription quota state, when this offer is under a subscription.\n Enables the agent to see remaining quota before committing.\n Multiple entries when the subscription has independent quotas\n (e.g., access count + spend cap).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Single-offer mode: the FULL signed Offer, reflected back exactly as\n received at discovery. The Exchange verifies `offer.signature` (which\n covers pricing, terms, and expires_at) over these presented bytes against\n its own key — a stateless, self-contained bearer token, with no\n reconstruct-from-catalog. Set this XOR `items` (see message rule).").optional(), "offerId": z.string().describe("Optional, non-authoritative correlation/audit key — the human-readable id\n of the committed offer. NOT used for verification: the Exchange verifies\n the reflected `offer.signature` over the presented Offer bytes, never this\n scalar. May be omitted; if set, it SHOULD match `offer.offer_id`.").optional(), "requester": z.object({ "billingRef": z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution.").optional(), "delegation": z.object({ "expiresAt": z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens.").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "issuer": z.string().describe("Token issuer. OIDC issuer URL or GNAP grant server URL.\n Exchange uses this for JWT validation (OIDC discovery → JWKS)\n or GNAP token introspection.").optional(), "maxAccesses": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling.").optional(), "maxSpendCents": z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap.").optional(), "principalDomain": z.string().describe("Who granted this delegation (domain for public key lookup).").default(""), "principalId": z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default(""), "quotaPeriod": z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at).").optional(), "revocationUri": z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff).").optional(), "scopes": z.array(z.string()).describe("Scopes granted by this delegation. MUST be a subset of the\n principal's own scopes (attenuation — can only narrow, not widen).").optional(), "token": z.string().regex(new RegExp("^[A-Za-z0-9+/]*={0,2}$")).describe("Token bytes. A JWT (base64url-encoded JWS) by default, or a Biscuit (binary,\n base64-encoded) when token_format is \"biscuit-v3\".").default(null), "tokenFormat": z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default("") }).catchall(z.union([z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling."), z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap."), z.string().describe("Who granted this delegation (domain for public key lookup).").default(""), z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default(""), z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at)."), z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff)."), z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["expiresAt", "ext", "extCritical", "issuer", "maxAccesses", "maxSpendCents", "principalDomain", "principalId", "quotaPeriod", "revocationUri", "scopes", "token", "tokenFormat"].includes(key) -if (key.match(new RegExp("^(expires_at)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("When this delegation expires. Exchange MUST reject expired tokens.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(max_accesses)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Maximum number of accesses allowed under this delegation.\n Exchange tracks cumulative access count against this cap.\n Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit.\n For subscriptions with \"10,000 accesses/month\", this carries the ceiling.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(max_spend_cents)$"))) { -evaluated = true -const result = z.coerce.number().int().describe("Maximum spend in currency minor units (e.g., cents for USD).\n Exchange tracks cumulative spend against this cap.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(principal_domain)$"))) { -evaluated = true -const result = z.string().describe("Who granted this delegation (domain for public key lookup).").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(principal_id)$"))) { -evaluated = true -const result = z.string().describe("Principal's identifier (e.g., \"user@acme.com\", \"marketdata.example.com\").").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_period)$"))) { -evaluated = true -const result = z.string().describe("Quota reset period. How often the access/spend counters reset.\n Example: 720h (30 days) for monthly subscriptions.\n When absent, the quota is lifetime (bounded only by expires_at).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(revocation_uri)$"))) { -evaluated = true -const result = z.string().describe("Optional: URI for real-time revocation checking.\n Exchange MAY check this for high-value transactions.\n Not checked for routine low-value access (performance tradeoff).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(token_format)$"))) { -evaluated = true -const result = z.string().describe("Token format: \"jwt\" (default) or \"biscuit-v3\" (optional, for deep\n multi-hop offline attenuation). Empty is treated as \"jwt\".").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Optional delegation — present when the requester acts on behalf of\n another entity (user, organization, upstream agent).").optional(), "domain": z.string().describe("Domain the requester belongs to — used for public key lookup.\n Keys published at {domain}/.well-known/ramp.json (WellKnownManifest, role=ROLE_AGENT).").default(""), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "id": z.string().describe("Unique requester identifier (e.g., \"agent-research-bot-001\").").default(""), "name": z.string().describe("Human-readable name (e.g., \"Acme Research Assistant\").").optional(), "scopes": z.array(z.string()).max(64).describe("The Exchange filters its catalog to resources matching these scopes.\n Resources outside the scopes are not returned — the requester never\n learns they exist. This is the enforcement mechanism for both enterprise\n RBAC and open-market subscription entitlements.\n\n Scope format: colon-separated segments, \"{domain}:{permission}\" or\n \"{profile}:{permission}\", optionally multi-segment (\"dist:US:CA\");\n matching is segment-wise per the rule below (no implicit hierarchy).\n Examples:\n \"credit:read\" — can access credit reports\n \"subscription:marketdata-2026\" — has active MarketData subscription\n \"academic:*\" — full access to academic resources\n \"internal:reports\" — can access internal reports\n \"*\" — unrestricted (public Exchange default)\n\n Matching is SEGMENT-WISE (\":\" separated). A granted scope G covers a\n required scope R iff, segment by segment, each G segment equals the\n corresponding R segment or is \"*\"; a terminal \"*\" matches all remaining\n segments. There is NO implicit prefix match, and a grant NARROWER than\n the requirement does not cover it (G must be equal-to-or-broader than R).\n Examples: \"dist:*\" covers \"dist:US\" and \"dist:US:CA\"; \"dist:US:*\" covers\n \"dist:US:CA\" but not \"dist:EU\"; bare \"dist\" covers only \"dist\"; granted\n \"dist:US:CA\" does NOT cover required \"dist:US\"; \"*\" covers everything.\n This same rule governs LicenseTerm.scopes — one algorithm protocol-wide.\n\n When empty, Exchange applies its default access policy (typically\n returns all publicly available resources).").optional(), "type": z.enum(["REQUESTER_TYPE_AGENT","REQUESTER_TYPE_HUMAN_TOOL","REQUESTER_TYPE_SERVICE","REQUESTER_TYPE_DELEGATED","REQUESTER_TYPE_RESEARCH"]).describe("What kind of entity is making this request.") }).catchall(z.union([z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["billingRef", "delegation", "domain", "ext", "extCritical", "id", "name", "scopes", "type"].includes(key) -if (key.match(new RegExp("^(billing_ref)$"))) { -evaluated = true -const result = z.string().describe("Opaque billing reference linking this requester to the Exchange's (and,\n through the Exchange, the publisher's) billing/accounting systems — e.g. a\n billing account, PO number, or cost center. NOT an entitlement or\n subscription credential: access is governed by scopes and delegation, and\n identity by the request signature. The Exchange uses it only for invoicing\n and cost attribution.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Requester identity — forwarded for authorization and audit.").optional(), "ver": z.string().describe("Protocol version").default("") }).catchall(z.union([z.object({ "signature": z.string().min(1).describe("Hex-encoded detached Ed25519 signature over the deterministic-marshaled\n AgentAcceptancePayload bytes."), "signatureAlgorithm": z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("") }).catchall(z.union([z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["signature", "signatureAlgorithm"].includes(key) -if (key.match(new RegExp("^(signature_algorithm)$"))) { -evaluated = true -const result = z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Single-offer mode: the agent's detached acceptance signature over the\n accepted `offer` (RAMP-102 §1). Optional on the wire (additive,\n backward-compatible); the Exchange enforces presence at the service layer\n for relayed requests. Signed bytes = deterministic AgentAcceptancePayload\n {offer_sig=offer.signature, requester_id=requester.id,\n requester_domain=requester.domain, idempotency_key=idempotency_key}."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response.").default(""), z.string().describe("Optional, non-authoritative correlation/audit key — the human-readable id\n of the committed offer. NOT used for verification: the Exchange verifies\n the reflected `offer.signature` over the presented Offer bytes, never this\n scalar. May be omitted; if set, it SHOULD match `offer.offer_id`."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["agentAcceptance", "ext", "extCritical", "idempotencyKey", "items", "offer", "offerId", "requester", "ver"].includes(key) -if (key.match(new RegExp("^(agent_acceptance)$"))) { -evaluated = true -const result = z.object({ "signature": z.string().min(1).describe("Hex-encoded detached Ed25519 signature over the deterministic-marshaled\n AgentAcceptancePayload bytes."), "signatureAlgorithm": z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("") }).catchall(z.union([z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["signature", "signatureAlgorithm"].includes(key) -if (key.match(new RegExp("^(signature_algorithm)$"))) { -evaluated = true -const result = z.string().describe("Signature algorithm; \"EdDSA\" for Ed25519.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Single-offer mode: the agent's detached acceptance signature over the\n accepted `offer` (RAMP-102 §1). Optional on the wire (additive,\n backward-compatible); the Exchange enforces presence at the service layer\n for relayed requests. Signed bytes = deterministic AgentAcceptancePayload\n {offer_sig=offer.signature, requester_id=requester.id,\n requester_domain=requester.domain, idempotency_key=idempotency_key}.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(idempotency_key)$"))) { -evaluated = true -const result = z.string().min(1).describe("Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns\n the original result rather than re-executing. The transaction's durable\n identity is the Exchange-assigned transaction_id in the response.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(offer_id)$"))) { -evaluated = true -const result = z.string().describe("Optional, non-authoritative correlation/audit key — the human-readable id\n of the committed offer. NOT used for verification: the Exchange verifies\n the reflected `offer.signature` over the presented Offer bytes, never this\n scalar. May be omitted; if set, it SHOULD match `offer.offer_id`.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("After selecting offers, the caller commits by sending this to the\n Exchange. Supports both single-offer and batch (multi-offer) modes.\n The Exchange validates eligibility, authorizes billing, creates\n delivery, and logs each transaction."); - -export const TransactionResponseSchema = z.object({ "agentIdentityHash": z.string().describe("Identity that retrieval_endpoint is bound to: the RFC 7638 JWK Thumbprint of\n the agent's Ed25519 request-signing key (see \"Retrieval-URL identity binding\"\n above). Empty string when absent; non-empty iff a signed retrieval_endpoint\n is present. Delivery-endpoint enforcement of the binding is OPTIONAL.").default(""), "billingId": z.string().describe("Billing reference").optional(), "cost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Transaction cost").optional(), "deliveryMethod": z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered in this transaction.").default(0), "expiresAt": z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "items": z.array(z.object({ "billingId": z.string().describe("Billing reference.").default(""), "cost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Cost for this item.").optional(), "deliveryMethod": z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0), "denialReason": z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed).").optional(), "expiresAt": z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").optional(), "offerId": z.string().describe("The offer_id this result is for.").default(""), "reportingObligation": z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(required_fields)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Reporting requirements for this item.").optional(), "resourceTitle": z.string().describe("Resource title echoed from the Offer.").optional(), "restrictionMismatches": z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use.").optional(), "retrievalEndpoint": z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based.").optional(), "subscriptionId": z.string().describe("If under subscription, no per-request charge.").optional(), "subscriptionUnitValue": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").optional(), "transactionId": z.string().describe("Exchange-assigned transaction identifier.").default("") }).catchall(z.union([z.string().describe("Billing reference.").default(""), z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0), z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed)."), z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires."), z.string().describe("The offer_id this result is for.").default(""), z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(required_fields)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Reporting requirements for this item."), z.string().describe("Resource title echoed from the Offer."), z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use."), z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based."), z.string().describe("If under subscription, no per-request charge."), z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown)."), z.string().describe("Exchange-assigned transaction identifier.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["billingId", "cost", "deliveryMethod", "denialReason", "expiresAt", "offerId", "reportingObligation", "resourceTitle", "restrictionMismatches", "retrievalEndpoint", "subscriptionId", "subscriptionUnitValue", "transactionId"].includes(key) -if (key.match(new RegExp("^(billing_id)$"))) { -evaluated = true -const result = z.string().describe("Billing reference.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(delivery_method)$"))) { -evaluated = true -const result = z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered for this item.").default(0).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(denial_reason)$"))) { -evaluated = true -const result = z.enum(["DENIAL_REASON_BILLING_REF_INACTIVE","DENIAL_REASON_INSUFFICIENT_BALANCE","DENIAL_REASON_RATE_LIMITED","DENIAL_REASON_CONTENT_UNAVAILABLE","DENIAL_REASON_RESTRICTION_NOT_SATISFIED","DENIAL_REASON_REPORTING_OVERDUE","DENIAL_REASON_OFFER_EXPIRED","DENIAL_REASON_SIGNATURE_INVALID","DENIAL_REASON_QUOTA_EXCEEDED","DENIAL_REASON_DELEGATION_INVALID","DENIAL_REASON_SCOPE_INSUFFICIENT","DENIAL_REASON_ENTITLEMENT_MISSING","DENIAL_REASON_ENTITLEMENT_MALFORMED","DENIAL_REASON_ENTITLEMENT_EXPIRED","DENIAL_REASON_ENTITLEMENT_WRONG_BUYER","DENIAL_REASON_SUBSCRIPTION_LAPSED","DENIAL_REASON_ENTITLEMENT_NOT_GRANTED","DENIAL_REASON_ENTITLEMENT_STALE_ATTENUATION"]).describe("Set if this specific item was denied (others may succeed).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(expires_at)$"))) { -evaluated = true -const result = z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(offer_id)$"))) { -evaluated = true -const result = z.string().describe("The offer_id this result is for.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(reporting_obligation)$"))) { -evaluated = true -const result = z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(required_fields)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Reporting requirements for this item.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(resource_title)$"))) { -evaluated = true -const result = z.string().describe("Resource title echoed from the Offer.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(restriction_mismatches)$"))) { -evaluated = true -const result = z.array(z.enum(["RESTRICTION_KIND_FUNCTION","RESTRICTION_KIND_GEOGRAPHY","RESTRICTION_KIND_USER_TYPE","RESTRICTION_KIND_OTHER"])).describe("When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the\n request failed, in the same RestrictionKind vocabulary the terms use.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(retrieval_endpoint)$"))) { -evaluated = true -const result = z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_id)$"))) { -evaluated = true -const result = z.string().describe("If under subscription, no per-request charge.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(subscription_unit_value)$"))) { -evaluated = true -const result = z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(transaction_id)$"))) { -evaluated = true -const result = z.string().describe("Exchange-assigned transaction identifier.").default("").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("TransactionResultItem — Result for a single offer in a batch transaction.")).describe("Batch mode: per-offer results.").optional(), "reportingObligation": z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(required_fields)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Reporting requirements attached to this delivery.").optional(), "resourceTitle": z.string().describe("Resource title echoed from the Offer (for logging/display).").optional(), "retrievalEndpoint": z.string().describe("Signed retrieval URL the agent uses to fetch the purchased resource.\n Bound to agent_identity_hash; expires at expires_at. Absent on denial\n and on transactions whose delivery_method is not signed-URL-based.").optional(), "subscriptionId": z.string().describe("If set, this transaction was fulfilled under a subscription/deal.\n No per-request charge — usage tracked against subscription quota.").optional(), "subscriptionQuota": z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) -if (key.match(new RegExp("^(quota_limit)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_remaining)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(quota_used)$"))) { -evaluated = true -const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(resets_at)$"))) { +if (key.match(new RegExp("^(retrieval_endpoint)$"))) { evaluated = true -const result = z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").safeParse(value[key]) +const result = z.string().describe("Signed retrieval URL for this item. Bound to the requesting agent's identity\n via the parent TransactionResponse.agent_identity_hash (shared across all\n batch items); expires at expires_at. Absent if this item was denied or its\n delivery_method is not signed-URL-based.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18460,7 +17159,7 @@ ctx.addIssue({ } if (key.match(new RegExp("^(subscription_id)$"))) { evaluated = true -const result = z.string().describe("Subscription this quota applies to.").default("").safeParse(value[key]) +const result = z.string().describe("If under subscription, no per-request charge.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18472,52 +17171,9 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Post-transaction quota state. Tells the agent how much quota remains\n after this transaction. Enables proactive throttling (\"1 access left\").\n Multiple entries for multi-dimensional quotas.").optional(), "subscriptionUnitValue": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { +if (key.match(new RegExp("^(subscription_unit_value)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").optional(), "totalCost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { +const result = z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["amount", "currency", "unitCost"].includes(key) if (key.match(new RegExp("^(unit_cost)$"))) { @@ -18548,12 +17204,7 @@ ctx.addIssue({ } } } -}).describe("Batch mode: aggregate cost across all items.").optional(), "transactionId": z.string().describe("Single-offer result.\n For batch mode, these may be empty — check `items` instead.").optional(), "ver": z.string().describe("Protocol version").default("") }).catchall(z.union([z.string().describe("Identity that retrieval_endpoint is bound to: the RFC 7638 JWK Thumbprint of\n the agent's Ed25519 request-signing key (see \"Retrieval-URL identity binding\"\n above). Empty string when absent; non-empty iff a signed retrieval_endpoint\n is present. Delivery-endpoint enforcement of the binding is OPTIONAL.").default(""), z.string().describe("Billing reference"), z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered in this transaction.").default(0), z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires."), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) +}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18565,9 +17216,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(required_fields)$"))) { +if (key.match(new RegExp("^(transaction_id)$"))) { evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) +const result = z.string().describe("Exchange-assigned transaction identifier.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18593,7 +17244,7 @@ ctx.addIssue({ } } } -}).describe("Reporting requirements attached to this delivery."), z.string().describe("Resource title echoed from the Offer (for logging/display)."), z.string().describe("Signed retrieval URL the agent uses to fetch the purchased resource.\n Bound to agent_identity_hash; expires at expires_at. Absent on denial\n and on transactions whose delivery_method is not signed-URL-based."), z.string().describe("If set, this transaction was fulfilled under a subscription/deal.\n No per-request charge — usage tracked against subscription quota."), z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { +}).describe("TransactionResultItem — Result for a single offer in a batch transaction.")).describe("Per-offer results (one entry per committed item, in original order).").optional(), "subscriptionQuota": z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) if (key.match(new RegExp("^(quota_limit)$"))) { @@ -18680,7 +17331,7 @@ ctx.addIssue({ } } } -}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Post-transaction quota state. Tells the agent how much quota remains\n after this transaction. Enables proactive throttling (\"1 access left\").\n Multiple entries for multi-dimensional quotas."), z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { +}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Post-transaction quota state. Tells the agent how much quota remains\n after this transaction. Enables proactive throttling (\"1 access left\").\n Multiple entries for multi-dimensional quotas.").optional(), "totalCost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { for (const key in value) { let evaluated = ["amount", "currency", "unitCost"].includes(key) if (key.match(new RegExp("^(unit_cost)$"))) { @@ -18711,12 +17362,12 @@ ctx.addIssue({ } } } -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown)."), z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { +}).describe("Aggregate cost across all items.").optional(), "ver": z.string().describe("Protocol version").default("") }).catchall(z.union([z.string().describe("Identity that a delivered retrieval_endpoint is bound to: the RFC 7638 JWK\n Thumbprint of the agent's Ed25519 request-signing key (see \"Retrieval-URL\n identity binding\" above). Shared across the request; set once.").default(""), z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.object({ "quotaLimit": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").optional(), "quotaRemaining": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").optional(), "quotaUsed": z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").optional(), "resetsAt": z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").optional(), "subscriptionId": z.string().describe("Subscription this quota applies to.").default(""), "unit": z.string().describe("What is being metered. Distinguishes access count quotas from\n spend quotas from burst limits.\n Standard values: \"accesses\", \"tokens\", \"spend_cents\", \"burst\"").optional() }).catchall(z.union([z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period."), z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period."), z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC)."), z.string().describe("Subscription this quota applies to.").default(""), z.never()])).superRefine((value, ctx) => { for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { +let evaluated = ["quotaLimit", "quotaRemaining", "quotaUsed", "resetsAt", "subscriptionId", "unit"].includes(key) +if (key.match(new RegExp("^(quota_limit)$"))) { evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) +const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Total allowed in the current period.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18728,26 +17379,9 @@ ctx.addIssue({ }) } } -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Batch mode: aggregate cost across all items."), z.string().describe("Single-offer result.\n For batch mode, these may be empty — check `items` instead."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["agentIdentityHash", "billingId", "cost", "deliveryMethod", "expiresAt", "ext", "extCritical", "items", "reportingObligation", "resourceTitle", "retrievalEndpoint", "subscriptionId", "subscriptionQuota", "subscriptionUnitValue", "totalCost", "transactionId", "ver"].includes(key) -if (key.match(new RegExp("^(agent_identity_hash)$"))) { +if (key.match(new RegExp("^(quota_remaining)$"))) { evaluated = true -const result = z.string().describe("Identity that retrieval_endpoint is bound to: the RFC 7638 JWK Thumbprint of\n the agent's Ed25519 request-signing key (see \"Retrieval-URL identity binding\"\n above). Empty string when absent; non-empty iff a signed retrieval_endpoint\n is present. Delivery-endpoint enforcement of the binding is OPTIONAL.").default("").safeParse(value[key]) +const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Remaining in the current period.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18759,9 +17393,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(billing_id)$"))) { +if (key.match(new RegExp("^(quota_used)$"))) { evaluated = true -const result = z.string().describe("Billing reference").safeParse(value[key]) +const result = z.coerce.number().int().gte(-2147483648).lte(2147483647).describe("Used so far in the current period.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18773,9 +17407,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(delivery_method)$"))) { +if (key.match(new RegExp("^(resets_at)$"))) { evaluated = true -const result = z.union([z.string().regex(new RegExp("^DELIVERY_METHOD_UNSPECIFIED$")), z.enum(["DELIVERY_METHOD_DIRECT","DELIVERY_METHOD_INSTRUCTIONS","DELIVERY_METHOD_STREAMING"]), z.coerce.number().int().gte(-2147483648).lte(2147483647)]).describe("How resource is delivered in this transaction.").default(0).safeParse(value[key]) +const result = z.string().datetime({ offset: true }).describe("When the quota counter resets (UTC).").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18787,9 +17421,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(expires_at)$"))) { +if (key.match(new RegExp("^(subscription_id)$"))) { evaluated = true -const result = z.string().datetime({ offset: true }).describe("When retrieval_endpoint expires.").safeParse(value[key]) +const result = z.string().describe("Subscription this quota applies to.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18801,42 +17435,26 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) +if (!evaluated) { +const result = z.never().safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, + message: `Invalid input: must match catchall schema`, params: { issues: result.error.issues } }) } } -if (key.match(new RegExp("^(reporting_obligation)$"))) { -evaluated = true -const result = z.object({ "endpoint": z.string().describe("URL to submit the usage report to (if different from Exchange).").optional(), "ext": z.record(z.string(), z.any()).describe("Extension point").optional(), "extCritical": z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").optional(), "required": z.boolean().describe("Whether post-usage reporting is required.").default(false), "requiredFields": z.array(z.string()).describe("Field names that must be present in the report.").optional(), "window": z.string().describe("Duration within which the report must be submitted (e.g. 24h).").optional() }).catchall(z.union([z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore."), z.array(z.string()).describe("Field names that must be present in the report."), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["endpoint", "ext", "extCritical", "required", "requiredFields", "window"].includes(key) -if (key.match(new RegExp("^(ext_critical)$"))) { -evaluated = true -const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} } -if (key.match(new RegExp("^(required_fields)$"))) { +}).describe("Analogous to RateLimitInfo (which signals API request rate limits), this\n signals subscription consumption quotas. Enables agents to throttle\n proactively instead of discovering exhaustion via denial.\n\n Returned on Offer (per-offer quota visibility) and TransactionResponse\n (post-transaction remaining quota). A subscription may have multiple\n independent quotas (access count + spend cap + burst limit), so this\n message is used as a repeated field.\n\n Quota decrement timing: the counter increments at ExecuteTransaction\n (optimistic decrement, before delivery). If delivery fails, the agent\n files a DisputeTransaction which may reverse the decrement. This is\n consistent with the billing model (billing_id created at transaction time).")).describe("Post-transaction quota state. Tells the agent how much quota remains\n after this transaction. Enables proactive throttling (\"1 access left\").\n Multiple entries for multi-dimensional quotas."), z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["amount", "currency", "unitCost"].includes(key) +if (key.match(new RegExp("^(unit_cost)$"))) { evaluated = true -const result = z.array(z.string()).describe("Field names that must be present in the report.").safeParse(value[key]) +const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18862,35 +17480,12 @@ ctx.addIssue({ } } } -}).describe("Reporting requirements attached to this delivery.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(resource_title)$"))) { -evaluated = true -const result = z.string().describe("Resource title echoed from the Offer (for logging/display).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(retrieval_endpoint)$"))) { +}).describe("Aggregate cost across all items."), z.never()])).superRefine((value, ctx) => { +for (const key in value) { +let evaluated = ["agentIdentityHash", "ext", "extCritical", "items", "subscriptionQuota", "totalCost", "ver"].includes(key) +if (key.match(new RegExp("^(agent_identity_hash)$"))) { evaluated = true -const result = z.string().describe("Signed retrieval URL the agent uses to fetch the purchased resource.\n Bound to agent_identity_hash; expires at expires_at. Absent on denial\n and on transactions whose delivery_method is not signed-URL-based.").safeParse(value[key]) +const result = z.string().describe("Identity that a delivered retrieval_endpoint is bound to: the RFC 7638 JWK\n Thumbprint of the agent's Ed25519 request-signing key (see \"Retrieval-URL\n identity binding\" above). Shared across the request; set once.").default("").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -18902,9 +17497,9 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(subscription_id)$"))) { +if (key.match(new RegExp("^(ext_critical)$"))) { evaluated = true -const result = z.string().describe("If set, this transaction was fulfilled under a subscription/deal.\n No per-request charge — usage tracked against subscription quota.").safeParse(value[key]) +const result = z.array(z.string()).describe("Critical extension keys (COSE crit pattern, RFC 9052).\n Lists keys within ext that the consumer MUST understand.\n Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION.\n Empty (default) → all ext keys are safe to ignore.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -19017,51 +17612,6 @@ ctx.addIssue({ }) } } -if (key.match(new RegExp("^(subscription_unit_value)$"))) { -evaluated = true -const result = z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { -for (const key in value) { -let evaluated = ["amount", "currency", "unitCost"].includes(key) -if (key.match(new RegExp("^(unit_cost)$"))) { -evaluated = true -const result = z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (!evaluated) { -const result = z.never().safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: must match catchall schema`, - params: { - issues: result.error.issues - } - }) -} -} -} -}).describe("Computed per-unit cost for financial attribution on subscription transactions.\n Even when cost.amount=0 (subscription), this field carries the value\n of the access for accounting purposes (e.g., ASC 606 prepaid drawdown).").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} if (key.match(new RegExp("^(total_cost)$"))) { evaluated = true const result = z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { @@ -19095,21 +17645,7 @@ ctx.addIssue({ } } } -}).describe("Batch mode: aggregate cost across all items.").safeParse(value[key]) -if (!result.success) { -ctx.addIssue({ - path: [key], - code: 'custom', - message: `Invalid input: Key matching regex /${key}/ must match schema`, - params: { - issues: result.error.issues - } - }) -} -} -if (key.match(new RegExp("^(transaction_id)$"))) { -evaluated = true -const result = z.string().describe("Single-offer result.\n For batch mode, these may be empty — check `items` instead.").safeParse(value[key]) +}).describe("Aggregate cost across all items.").safeParse(value[key]) if (!result.success) { ctx.addIssue({ path: [key], @@ -19135,7 +17671,7 @@ ctx.addIssue({ } } } -}).describe("For single-offer mode, the top-level fields are populated.\n For batch mode, `items` contains per-offer results and `total_cost`\n summarizes the aggregate cost."); +}).describe("Items-only (RAMP-102 / epic 6afpc): every per-result datum lives in `items`\n (one TransactionResultItem per committed offer, in original order); the\n top-level fields carry only the shared aggregate state. A single offer is the\n degenerate 1-element `items`. The per-item denials remain in-body on\n TransactionResultItem as partial results of a successful request."); export const TransactionResultItemSchema = z.object({ "billingId": z.string().describe("Billing reference.").default(""), "cost": z.object({ "amount": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).describe("Exact decimal string (not a float), e.g. \"19.99\". Denominated in `currency`.").default(""), "currency": z.string().default(""), "unitCost": z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")).optional() }).catchall(z.union([z.string().regex(new RegExp("^([0-9]+([.][0-9]+)?)?$")), z.never()])).superRefine((value, ctx) => { for (const key in value) { diff --git a/proto/ramp/v1/ramp.proto b/proto/ramp/v1/ramp.proto index 624df5f3..379794e5 100644 --- a/proto/ramp/v1/ramp.proto +++ b/proto/ramp/v1/ramp.proto @@ -3028,7 +3028,17 @@ enum CatalogRejectionReason { CATALOG_REJECTION_REASON_SIGNATURE_INVALID = 4; // request signature missing or invalid CATALOG_REJECTION_REASON_MALFORMED_ENTRY = 5; // a resource entry failed schema/validation CATALOG_REJECTION_REASON_UNKNOWN_VOCAB_TOKEN = 6; // an unregistered vocab token in a restriction/term - CATALOG_REJECTION_REASON_QUOTA_EXCEEDED = 7; // contributor push quota exceeded + CATALOG_REJECTION_REASON_QUOTA_EXCEEDED = 7; // contributor push quota exceeded (per-caller) + CATALOG_REJECTION_REASON_TERMS_LIMIT_EXCEEDED = 8; // a single entry carries more license terms than allowed (per-entry cap) + // The URI cannot be claimed by this caller's entries. Named from the caller's + // own perspective ON PURPOSE: it MUST NOT disclose that another resource/ + // contributor already owns the URI. Within one publisher, mutually-untrusting + // contributors share a catalog, so an "owned by another" reason would be a + // confirmed-existence oracle a contributor could use to map a competitor's + // catalog. The conflict is resolvable only by the publisher (who is authorized + // to see full ownership); the human-readable message routes the caller there + // without confirming who, if anyone, holds the URI. + CATALOG_REJECTION_REASON_URI_UNAVAILABLE = 9; } // CatalogRejection — a CatalogService call could not be applied.