@adcp/sdk 16.1.0 → 16.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/dist/lib/core/AgentClient.js +17 -1
- package/dist/lib/core/AgentClient.mjs +17 -1
- package/dist/lib/core/SingleAgentClient.js +13 -0
- package/dist/lib/core/SingleAgentClient.mjs +13 -0
- package/dist/lib/core/TaskExecutor.js +88 -13
- package/dist/lib/core/TaskExecutor.mjs +89 -14
- package/dist/lib/errors/index.d.mts +34 -0
- package/dist/lib/errors/index.d.ts +34 -0
- package/dist/lib/errors/index.js +11 -0
- package/dist/lib/errors/index.mjs +10 -0
- package/dist/lib/governance/authorization.d.mts +66 -0
- package/dist/lib/governance/authorization.d.ts +66 -0
- package/dist/lib/governance/authorization.js +181 -37
- package/dist/lib/governance/authorization.mjs +183 -36
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +4 -2
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/identity-3.3/index.d.mts +1 -0
- package/dist/lib/identity-3.3/index.d.ts +1 -0
- package/dist/lib/identity-3.3/index.js +3 -1
- package/dist/lib/identity-3.3/index.mjs +1 -0
- package/dist/lib/identity-3.3/jws-artifacts.d.mts +147 -0
- package/dist/lib/identity-3.3/jws-artifacts.d.ts +147 -0
- package/dist/lib/identity-3.3/jws-artifacts.js +258 -0
- package/dist/lib/identity-3.3/jws-artifacts.mjs +234 -0
- package/dist/lib/identity-3.3/revocation.d.mts +7 -0
- package/dist/lib/identity-3.3/revocation.d.ts +7 -0
- package/dist/lib/identity-3.3/revocation.js +13 -0
- package/dist/lib/identity-3.3/revocation.mjs +13 -0
- package/dist/lib/index.d.mts +3 -3
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +4 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/media-buy/compatibility.d.mts +1 -0
- package/dist/lib/media-buy/compatibility.d.ts +1 -0
- package/dist/lib/media-buy/compatibility.js +33 -10
- package/dist/lib/media-buy/compatibility.mjs +32 -14
- package/dist/lib/negotiation/buyer.js +11 -5
- package/dist/lib/negotiation/buyer.mjs +11 -5
- package/dist/lib/negotiation/types.d.mts +4 -2
- package/dist/lib/negotiation/types.d.ts +4 -2
- package/dist/lib/negotiation/verification.d.mts +15 -6
- package/dist/lib/negotiation/verification.d.ts +15 -6
- package/dist/lib/negotiation/verification.js +146 -27
- package/dist/lib/negotiation/verification.mjs +146 -28
- package/dist/lib/negotiation/version.d.mts +6 -0
- package/dist/lib/negotiation/version.d.ts +6 -0
- package/dist/lib/negotiation/version.js +60 -0
- package/dist/lib/negotiation/version.mjs +33 -0
- package/dist/lib/registry/types.generated.d.mts +24 -1
- package/dist/lib/registry/types.generated.d.ts +24 -1
- package/dist/lib/server/create-adcp-server.js +1 -1
- package/dist/lib/server/create-adcp-server.mjs +1 -1
- package/dist/lib/server/error-arm-tools.d.mts +10 -10
- package/dist/lib/server/error-arm-tools.d.ts +10 -10
- package/dist/lib/server/error-arm-tools.js +22 -58
- package/dist/lib/server/error-arm-tools.mjs +30 -65
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +2 -0
- package/dist/lib/signing/web-bot-auth.js +5 -0
- package/dist/lib/signing/web-bot-auth.mjs +4 -0
- package/dist/lib/testing/compliance/comply.js +17 -0
- package/dist/lib/testing/compliance/comply.mjs +17 -0
- package/dist/lib/testing/index.d.mts +3 -0
- package/dist/lib/testing/index.d.ts +3 -0
- package/dist/lib/testing/index.js +44 -2
- package/dist/lib/testing/index.mjs +42 -0
- package/dist/lib/testing/storyboard/collection-selection.d.mts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.d.ts +259 -0
- package/dist/lib/testing/storyboard/collection-selection.js +384 -0
- package/dist/lib/testing/storyboard/collection-selection.mjs +348 -0
- package/dist/lib/testing/storyboard/context.js +12 -6
- package/dist/lib/testing/storyboard/context.mjs +12 -6
- package/dist/lib/testing/storyboard/fixture-resolution.d.mts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.d.ts +39 -1
- package/dist/lib/testing/storyboard/fixture-resolution.js +216 -12
- package/dist/lib/testing/storyboard/fixture-resolution.mjs +218 -12
- package/dist/lib/testing/storyboard/index.d.mts +9 -2
- package/dist/lib/testing/storyboard/index.d.ts +9 -2
- package/dist/lib/testing/storyboard/index.js +51 -2
- package/dist/lib/testing/storyboard/index.mjs +49 -1
- package/dist/lib/testing/storyboard/prepared-fixtures.d.mts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.d.ts +471 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.js +434 -0
- package/dist/lib/testing/storyboard/prepared-fixtures.mjs +389 -0
- package/dist/lib/testing/storyboard/protected-context.d.mts +127 -0
- package/dist/lib/testing/storyboard/protected-context.d.ts +127 -0
- package/dist/lib/testing/storyboard/protected-context.js +301 -0
- package/dist/lib/testing/storyboard/protected-context.mjs +267 -0
- package/dist/lib/testing/storyboard/rejection-hints.js +13 -4
- package/dist/lib/testing/storyboard/rejection-hints.mjs +13 -4
- package/dist/lib/testing/storyboard/runner.d.mts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.js +1061 -105
- package/dist/lib/testing/storyboard/runner.mjs +1075 -106
- package/dist/lib/testing/storyboard/seeding.d.mts +15 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +15 -1
- package/dist/lib/testing/storyboard/seeding.js +272 -12
- package/dist/lib/testing/storyboard/seeding.mjs +274 -13
- package/dist/lib/testing/storyboard/types.d.mts +207 -10
- package/dist/lib/testing/storyboard/types.d.ts +207 -10
- package/dist/lib/testing/storyboard/types.js +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +95 -0
- package/dist/lib/testing/storyboard/validations.mjs +95 -0
- package/dist/lib/validation/bundled-schema-store.js +5 -0
- package/dist/lib/validation/bundled-schema-store.mjs +4 -0
- package/dist/lib/validation/schema-loader.js +19 -2
- package/dist/lib/validation/schema-loader.mjs +22 -3
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/COLLECTION-FIXTURE-FOUNDATION.md +850 -0
- package/docs/guides/PROPOSAL-TERMS-VERIFICATION.md +66 -0
- package/docs/llms.txt +4 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +51 -6
- package/package.json +6 -4
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-qualified collection identity: one normalizer, one eligibility
|
|
3
|
+
* predicate, one witness builder, one set comparison.
|
|
4
|
+
*
|
|
5
|
+
* Every consumer of collection identity in the runner goes through this
|
|
6
|
+
* module — the fixture predicate (`collection_selection_satisfies`), the
|
|
7
|
+
* derived witness captures published by fixture preparation, and the
|
|
8
|
+
* `collection_set_equals_context` validator. Splitting those three would let
|
|
9
|
+
* "what counts as the same collection" drift between selection and grading,
|
|
10
|
+
* which is exactly the failure the upstream contract calls out.
|
|
11
|
+
*
|
|
12
|
+
* Identity rules, and the reasons they are what they are:
|
|
13
|
+
*
|
|
14
|
+
* - A selector is normalized to atomic `(publisher_domain, collection_id)`
|
|
15
|
+
* pairs. IDs are never flattened without their domain: two publishers may
|
|
16
|
+
* legitimately declare the same `collection_id`.
|
|
17
|
+
* - `collection_ids` is optional on `CollectionSelector` and means "every
|
|
18
|
+
* collection declared in that domain's adagents.json". That bulk-grant
|
|
19
|
+
* form is a *different* selector from an explicit enumeration, so it
|
|
20
|
+
* normalizes to a pair with `collection_id: null` rather than silently
|
|
21
|
+
* comparing equal to an enumerated set. Product composition is required
|
|
22
|
+
* to name explicit ids, so `null` pairs do not count toward
|
|
23
|
+
* `minimum_pairs`.
|
|
24
|
+
* - Schema-valid redundancy collapses: repeated ids inside one selector,
|
|
25
|
+
* and overlapping atomic pairs across differently-grouped selectors, are
|
|
26
|
+
* one pair. Neither `product.collections[]` nor `collection_ids[]`
|
|
27
|
+
* carries a uniqueness constraint, so redundancy is legal *product
|
|
28
|
+
* composition*. The readback arm is stricter:
|
|
29
|
+
* `core/collection-selection.json` declares `uniqueItems: true` on
|
|
30
|
+
* `collections` in `mode: selected`. Normalizing here is for COMPARISON
|
|
31
|
+
* only and never excuses that — the surrounding response is still
|
|
32
|
+
* schema-validated, which is what rejects a duplicated outer entry.
|
|
33
|
+
* Schema-invalid input (a non-array, a non-object entry, an empty
|
|
34
|
+
* `collection_ids`) stays a visible failure here and is additionally
|
|
35
|
+
* rejected by ordinary response-schema validation upstream.
|
|
36
|
+
* - Ordering is bytewise over UTF-8, on the *pairs*, so outer grouping and
|
|
37
|
+
* inner id order never affect the result while non-ASCII identifiers stay
|
|
38
|
+
* lossless. Nothing is case-folded, trimmed, punycoded or otherwise
|
|
39
|
+
* rewritten. `collection_id` is a plain string with no `minLength` and no
|
|
40
|
+
* declared normalization form, so inventing one would grade agents
|
|
41
|
+
* against a rule the protocol does not state. `publisher_domain` is
|
|
42
|
+
* separately pattern-constrained to lowercase LDH by
|
|
43
|
+
* `core/collection-selector.json`, which response-schema validation
|
|
44
|
+
* already enforces — canonicalizing it here would be redundant at best
|
|
45
|
+
* and would mask a non-conformant catalog at worst.
|
|
46
|
+
*/
|
|
47
|
+
/** Maximum distinct pairs any single normalization will materialize. */
|
|
48
|
+
export declare const COLLECTION_PAIR_LIMIT = 10000;
|
|
49
|
+
/** Maximum pairs echoed into a diagnostic before it is elided. */
|
|
50
|
+
export declare const COLLECTION_DIAGNOSTIC_LIMIT = 20;
|
|
51
|
+
/** One atomic, domain-qualified collection identity. */
|
|
52
|
+
export interface CollectionPair {
|
|
53
|
+
publisher_domain: string;
|
|
54
|
+
/** `null` is the domain-only bulk-grant selector, not an empty id. */
|
|
55
|
+
collection_id: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** A concrete `CollectionSelector` as it appears on the wire. */
|
|
58
|
+
export interface CollectionSelectorObject {
|
|
59
|
+
publisher_domain: string;
|
|
60
|
+
collection_ids?: string[];
|
|
61
|
+
}
|
|
62
|
+
export interface NormalizedCollectionSelectors {
|
|
63
|
+
/** Distinct pairs in bytewise order. */
|
|
64
|
+
pairs: CollectionPair[];
|
|
65
|
+
/** Outer selector entries observed, before normalization. */
|
|
66
|
+
selector_count: number;
|
|
67
|
+
/** Distinct publisher domains, bytewise ordered. */
|
|
68
|
+
domains: string[];
|
|
69
|
+
}
|
|
70
|
+
export type CollectionNormalizationCode = 'not_an_array' | 'selector_not_object' | 'publisher_domain_invalid' | 'collection_ids_invalid' | 'collection_id_invalid' | 'pair_limit_exceeded';
|
|
71
|
+
export interface CollectionNormalizationFailure {
|
|
72
|
+
code: CollectionNormalizationCode;
|
|
73
|
+
detail: string;
|
|
74
|
+
}
|
|
75
|
+
export type CollectionNormalizationResult = {
|
|
76
|
+
ok: true;
|
|
77
|
+
value: NormalizedCollectionSelectors;
|
|
78
|
+
} | {
|
|
79
|
+
ok: false;
|
|
80
|
+
failure: CollectionNormalizationFailure;
|
|
81
|
+
};
|
|
82
|
+
/** Total bytewise order over pairs. Domain first, then id; `null` sorts first. */
|
|
83
|
+
export declare function compareCollectionPairs(a: CollectionPair, b: CollectionPair): number;
|
|
84
|
+
/** Stable, unambiguous rendering for diagnostics and dedup keys. */
|
|
85
|
+
export declare function formatCollectionPair(pair: CollectionPair): string;
|
|
86
|
+
/**
|
|
87
|
+
* Normalize an array of `CollectionSelector` objects into distinct atomic
|
|
88
|
+
* pairs. Used for product catalog entries, authored expected sets and
|
|
89
|
+
* seller-returned `selected.collections` alike — same definition everywhere.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeCollectionSelectors(value: unknown, options?: {
|
|
92
|
+
limit?: number;
|
|
93
|
+
}): CollectionNormalizationResult;
|
|
94
|
+
/** Exact set difference over normalized pairs, with bounded diagnostics. */
|
|
95
|
+
export declare function diffCollectionPairSets(actual: readonly CollectionPair[], expected: readonly CollectionPair[]): {
|
|
96
|
+
equal: boolean;
|
|
97
|
+
missing: CollectionPair[];
|
|
98
|
+
unexpected: CollectionPair[];
|
|
99
|
+
};
|
|
100
|
+
/** Render at most `COLLECTION_DIAGNOSTIC_LIMIT` pairs, flagging elision. */
|
|
101
|
+
export declare function summarizeCollectionPairs(pairs: readonly CollectionPair[]): string;
|
|
102
|
+
/**
|
|
103
|
+
* Operand of the `collection_selection_satisfies` fixture predicate.
|
|
104
|
+
*
|
|
105
|
+
* `collection_targeting` encodes the schema's omitted-false default
|
|
106
|
+
* explicitly rather than teaching every `equals` comparison to apply
|
|
107
|
+
* arbitrary defaults. It is deliberately NOT named `mode`: the wire's
|
|
108
|
+
* `collection_selection.mode` is `selected | default`, and the two appear in
|
|
109
|
+
* the same storyboard file.
|
|
110
|
+
*
|
|
111
|
+
* - `fixed` — `collection_targeting_allowed` is literal `false` OR
|
|
112
|
+
* absent (the normative default). `true` does not qualify.
|
|
113
|
+
* - `selectable` — literal `true` only.
|
|
114
|
+
* - `any` — either, as long as the flag is absent or a boolean.
|
|
115
|
+
*
|
|
116
|
+
* A non-boolean, non-absent flag never qualifies in any mode: that is a
|
|
117
|
+
* schema failure of the seller's catalog, surfaced by response-schema
|
|
118
|
+
* validation during discovery, not something this predicate repairs.
|
|
119
|
+
*/
|
|
120
|
+
export interface CollectionSelectionSelector {
|
|
121
|
+
collection_targeting: 'selectable' | 'fixed' | 'any';
|
|
122
|
+
/**
|
|
123
|
+
* Distinct concrete pairs the product must expose. Defaults to `1`, which
|
|
124
|
+
* covers default materialization / full-bundle restatement; `2` is
|
|
125
|
+
* required before a genuine strict subset exists to exercise.
|
|
126
|
+
*/
|
|
127
|
+
minimum_pairs?: 1 | 2;
|
|
128
|
+
/**
|
|
129
|
+
* Require a wire-visible owner/host relation: at least one collection
|
|
130
|
+
* selector whose publisher domain is outside every declared property
|
|
131
|
+
* (host) domain of the same product. Defaults to `false`.
|
|
132
|
+
*/
|
|
133
|
+
require_cross_publisher_collection?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Post-normalization operand the evaluator consumes, with both defaults
|
|
137
|
+
* applied.
|
|
138
|
+
*
|
|
139
|
+
* Kept separate from the authored form so the type accepts exactly what the
|
|
140
|
+
* loader accepts: `{ collection_targeting: 'fixed' }` is valid YAML and must type-check,
|
|
141
|
+
* while an index signature would have let a typo'd key compile clean and
|
|
142
|
+
* then fail at load against the closed-operand rule.
|
|
143
|
+
*/
|
|
144
|
+
export interface ResolvedCollectionSelectionSelector {
|
|
145
|
+
collection_targeting: 'selectable' | 'fixed' | 'any';
|
|
146
|
+
minimum_pairs: 1 | 2;
|
|
147
|
+
require_cross_publisher_collection: boolean;
|
|
148
|
+
}
|
|
149
|
+
export declare const COLLECTION_TARGETING_MODES: readonly ["selectable", "fixed", "any"];
|
|
150
|
+
/** Validate and default the closed operand. Throws on authoring errors. */
|
|
151
|
+
export declare function normalizeCollectionSelectionSelector(value: unknown, location: string): ResolvedCollectionSelectionSelector;
|
|
152
|
+
/** The declared owner/host relation observed on one product. */
|
|
153
|
+
export interface CrossPublisherCollectionRelation {
|
|
154
|
+
/**
|
|
155
|
+
* The collection namespace that lies outside every property namespace of
|
|
156
|
+
* the SAME selected product.
|
|
157
|
+
*/
|
|
158
|
+
collection_publisher_domain: string;
|
|
159
|
+
/** Property namespaces declared by that same product. */
|
|
160
|
+
property_publisher_domains: string[];
|
|
161
|
+
/**
|
|
162
|
+
* The concrete selector carrying it. `selector.publisher_domain` always
|
|
163
|
+
* equals `collection_publisher_domain`.
|
|
164
|
+
*/
|
|
165
|
+
selector: CollectionSelectorObject;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Everything the resolver retains about the product it actually selected.
|
|
169
|
+
* Built once, from the selected candidate, and never recomputed from a
|
|
170
|
+
* different response.
|
|
171
|
+
*/
|
|
172
|
+
export interface CollectionSelectionWitness {
|
|
173
|
+
product_id: string | null;
|
|
174
|
+
/**
|
|
175
|
+
* Effective targeting of the selected product, with the schema's
|
|
176
|
+
* omitted-false default applied. Distinct from the native seller field
|
|
177
|
+
* `collection_targeting_allowed`, whose raw observed value is reported
|
|
178
|
+
* beside it, and from the wire's `collection_selection.mode`.
|
|
179
|
+
*/
|
|
180
|
+
collection_targeting: 'selectable' | 'fixed';
|
|
181
|
+
/** The flag exactly as observed — `undefined` when the seller omitted it. */
|
|
182
|
+
collection_targeting_allowed: boolean | undefined;
|
|
183
|
+
/** The product's complete, concrete collection selector bundle. */
|
|
184
|
+
declared_selectors: CollectionSelectorObject[];
|
|
185
|
+
/** Distinct concrete pairs (bulk-grant entries excluded). */
|
|
186
|
+
distinct_pairs: CollectionPair[];
|
|
187
|
+
/**
|
|
188
|
+
* A one-entry selector array naming exactly one of the bundle's pairs, so
|
|
189
|
+
* it is a strict subset whenever the bundle has two or more. Shaped as an
|
|
190
|
+
* array because that is the shape a `collections` field takes on the wire,
|
|
191
|
+
* so a storyboard can substitute it whole.
|
|
192
|
+
*
|
|
193
|
+
* When `require_cross_publisher_collection` was requested, the pair is drawn from the
|
|
194
|
+
* retained external owner, so the subset actually exercises the carriage
|
|
195
|
+
* relation rather than the host's own inventory.
|
|
196
|
+
*
|
|
197
|
+
* `null` when the product exposes fewer than two distinct concrete pairs —
|
|
198
|
+
* a one-pair product still qualifies for default materialization, so the
|
|
199
|
+
* absence of a subset witness must not suppress that coverage.
|
|
200
|
+
*/
|
|
201
|
+
strict_subset: CollectionSelectorObject[] | null;
|
|
202
|
+
/**
|
|
203
|
+
* One-entry selector array naming an id verified absent from this
|
|
204
|
+
* product's pairs, under a domain the product does carry.
|
|
205
|
+
*/
|
|
206
|
+
undeclared_collection_id_selectors: CollectionSelectorObject[];
|
|
207
|
+
/**
|
|
208
|
+
* One-entry selector array whose domain is verified absent from every
|
|
209
|
+
* COLLECTION namespace this product declares.
|
|
210
|
+
*
|
|
211
|
+
* Property namespaces (`publisher_properties[].publisher_domain`) are not
|
|
212
|
+
* checked — the synthesized domain is a `.invalid` name, which RFC 2606
|
|
213
|
+
* reserves and no real publisher can hold, so a collision is not possible
|
|
214
|
+
* in practice. Stated precisely because "every namespace this product
|
|
215
|
+
* declares" read as including the property side, and it does not.
|
|
216
|
+
*/
|
|
217
|
+
undeclared_publisher_domain_selectors: CollectionSelectorObject[];
|
|
218
|
+
/** Present only when the relation was requested and found. */
|
|
219
|
+
cross_publisher_relation: CrossPublisherCollectionRelation | null;
|
|
220
|
+
/** Declared property namespaces, bytewise ordered. */
|
|
221
|
+
property_publisher_domains: string[];
|
|
222
|
+
}
|
|
223
|
+
export type CollectionSelectionEvaluation = {
|
|
224
|
+
eligible: true;
|
|
225
|
+
witness: CollectionSelectionWitness;
|
|
226
|
+
} | {
|
|
227
|
+
eligible: false;
|
|
228
|
+
reason: string;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Decide whether one discovered product qualifies, and retain the witness
|
|
232
|
+
* for the candidate that is actually selected.
|
|
233
|
+
*
|
|
234
|
+
* Qualification is evaluated against the complete validated candidate before
|
|
235
|
+
* any deterministic ordering is applied, so a nonqualifying first product can
|
|
236
|
+
* never hide a later qualifying one.
|
|
237
|
+
*/
|
|
238
|
+
export declare function evaluateCollectionSelection(candidate: unknown, selector: CollectionSelectionSelector): CollectionSelectionEvaluation;
|
|
239
|
+
/** Boolean form for the legacy matcher. */
|
|
240
|
+
export declare function productSatisfiesCollectionSelection(candidate: unknown, selector: CollectionSelectionSelector): boolean;
|
|
241
|
+
/** Derived witness captures a declaration may bind to a context key. */
|
|
242
|
+
export declare const COLLECTION_WITNESS_KINDS: readonly ["declared_selectors", "distinct_pairs", "strict_subset", "undeclared_collection_id_selectors", "undeclared_publisher_domain_selectors", "cross_publisher_relation", "property_publisher_domains"];
|
|
243
|
+
export type CollectionWitnessKind = (typeof COLLECTION_WITNESS_KINDS)[number];
|
|
244
|
+
export type CollectionWitnessReadResult = {
|
|
245
|
+
ok: true;
|
|
246
|
+
value: unknown;
|
|
247
|
+
} | {
|
|
248
|
+
ok: false;
|
|
249
|
+
detail: string;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Read one derived capture off a retained witness.
|
|
253
|
+
*
|
|
254
|
+
* A declared capture that cannot be built is a setup failure, never an
|
|
255
|
+
* unsupported/not-applicable downgrade — the storyboard asked for state the
|
|
256
|
+
* selected product does not provide, and silently proceeding would grade the
|
|
257
|
+
* agent against a request the author did not write.
|
|
258
|
+
*/
|
|
259
|
+
export declare function readCollectionWitness(witness: CollectionSelectionWitness | undefined, kind: CollectionWitnessKind): CollectionWitnessReadResult;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-qualified collection identity: one normalizer, one eligibility
|
|
3
|
+
* predicate, one witness builder, one set comparison.
|
|
4
|
+
*
|
|
5
|
+
* Every consumer of collection identity in the runner goes through this
|
|
6
|
+
* module — the fixture predicate (`collection_selection_satisfies`), the
|
|
7
|
+
* derived witness captures published by fixture preparation, and the
|
|
8
|
+
* `collection_set_equals_context` validator. Splitting those three would let
|
|
9
|
+
* "what counts as the same collection" drift between selection and grading,
|
|
10
|
+
* which is exactly the failure the upstream contract calls out.
|
|
11
|
+
*
|
|
12
|
+
* Identity rules, and the reasons they are what they are:
|
|
13
|
+
*
|
|
14
|
+
* - A selector is normalized to atomic `(publisher_domain, collection_id)`
|
|
15
|
+
* pairs. IDs are never flattened without their domain: two publishers may
|
|
16
|
+
* legitimately declare the same `collection_id`.
|
|
17
|
+
* - `collection_ids` is optional on `CollectionSelector` and means "every
|
|
18
|
+
* collection declared in that domain's adagents.json". That bulk-grant
|
|
19
|
+
* form is a *different* selector from an explicit enumeration, so it
|
|
20
|
+
* normalizes to a pair with `collection_id: null` rather than silently
|
|
21
|
+
* comparing equal to an enumerated set. Product composition is required
|
|
22
|
+
* to name explicit ids, so `null` pairs do not count toward
|
|
23
|
+
* `minimum_pairs`.
|
|
24
|
+
* - Schema-valid redundancy collapses: repeated ids inside one selector,
|
|
25
|
+
* and overlapping atomic pairs across differently-grouped selectors, are
|
|
26
|
+
* one pair. Neither `product.collections[]` nor `collection_ids[]`
|
|
27
|
+
* carries a uniqueness constraint, so redundancy is legal *product
|
|
28
|
+
* composition*. The readback arm is stricter:
|
|
29
|
+
* `core/collection-selection.json` declares `uniqueItems: true` on
|
|
30
|
+
* `collections` in `mode: selected`. Normalizing here is for COMPARISON
|
|
31
|
+
* only and never excuses that — the surrounding response is still
|
|
32
|
+
* schema-validated, which is what rejects a duplicated outer entry.
|
|
33
|
+
* Schema-invalid input (a non-array, a non-object entry, an empty
|
|
34
|
+
* `collection_ids`) stays a visible failure here and is additionally
|
|
35
|
+
* rejected by ordinary response-schema validation upstream.
|
|
36
|
+
* - Ordering is bytewise over UTF-8, on the *pairs*, so outer grouping and
|
|
37
|
+
* inner id order never affect the result while non-ASCII identifiers stay
|
|
38
|
+
* lossless. Nothing is case-folded, trimmed, punycoded or otherwise
|
|
39
|
+
* rewritten. `collection_id` is a plain string with no `minLength` and no
|
|
40
|
+
* declared normalization form, so inventing one would grade agents
|
|
41
|
+
* against a rule the protocol does not state. `publisher_domain` is
|
|
42
|
+
* separately pattern-constrained to lowercase LDH by
|
|
43
|
+
* `core/collection-selector.json`, which response-schema validation
|
|
44
|
+
* already enforces — canonicalizing it here would be redundant at best
|
|
45
|
+
* and would mask a non-conformant catalog at worst.
|
|
46
|
+
*/
|
|
47
|
+
/** Maximum distinct pairs any single normalization will materialize. */
|
|
48
|
+
export declare const COLLECTION_PAIR_LIMIT = 10000;
|
|
49
|
+
/** Maximum pairs echoed into a diagnostic before it is elided. */
|
|
50
|
+
export declare const COLLECTION_DIAGNOSTIC_LIMIT = 20;
|
|
51
|
+
/** One atomic, domain-qualified collection identity. */
|
|
52
|
+
export interface CollectionPair {
|
|
53
|
+
publisher_domain: string;
|
|
54
|
+
/** `null` is the domain-only bulk-grant selector, not an empty id. */
|
|
55
|
+
collection_id: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** A concrete `CollectionSelector` as it appears on the wire. */
|
|
58
|
+
export interface CollectionSelectorObject {
|
|
59
|
+
publisher_domain: string;
|
|
60
|
+
collection_ids?: string[];
|
|
61
|
+
}
|
|
62
|
+
export interface NormalizedCollectionSelectors {
|
|
63
|
+
/** Distinct pairs in bytewise order. */
|
|
64
|
+
pairs: CollectionPair[];
|
|
65
|
+
/** Outer selector entries observed, before normalization. */
|
|
66
|
+
selector_count: number;
|
|
67
|
+
/** Distinct publisher domains, bytewise ordered. */
|
|
68
|
+
domains: string[];
|
|
69
|
+
}
|
|
70
|
+
export type CollectionNormalizationCode = 'not_an_array' | 'selector_not_object' | 'publisher_domain_invalid' | 'collection_ids_invalid' | 'collection_id_invalid' | 'pair_limit_exceeded';
|
|
71
|
+
export interface CollectionNormalizationFailure {
|
|
72
|
+
code: CollectionNormalizationCode;
|
|
73
|
+
detail: string;
|
|
74
|
+
}
|
|
75
|
+
export type CollectionNormalizationResult = {
|
|
76
|
+
ok: true;
|
|
77
|
+
value: NormalizedCollectionSelectors;
|
|
78
|
+
} | {
|
|
79
|
+
ok: false;
|
|
80
|
+
failure: CollectionNormalizationFailure;
|
|
81
|
+
};
|
|
82
|
+
/** Total bytewise order over pairs. Domain first, then id; `null` sorts first. */
|
|
83
|
+
export declare function compareCollectionPairs(a: CollectionPair, b: CollectionPair): number;
|
|
84
|
+
/** Stable, unambiguous rendering for diagnostics and dedup keys. */
|
|
85
|
+
export declare function formatCollectionPair(pair: CollectionPair): string;
|
|
86
|
+
/**
|
|
87
|
+
* Normalize an array of `CollectionSelector` objects into distinct atomic
|
|
88
|
+
* pairs. Used for product catalog entries, authored expected sets and
|
|
89
|
+
* seller-returned `selected.collections` alike — same definition everywhere.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeCollectionSelectors(value: unknown, options?: {
|
|
92
|
+
limit?: number;
|
|
93
|
+
}): CollectionNormalizationResult;
|
|
94
|
+
/** Exact set difference over normalized pairs, with bounded diagnostics. */
|
|
95
|
+
export declare function diffCollectionPairSets(actual: readonly CollectionPair[], expected: readonly CollectionPair[]): {
|
|
96
|
+
equal: boolean;
|
|
97
|
+
missing: CollectionPair[];
|
|
98
|
+
unexpected: CollectionPair[];
|
|
99
|
+
};
|
|
100
|
+
/** Render at most `COLLECTION_DIAGNOSTIC_LIMIT` pairs, flagging elision. */
|
|
101
|
+
export declare function summarizeCollectionPairs(pairs: readonly CollectionPair[]): string;
|
|
102
|
+
/**
|
|
103
|
+
* Operand of the `collection_selection_satisfies` fixture predicate.
|
|
104
|
+
*
|
|
105
|
+
* `collection_targeting` encodes the schema's omitted-false default
|
|
106
|
+
* explicitly rather than teaching every `equals` comparison to apply
|
|
107
|
+
* arbitrary defaults. It is deliberately NOT named `mode`: the wire's
|
|
108
|
+
* `collection_selection.mode` is `selected | default`, and the two appear in
|
|
109
|
+
* the same storyboard file.
|
|
110
|
+
*
|
|
111
|
+
* - `fixed` — `collection_targeting_allowed` is literal `false` OR
|
|
112
|
+
* absent (the normative default). `true` does not qualify.
|
|
113
|
+
* - `selectable` — literal `true` only.
|
|
114
|
+
* - `any` — either, as long as the flag is absent or a boolean.
|
|
115
|
+
*
|
|
116
|
+
* A non-boolean, non-absent flag never qualifies in any mode: that is a
|
|
117
|
+
* schema failure of the seller's catalog, surfaced by response-schema
|
|
118
|
+
* validation during discovery, not something this predicate repairs.
|
|
119
|
+
*/
|
|
120
|
+
export interface CollectionSelectionSelector {
|
|
121
|
+
collection_targeting: 'selectable' | 'fixed' | 'any';
|
|
122
|
+
/**
|
|
123
|
+
* Distinct concrete pairs the product must expose. Defaults to `1`, which
|
|
124
|
+
* covers default materialization / full-bundle restatement; `2` is
|
|
125
|
+
* required before a genuine strict subset exists to exercise.
|
|
126
|
+
*/
|
|
127
|
+
minimum_pairs?: 1 | 2;
|
|
128
|
+
/**
|
|
129
|
+
* Require a wire-visible owner/host relation: at least one collection
|
|
130
|
+
* selector whose publisher domain is outside every declared property
|
|
131
|
+
* (host) domain of the same product. Defaults to `false`.
|
|
132
|
+
*/
|
|
133
|
+
require_cross_publisher_collection?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Post-normalization operand the evaluator consumes, with both defaults
|
|
137
|
+
* applied.
|
|
138
|
+
*
|
|
139
|
+
* Kept separate from the authored form so the type accepts exactly what the
|
|
140
|
+
* loader accepts: `{ collection_targeting: 'fixed' }` is valid YAML and must type-check,
|
|
141
|
+
* while an index signature would have let a typo'd key compile clean and
|
|
142
|
+
* then fail at load against the closed-operand rule.
|
|
143
|
+
*/
|
|
144
|
+
export interface ResolvedCollectionSelectionSelector {
|
|
145
|
+
collection_targeting: 'selectable' | 'fixed' | 'any';
|
|
146
|
+
minimum_pairs: 1 | 2;
|
|
147
|
+
require_cross_publisher_collection: boolean;
|
|
148
|
+
}
|
|
149
|
+
export declare const COLLECTION_TARGETING_MODES: readonly ["selectable", "fixed", "any"];
|
|
150
|
+
/** Validate and default the closed operand. Throws on authoring errors. */
|
|
151
|
+
export declare function normalizeCollectionSelectionSelector(value: unknown, location: string): ResolvedCollectionSelectionSelector;
|
|
152
|
+
/** The declared owner/host relation observed on one product. */
|
|
153
|
+
export interface CrossPublisherCollectionRelation {
|
|
154
|
+
/**
|
|
155
|
+
* The collection namespace that lies outside every property namespace of
|
|
156
|
+
* the SAME selected product.
|
|
157
|
+
*/
|
|
158
|
+
collection_publisher_domain: string;
|
|
159
|
+
/** Property namespaces declared by that same product. */
|
|
160
|
+
property_publisher_domains: string[];
|
|
161
|
+
/**
|
|
162
|
+
* The concrete selector carrying it. `selector.publisher_domain` always
|
|
163
|
+
* equals `collection_publisher_domain`.
|
|
164
|
+
*/
|
|
165
|
+
selector: CollectionSelectorObject;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Everything the resolver retains about the product it actually selected.
|
|
169
|
+
* Built once, from the selected candidate, and never recomputed from a
|
|
170
|
+
* different response.
|
|
171
|
+
*/
|
|
172
|
+
export interface CollectionSelectionWitness {
|
|
173
|
+
product_id: string | null;
|
|
174
|
+
/**
|
|
175
|
+
* Effective targeting of the selected product, with the schema's
|
|
176
|
+
* omitted-false default applied. Distinct from the native seller field
|
|
177
|
+
* `collection_targeting_allowed`, whose raw observed value is reported
|
|
178
|
+
* beside it, and from the wire's `collection_selection.mode`.
|
|
179
|
+
*/
|
|
180
|
+
collection_targeting: 'selectable' | 'fixed';
|
|
181
|
+
/** The flag exactly as observed — `undefined` when the seller omitted it. */
|
|
182
|
+
collection_targeting_allowed: boolean | undefined;
|
|
183
|
+
/** The product's complete, concrete collection selector bundle. */
|
|
184
|
+
declared_selectors: CollectionSelectorObject[];
|
|
185
|
+
/** Distinct concrete pairs (bulk-grant entries excluded). */
|
|
186
|
+
distinct_pairs: CollectionPair[];
|
|
187
|
+
/**
|
|
188
|
+
* A one-entry selector array naming exactly one of the bundle's pairs, so
|
|
189
|
+
* it is a strict subset whenever the bundle has two or more. Shaped as an
|
|
190
|
+
* array because that is the shape a `collections` field takes on the wire,
|
|
191
|
+
* so a storyboard can substitute it whole.
|
|
192
|
+
*
|
|
193
|
+
* When `require_cross_publisher_collection` was requested, the pair is drawn from the
|
|
194
|
+
* retained external owner, so the subset actually exercises the carriage
|
|
195
|
+
* relation rather than the host's own inventory.
|
|
196
|
+
*
|
|
197
|
+
* `null` when the product exposes fewer than two distinct concrete pairs —
|
|
198
|
+
* a one-pair product still qualifies for default materialization, so the
|
|
199
|
+
* absence of a subset witness must not suppress that coverage.
|
|
200
|
+
*/
|
|
201
|
+
strict_subset: CollectionSelectorObject[] | null;
|
|
202
|
+
/**
|
|
203
|
+
* One-entry selector array naming an id verified absent from this
|
|
204
|
+
* product's pairs, under a domain the product does carry.
|
|
205
|
+
*/
|
|
206
|
+
undeclared_collection_id_selectors: CollectionSelectorObject[];
|
|
207
|
+
/**
|
|
208
|
+
* One-entry selector array whose domain is verified absent from every
|
|
209
|
+
* COLLECTION namespace this product declares.
|
|
210
|
+
*
|
|
211
|
+
* Property namespaces (`publisher_properties[].publisher_domain`) are not
|
|
212
|
+
* checked — the synthesized domain is a `.invalid` name, which RFC 2606
|
|
213
|
+
* reserves and no real publisher can hold, so a collision is not possible
|
|
214
|
+
* in practice. Stated precisely because "every namespace this product
|
|
215
|
+
* declares" read as including the property side, and it does not.
|
|
216
|
+
*/
|
|
217
|
+
undeclared_publisher_domain_selectors: CollectionSelectorObject[];
|
|
218
|
+
/** Present only when the relation was requested and found. */
|
|
219
|
+
cross_publisher_relation: CrossPublisherCollectionRelation | null;
|
|
220
|
+
/** Declared property namespaces, bytewise ordered. */
|
|
221
|
+
property_publisher_domains: string[];
|
|
222
|
+
}
|
|
223
|
+
export type CollectionSelectionEvaluation = {
|
|
224
|
+
eligible: true;
|
|
225
|
+
witness: CollectionSelectionWitness;
|
|
226
|
+
} | {
|
|
227
|
+
eligible: false;
|
|
228
|
+
reason: string;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Decide whether one discovered product qualifies, and retain the witness
|
|
232
|
+
* for the candidate that is actually selected.
|
|
233
|
+
*
|
|
234
|
+
* Qualification is evaluated against the complete validated candidate before
|
|
235
|
+
* any deterministic ordering is applied, so a nonqualifying first product can
|
|
236
|
+
* never hide a later qualifying one.
|
|
237
|
+
*/
|
|
238
|
+
export declare function evaluateCollectionSelection(candidate: unknown, selector: CollectionSelectionSelector): CollectionSelectionEvaluation;
|
|
239
|
+
/** Boolean form for the legacy matcher. */
|
|
240
|
+
export declare function productSatisfiesCollectionSelection(candidate: unknown, selector: CollectionSelectionSelector): boolean;
|
|
241
|
+
/** Derived witness captures a declaration may bind to a context key. */
|
|
242
|
+
export declare const COLLECTION_WITNESS_KINDS: readonly ["declared_selectors", "distinct_pairs", "strict_subset", "undeclared_collection_id_selectors", "undeclared_publisher_domain_selectors", "cross_publisher_relation", "property_publisher_domains"];
|
|
243
|
+
export type CollectionWitnessKind = (typeof COLLECTION_WITNESS_KINDS)[number];
|
|
244
|
+
export type CollectionWitnessReadResult = {
|
|
245
|
+
ok: true;
|
|
246
|
+
value: unknown;
|
|
247
|
+
} | {
|
|
248
|
+
ok: false;
|
|
249
|
+
detail: string;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Read one derived capture off a retained witness.
|
|
253
|
+
*
|
|
254
|
+
* A declared capture that cannot be built is a setup failure, never an
|
|
255
|
+
* unsupported/not-applicable downgrade — the storyboard asked for state the
|
|
256
|
+
* selected product does not provide, and silently proceeding would grade the
|
|
257
|
+
* agent against a request the author did not write.
|
|
258
|
+
*/
|
|
259
|
+
export declare function readCollectionWitness(witness: CollectionSelectionWitness | undefined, kind: CollectionWitnessKind): CollectionWitnessReadResult;
|