@instruments/materialgraph 0.26.0 → 0.27.1

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.
Files changed (113) hide show
  1. package/association-types.d.ts +18 -7
  2. package/attribution-types.d.ts +2 -2
  3. package/brand-types.d.ts +8 -0
  4. package/brands-client.d.ts +5 -36
  5. package/brands-client.js +7 -33
  6. package/by-colour-types.d.ts +40 -13
  7. package/by-spec-types.d.ts +0 -67
  8. package/by-spec-types.js +7 -5
  9. package/catalog-discovery-types.d.ts +1 -16
  10. package/catalogue-client.d.ts +12 -31
  11. package/catalogue-client.js +10 -46
  12. package/catalogue-types.d.ts +40 -0
  13. package/categories-client.d.ts +63 -0
  14. package/categories-client.js +48 -0
  15. package/category-types.d.ts +114 -0
  16. package/client-config.d.ts +2 -4
  17. package/client-config.js +2 -4
  18. package/client-vocabulary.d.ts +2 -4
  19. package/client-vocabulary.js +11 -9
  20. package/client.d.ts +103 -203
  21. package/client.js +128 -251
  22. package/codes-types.d.ts +21 -0
  23. package/colours-client.d.ts +4 -9
  24. package/colours-client.js +0 -8
  25. package/errors.d.ts +1 -1
  26. package/errors.js +1 -0
  27. package/identify-types.d.ts +49 -39
  28. package/identify-types.js +5 -3
  29. package/index.d.ts +11 -21
  30. package/index.js +4 -4
  31. package/list-variant-facets-types.d.ts +60 -0
  32. package/list-variants-types.d.ts +149 -0
  33. package/list-variants-types.js +12 -0
  34. package/match-stream-types.d.ts +13 -0
  35. package/match-stream-types.js +2 -0
  36. package/match-stream.js +12 -8
  37. package/match-types.d.ts +24 -0
  38. package/material-asset-types.d.ts +69 -49
  39. package/material-block-types.d.ts +2 -0
  40. package/material-composition-client.d.ts +21 -0
  41. package/material-composition-client.js +13 -0
  42. package/material-composition-types.d.ts +403 -0
  43. package/material-query-plan-types.d.ts +57 -141
  44. package/material-resources-client.d.ts +9 -7
  45. package/material-resources-client.js +19 -4
  46. package/material-types.d.ts +6 -14
  47. package/operations.d.ts +1 -1
  48. package/operations.js +20 -117
  49. package/package.json +5 -1
  50. package/palette-search-types.d.ts +59 -24
  51. package/query-understanding-types.d.ts +27 -5
  52. package/resolve-client.d.ts +6 -10
  53. package/resolve-client.js +5 -7
  54. package/response-schemas.d.ts +20 -0
  55. package/response-schemas.generated.d.ts +5 -0
  56. package/response-schemas.generated.js +160 -0
  57. package/response-schemas.js +113 -0
  58. package/run-types.d.ts +12 -93
  59. package/runs.d.ts +1 -37
  60. package/runs.js +2 -79
  61. package/schema-resource-types.d.ts +14 -1
  62. package/search-card-types.d.ts +22 -0
  63. package/search-index-plan-types.d.ts +70 -0
  64. package/search-refinement-types.d.ts +8 -38
  65. package/search-refinement-types.js +1 -0
  66. package/search-request-types.d.ts +117 -200
  67. package/search-response-receipt-types.d.ts +12 -268
  68. package/search-result-validation.d.ts +0 -16
  69. package/search-result-validation.js +22 -180
  70. package/spec-facet-types.d.ts +3 -8
  71. package/spec-types.d.ts +17 -10
  72. package/spec-types.js +8 -7
  73. package/specifications.d.ts +13 -55
  74. package/specifications.js +29 -73
  75. package/suggest-types.d.ts +4 -10
  76. package/suggest-types.js +1 -1
  77. package/types.d.ts +135 -338
  78. package/webmcp.d.ts +1 -1
  79. package/webmcp.generated.js +10 -94
  80. package/brand-most-sampled-types.d.ts +0 -33
  81. package/browse-types.d.ts +0 -236
  82. package/client-browse.d.ts +0 -78
  83. package/client-browse.js +0 -71
  84. package/client-exploration.d.ts +0 -9
  85. package/client-exploration.js +0 -5
  86. package/compare-types.d.ts +0 -89
  87. package/composition-client.d.ts +0 -33
  88. package/composition-client.js +0 -28
  89. package/composition-types.d.ts +0 -455
  90. package/composition-types.js +0 -10
  91. package/correction-types.d.ts +0 -88
  92. package/exploration-types.d.ts +0 -118
  93. package/exploration-types.js +0 -1
  94. package/fan-deck-types.d.ts +0 -42
  95. package/fan-deck-types.js +0 -9
  96. package/match-rejection-types.d.ts +0 -43
  97. package/match-rejection-types.js +0 -7
  98. package/palette-completion-types.d.ts +0 -115
  99. package/palette-completion-types.js +0 -1
  100. package/render-assets-types.d.ts +0 -65
  101. package/render-assets-types.js +0 -1
  102. package/search-collapse-types.d.ts +0 -69
  103. package/search-collapse-types.js +0 -1
  104. package/search-mood-types.d.ts +0 -6
  105. package/search-mood-types.js +0 -1
  106. package/search-window-types.d.ts +0 -25
  107. package/search-window-types.js +0 -1
  108. package/slot-pool-types.d.ts +0 -372
  109. package/slot-pool-types.js +0 -1
  110. /package/{brand-most-sampled-types.js → category-types.js} +0 -0
  111. /package/{browse-types.js → list-variant-facets-types.js} +0 -0
  112. /package/{compare-types.js → material-composition-types.js} +0 -0
  113. /package/{correction-types.js → search-index-plan-types.js} +0 -0
package/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { ResponseAttribution } from "./attribution-types.js";
2
- import type { BySpecVerdict } from "./by-spec-types.js";
3
2
  /**
4
3
  * Hand-mirrored wire types for the MaterialGraph public API (spec 0.5.0).
5
4
  * These interfaces intentionally have NO runtime dependency on any
@@ -26,18 +25,13 @@ import type { BySpecVerdict } from "./by-spec-types.js";
26
25
  * match set. Follow the cursor for the rest, and never report a page size as
27
26
  * a total.
28
27
  */
29
- import type { CorpusScopeReceipt } from "./corpus-scope-types.js";
28
+ import type { CorpusScope } from "./corpus-scope-types.js";
30
29
  import type { Cursor, ProductId, VariantId } from "./identifiers.js";
31
- import type { MaterialDetail, MaterialTile, SiblingTile } from "./material-types.js";
32
- import type { QueryUnderstanding } from "./query-understanding-types.js";
33
- import type { CollapsedVariant, SearchAnatomyMember } from "./search-collapse-types.js";
34
- import type { MoodInterpretation } from "./search-mood-types.js";
35
- import type { SearchDisambiguation, SearchSuggestion } from "./search-refinement-types.js";
36
- import type { CompoundColourConstraintReceipt, CompoundFormConstraintReceipt, SearchExecutionPlan, SearchMode, SearchRefinementReceipt, SearchRerankReport, SearchResponseExplainReceipts } from "./search-response-receipt-types.js";
37
- import type { SearchWindowReceipt } from "./search-window-types.js";
30
+ import type { FilterOp } from "./list-variants-types.js";
31
+ import type { MaterialDetail, MaterialTile } from "./material-types.js";
32
+ import type { SearchMode, SearchResponseExplainReceipts } from "./search-response-receipt-types.js";
38
33
  export type { SearchResponse } from "./search-response-types.js";
39
34
  export type * from "./search-response-receipt-types.js";
40
- export type { MoodInterpretation } from "./search-mood-types.js";
41
35
  export type OpenEnum<T extends string> = T | (string & {});
42
36
  export type { JsonValue } from "./json-value-types.js";
43
37
  /**
@@ -83,37 +77,21 @@ export type MaterialColour = {
83
77
  };
84
78
  /** Where the served image came from. */
85
79
  export type ImageSource = OpenEnum<"r2" | "origin">;
86
- export type { DimensionEntry, MaterialAvailability, MaterialDetail, MaterialFacet, MaterialSuccessor, MaterialTile, SiblingTile, } from "./material-types.js";
87
- /** Per-signal scores, present on a result when `explain` was requested. */
80
+ export type { DimensionEntry, MaterialAvailability, MaterialDetail, MaterialFacet, MaterialSuccessor, MaterialTile, } from "./material-types.js";
81
+ /**
82
+ * Match only: the image-space cosine between the query image and this row's
83
+ * image, in [0,1]. Null when it was not measured.
84
+ */
88
85
  export type SearchResultSignals = {
89
- /** Voyage similarity in the dedicated variant text-portrait space. */
90
- portrait?: number | null;
91
86
  image: number | null;
92
- category: number | null;
93
- colour: number | null;
94
- lexical: number | null;
95
- /** Compatibility slot for retired seeded secondary scoring; currently null. */
96
- text: number | null;
97
- };
98
- /** An accepted/measured visual meaning — descriptive evidence, never a ranking cause. */
99
- export type VisualMeaning = {
100
- id: string;
101
- label: string;
102
- values: string[];
103
- basis: OpenEnum<"canonical_attribute" | "image_colour_analysis">;
104
- confidence?: number;
105
- sourceBasis?: string;
106
- version?: string;
107
- appliedAt?: string;
108
87
  };
109
88
  /**
110
89
  * One material in a search / match result list.
111
90
  *
112
91
  * ABSENT IS NOT FALSE. The optional fields here (`colourMeta`, `signals`,
113
- * `retired`, `siblingCount`, `visualMeanings`, `verdict`) are absent when the
114
- * request did not ask for them or the graph holds no answer — never because the
115
- * answer is "no". A missing `retired` is not "still current"; a missing
116
- * `visualMeanings` is not "this material has no visual meaning".
92
+ * `retired`, `productType`) are absent when the request did not ask for them or
93
+ * the graph holds no answer — never because the answer is "no". A missing
94
+ * `retired` is not "still current".
117
95
  */
118
96
  export type SearchResultItem = {
119
97
  /** This hit's attribution key — see {@link ResponseAttribution}. Opaque. */
@@ -157,7 +135,20 @@ export type SearchResultItem = {
157
135
  color: string | null;
158
136
  variantName: string | null;
159
137
  imageUrl: string | null;
138
+ /**
139
+ * SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
140
+ * cache or dedupe on it rather than the URL. ABSENT means no hash is on
141
+ * record, never that there is no image.
142
+ */
143
+ imageSha256?: string;
160
144
  hasImage: boolean;
145
+ /**
146
+ * How many eligible variants the row's product has, the same set
147
+ * `listVariants` pages, so a follow-up list returns this many: the card's
148
+ * "12 colourways". ABSENT means the product is unknown or has no eligible
149
+ * variant.
150
+ */
151
+ variantCount?: number;
161
152
  palette: PaletteSwatch[];
162
153
  colourMeta?: ColourMeta;
163
154
  /**
@@ -175,28 +166,14 @@ export type SearchResultItem = {
175
166
  hex: string;
176
167
  };
177
168
  /**
178
- * EXPLAIN-only: the two identities a COLLAPSED result holds, which are not the
179
- * same thing and are routinely confused.
180
- *
181
- * - `representative` — the RANKING identity. Its score is the group's cursor
182
- * key and the item's `signals` describe IT, not the photograph on the card.
183
- * Omitted when the face IS the representative.
184
- * - the item's own `variantId` — the DISPLAY identity (the face).
185
- *
186
- * `siblingsTruncated` says the sibling list was capped: a product can hold
187
- * hundreds of colourways, so absence of a sibling is never evidence it does
188
- * not exist.
169
+ * Selected mode's weighted primary score; see `maxScore`. On a Match page
170
+ * this is the retrieval score and does NOT order the page: the order is
171
+ * colour agreement, then `confidence` (MG-1900).
189
172
  */
190
- anatomy?: {
191
- representative?: SearchAnatomyMember;
192
- siblings?: SearchAnatomyMember[];
193
- siblingsTruncated?: boolean;
194
- };
195
- /** Selected mode's weighted primary score; see `maxScore`. */
196
173
  score: number;
197
174
  /**
198
175
  * Match only. How close this material is to the query, as an ABSOLUTE
199
- * image-space similarity in [0,1] adjusted for colour — the same unit as
176
+ * image-space similarity in [0,1], not adjusted for colour — the same unit as
200
177
  * `qualityFloor` and `comparison.bestRejected`, so all three sit on one axis.
201
178
  *
202
179
  * Never normalised against the page, so the same material reads the same in
@@ -205,7 +182,9 @@ export type SearchResultItem = {
205
182
  * "99%+"); `Math.round` prints 100% from 0.995 up. Capping it server-side
206
183
  * would put it on a different ceiling from `comparison.bestRejected`, which
207
184
  * publishes the same unit uncapped, and the two are meant to be comparable.
208
- * `score` is the ranking value and lives on a different scale.
185
+ * A Match page lists rows whose colour agrees (`colourDistance` under 0.1)
186
+ * first, then unmeasured ones, then the rest, and orders each set by this
187
+ * number. `score` is the retrieval score and lives on a different scale.
209
188
  */
210
189
  confidence?: number;
211
190
  /**
@@ -215,8 +194,9 @@ export type SearchResultItem = {
215
194
  */
216
195
  confidenceBand?: "strong" | "good" | "fair" | "weak";
217
196
  /**
218
- * Match only. Proportion-weighted OKLab distance from the query crop's palette
219
- * to this material's. `null` means the material carries no stored palette —
197
+ * Match only. OKLab earth mover's distance between the query crop's palette
198
+ * and this material's, reading both palettes' proportions. Under 0.1 the
199
+ * colour agrees. `null` means the material carries no stored palette —
220
200
  * UNKNOWN colour, never "no colour" and never "distant". Absent when the
221
201
  * colour stage did not run.
222
202
  */
@@ -242,18 +222,6 @@ export type SearchResultItem = {
242
222
  * `includeBelowFloor`.
243
223
  */
244
224
  admittedOnColour?: boolean;
245
- /** Variants HIDDEN behind this card by `collapse: "product"` — the COMPLETE tally, even when {@link variants} is capped. */
246
- siblingCount?: number;
247
- /** The product's matching variants, MOST RELEVANT FIRST — present exactly under `collapse: "product"`, capped, and led by the group's RANKING representative, which is NOT always this item. {@link CollapsedVariant} states the full contract. */
248
- variants?: CollapsedVariant[];
249
- visualMeanings?: VisualMeaning[];
250
- /**
251
- * The shared report card for this hit against the request's `filters` (required
252
- * cells) and `preferences` (preferred cells). Present (full view only) whenever a
253
- * spec filter or preference was given. Reuses the by-spec verdict shape — the
254
- * shared CandidateVerdict mirror; see {@link MaterialVerdict}.
255
- */
256
- verdict?: BySpecVerdict;
257
225
  /**
258
226
  * Resolved values for the attribute ids sent in `returnAttributes`, keyed by
259
227
  * attribute id. ABSENT entirely when `returnAttributes` was not sent.
@@ -272,31 +240,23 @@ export type SearchResultItem = {
272
240
  * a different number from `results.length` (this page).
273
241
  *
274
242
  * `exact` — the true count. Say "473 materials match".
275
- * `lowerBound` — a floor (cap hit, ANN-pool bound, or a degraded count). Say
276
- * "at least 1,000", never "1,000". Never a silent approximation.
243
+ * `lowerBound` — a floor (cap hit or an ANN-pool bound). Say "at least
244
+ * 1,000", never "1,000". Never a silent approximation.
245
+ * `unknown` — the count query itself faulted (MG-2104): `value` is the pool
246
+ * this page was drawn from, NOT a claim about the corpus — do not print it as
247
+ * a bound. Check the response's `degraded.parts` for `"variant-count"`.
277
248
  *
278
- * The CAP differs by lane, and only `lowerBound` ever tells you one was hit. The
279
- * SEARCH feed counts exactly to 1,000 and reports a floor beyond it, because it
280
- * counts over attribute and tsquery universes where an unbounded count is a
281
- * timeout. The BROWSE feed ({@link BrowseCounts}) is uncapped — a plain count
282
- * over the same predicate its page scan ran, so a category of 66,000 reports
283
- * 66,000 exactly. Read `basis`; never assume a ceiling.
249
+ * Only `lowerBound` ever tells you a cap was hit. The SEARCH feed counts
250
+ * exactly to 1,000 and reports a floor beyond it, because it counts over
251
+ * attribute and tsquery universes where an unbounded count is a timeout. Read
252
+ * `basis`; never assume a ceiling.
284
253
  *
285
254
  * A response with no `counts` at all states no total: `count: true` is opt-in,
286
255
  * and the absence of a total is not a total of zero.
287
256
  */
288
257
  export type CountedTotal = {
289
258
  value: number;
290
- basis: OpenEnum<"exact" | "lowerBound">;
291
- };
292
- /**
293
- * Feed counts — present ONLY under `count:true` or `collapse:"product"`. This is
294
- * the corpus-level claim; `results.length` is the page. Never report one as the
295
- * other.
296
- */
297
- export type SearchCounts = {
298
- variants: CountedTotal;
299
- products?: CountedTotal;
259
+ basis: OpenEnum<"exact" | "lowerBound" | "unknown">;
300
260
  };
301
261
  /**
302
262
  * One value within a facet's counts: the CANONICAL token (send it back verbatim
@@ -349,126 +309,40 @@ export type SearchFacets = {
349
309
  /**
350
310
  * The search response envelope. `view` discriminates the `results` union:
351
311
  * `"full"` (default) carries {@link SearchResultItem}s, `"card"` the lean
352
- * {@link SearchCardItem}s. Every other field is shared. `counts` is present
353
- * under `count:true` or `collapse:"product"` (replacing the former
354
- * `variantCount`/`productCount`).
312
+ * {@link SearchCardItem}s. Every other field is shared.
355
313
  *
356
- * A PAGE, NOT A CORPUS. `results.length` is what you were handed;
357
- * {@link SearchCounts} is the only statement about the match set, and only when
358
- * you asked for it. Follow `cursor` for the rest. "I found 24 materials" is
359
- * wrong whenever `limit` was 24.
314
+ * A PAGE, NOT A CORPUS. `results.length` is what you were handed; follow
315
+ * `cursor` for the rest. "I found 24 materials" is wrong whenever `limit` was
316
+ * 24.
360
317
  */
361
318
  export type SearchData = {
362
319
  /** Result attribution — see {@link ResponseAttribution}. `basis` is `mode` verbatim here. */
363
320
  attribution?: ResponseAttribution;
321
+ /**
322
+ * Present when a word no material carries was corrected before the search
323
+ * ran: the page shows results for `to` (ADR 0031).
324
+ */
325
+ correctedQuery?: {
326
+ from: string;
327
+ to: string;
328
+ };
364
329
  view: OpenEnum<"full" | "card">;
365
330
  /** THIS PAGE. Its length is a page size, never a total. */
366
331
  results: SearchResultItem[];
367
- suggestions: SearchSuggestion[];
332
+ /** Always empty: the one search offers no refinements. */
333
+ suggestions: never[];
368
334
  mode: SearchMode;
369
- /**
370
- * Which dense text lane answered, when one did (MG-1796). `image-text` — the
371
- * query carried evidence it describes appearance, so it was ranked against
372
- * image vectors; `variant-text` — ranked against each variant's written
373
- * portrait.
374
- *
375
- * ABSENT means the answer did not come from the dense text arm at all: `mode`
376
- * was never `text`, or the text arm resolved without a lane. Absence is
377
- * UNKNOWN and never a default of `image-text` — treating it as one asserts a
378
- * dense reading of a lexical, colour or browse answer. Absent too from any
379
- * response produced by a server predating MG-1793.
380
- *
381
- * An {@link OpenEnum} because the lane vocabulary can grow: a value you do
382
- * not recognise is a lane this client predates, not a malformed response.
383
- */
384
- textLane?: OpenEnum<"image-text" | "variant-text">;
335
+ /** The first result's rank score, 1. */
385
336
  maxScore: number;
386
337
  cursor?: Cursor;
387
- window?: SearchWindowReceipt;
388
- rerank?: SearchRerankReport;
389
- /** Hard compound eligibility and abundance receipts, when applied. */
390
- colourConstraint?: CompoundColourConstraintReceipt;
391
- formConstraint?: CompoundFormConstraintReceipt;
392
- /** Explain-only receipt of the query shape and transform ledger that ran. */
393
- plan?: SearchExecutionPlan;
394
- counts?: SearchCounts;
395
- /**
396
- * The `#rrggbb` colours the colour signal ACTUALLY ran on, after
397
- * minor-proportion accents were dropped. Present whenever the request carried
398
- * explicit `colours`. Compare against your request: a difference means the
399
- * search that ran is not the search that was asked for.
400
- */
401
- effectiveColours?: string[];
402
- /**
403
- * Colours removed from the query for carrying a `weight` below the 5% accent
404
- * threshold. EMPTY is the normal case. Non-empty and the dropped colours are
405
- * absent from colour retrieval and ranking entirely — they did not merely
406
- * rank low. To keep a small accent in the query, give it `role: "accent"`.
407
- */
408
- droppedColours?: string[];
409
- /** Per-query facet counts — present ONLY when the request sent `facetCounts`. */
410
- facets?: SearchFacets;
411
- /** Receipt that narrows the exact finalized pool behind lower-bound facets. */
412
- refinement?: SearchRefinementReceipt;
413
338
  /** One or more sentences written for a person; absent when there is nothing to say. */
414
339
  note?: string;
415
340
  timings?: Record<string, number>;
416
- /** A detected mood term overridden by a stronger colour reading. */
417
- supersededMoodId?: string;
418
- /**
419
- * Explicit standard-search mood reading; null ids mean no selected mood.
420
- * Absent when mood was not evaluated or an older server produced the result.
421
- */
422
- moodInterpretation?: MoodInterpretation;
423
- /**
424
- * Present when the query had multiple defensible readings. Says which one
425
- * ran, or leaves every reading non-default when the engine chose none. Every
426
- * reading carries a complete executable replacement request.
427
- */
428
- disambiguation?: SearchDisambiguation;
429
- /** Structured receipt of the standard query-understanding decisions. */
430
- understanding?: QueryUnderstanding;
431
- /**
432
- * The corpus this page AND its `counts` were drawn from — present ONLY when
433
- * the request narrowed it. ABSENT means the whole graph: every material
434
- * MaterialGraph knows, orderable or not. Read it alongside `counts`: a short
435
- * page under a scope is a small SCOPED corpus, not a truncated one.
436
- */
437
- corpusScope?: CorpusScopeReceipt;
438
341
  /** Echo of the resolved query input (raw image bytes/vector are never echoed). */
439
342
  query?: Record<string, unknown>;
440
343
  /** A `getVariantBrief` echo of the seed variant, present only in `variantId` seed mode. */
441
344
  seed?: MaterialTile | Record<string, unknown>;
442
345
  } & SearchResponseExplainReceipts;
443
- export type ColourDictionaryId = OpenEnum<"colour_family" | "colour_mood" | "colour_undertone">;
444
- export type ColourFacetTerm = {
445
- id: string;
446
- label: string;
447
- aliases: string[];
448
- variants: number;
449
- brands: number;
450
- };
451
- export type ColourFacetDictionary = {
452
- attributeId: ColourDictionaryId;
453
- name: string;
454
- description: string;
455
- terms: ColourFacetTerm[];
456
- };
457
- export type ColourFacetsResponse = {
458
- count: number;
459
- dictionaries: ColourFacetDictionary[];
460
- };
461
- export type CategoryNode = {
462
- key: string;
463
- name: string;
464
- parentKey: string | null;
465
- path: string;
466
- level: OpenEnum<"group" | "type">;
467
- };
468
- export type TaxonomyResponse = {
469
- count: number;
470
- categories: CategoryNode[];
471
- };
472
346
  export type DemandMomentum = {
473
347
  state: OpenEnum<"rising" | "fading" | "stable" | "emerging" | "insufficient_volume">;
474
348
  percentChange: number | null;
@@ -503,6 +377,12 @@ export type ProjectTypeShare = {
503
377
  export type CospecPairing = {
504
378
  /** The evidence tier. `cospec` = orders; `considered` = boards. Read it before phrasing. */
505
379
  source: "cospec" | "considered";
380
+ /**
381
+ * Membership of the requested `corpusScope`: true in, false out, null when
382
+ * the graph cannot say. Absent when no narrowing scope was asked for. It
383
+ * flags, it never removes (MG-2292).
384
+ */
385
+ inScope?: boolean | null;
506
386
  /** Opaque — round-trip verbatim. */
507
387
  variantId: VariantId;
508
388
  brand: string;
@@ -547,11 +427,16 @@ export type CospecPairing = {
547
427
  export type ColourPairing = {
548
428
  source: "colour";
549
429
  score: number;
430
+ /**
431
+ * Membership of the requested `corpusScope`: true in, false out, null when
432
+ * the graph cannot say. Absent when no narrowing scope was asked for. It
433
+ * flags, it never removes (MG-2292).
434
+ */
435
+ inScope?: boolean | null;
550
436
  /** Graph-owned colour ranking facts, present only for an explicitly ordered rail. */
551
437
  ranking?: PairingRanking;
552
438
  } & MaterialTile;
553
439
  export type PairingOrderMode = "relevance" | "lightness_asc" | "lightness_desc";
554
- export type PairingCorpus = "all" | "material_bank";
555
440
  export type PairingRanking = {
556
441
  anchorColourSimilarity: number | null;
557
442
  lightness: number | null;
@@ -602,29 +487,6 @@ export type PairingsResponse = {
602
487
  };
603
488
  export type MaterialResponse = {
604
489
  material: MaterialDetail;
605
- /** Present only when `include: "siblings"` was requested. */
606
- siblings?: SiblingTile[];
607
- };
608
- export type SiblingsResponse = {
609
- siblings: SiblingTile[];
610
- };
611
- /**
612
- * Batch read of tile projections. `materials` is NOT positional — resolve by
613
- * `variantId`, and read `missingIds` rather than inferring absence from length.
614
- */
615
- export type MaterialsBatchTileResponse = {
616
- materials: MaterialTile[];
617
- /** Requested ids that resolved to nothing. Echoed verbatim, opaque. */
618
- missingIds: VariantId[];
619
- };
620
- /**
621
- * Batch read of detail projections. `materials` is NOT positional — resolve by
622
- * `variantId`, and read `missingIds` rather than inferring absence from length.
623
- */
624
- export type MaterialsBatchDetailResponse = {
625
- materials: MaterialDetail[];
626
- /** Requested ids that resolved to nothing. Echoed verbatim, opaque. */
627
- missingIds: VariantId[];
628
490
  };
629
491
  export type SuggestColourResponse = {
630
492
  suggestions: {
@@ -634,17 +496,56 @@ export type SuggestColourResponse = {
634
496
  kind: string;
635
497
  }[];
636
498
  };
637
- export type AttributeCandidate = {
638
- attributeId: string;
499
+ /**
500
+ * How a {@link AttributeListItem} matched the query. Declared here rather than
501
+ * imported: this package is published, so it may not reach across into
502
+ * `@materialgraph/products`, whose `listAttributesResultSchema` is the source
503
+ * of truth this wire type tracks. `method` is deliberately open - a server
504
+ * that grows a method must not break an older client.
505
+ */
506
+ export type AttributeMatch = {
507
+ method: OpenEnum<"exact" | "alias" | "fuzzy" | "vector">;
508
+ /** The id, name or alias that matched, or null for a match by meaning. */
509
+ matchedText: string | null;
510
+ /** Fuse distance, 0 best. Compares only within one method. */
511
+ distance?: number;
512
+ /** Cosine similarity, 1 best. Compares only within one method. */
513
+ similarity?: number;
514
+ };
515
+ /** One attribute definition on a `listAttributes()` page. */
516
+ export type AttributeListItem = {
517
+ id: string;
639
518
  name: string;
640
519
  description: string;
641
- score: number;
642
- facetable: boolean;
520
+ valueKind: string;
521
+ unitId: string | null;
522
+ /** The value dictionary its values come from, or null. */
523
+ dictionaryId: string | null;
524
+ blockIds: string[];
525
+ /** The `listVariants()` filter ops it accepts; empty when it cannot filter on it. */
526
+ filterOps: FilterOp[];
527
+ filterable: boolean;
528
+ /** Present only when the page answers a query. */
529
+ match?: AttributeMatch;
530
+ /** Live variants in the requested categories stating it; absent without `categoryIds`. */
531
+ statedBy?: number;
532
+ };
533
+ /** An attribute a block-scoped `listAttributes()` query missed, named with its real blocks. */
534
+ export type OutsideScopeAttribute = {
535
+ id: string;
536
+ name: string;
643
537
  blockIds: string[];
644
538
  };
645
- export type ResolveAttributeResponse = {
646
- query: string;
647
- candidates: AttributeCandidate[];
539
+ export type ListAttributesResult = {
540
+ items: AttributeListItem[];
541
+ /** Page one only: matches outside `blockIds` ranking above the best item. Absent when the check did not apply. */
542
+ outsideScope?: OutsideScopeAttribute[];
543
+ /** Live variants across the requested categories; absent without `categoryIds`. */
544
+ liveVariants?: number;
545
+ pageInfo: {
546
+ endCursor: string | null;
547
+ hasNextPage: boolean;
548
+ };
648
549
  };
649
550
  export type * from "./catalog-discovery-types.js";
650
551
  export type * from "./resolve-types.js";
@@ -671,19 +572,8 @@ export type SearchImageInput = {
671
572
  type: "base64";
672
573
  data: string;
673
574
  };
674
- export type { SearchGetParams, SearchInput } from "./search-request-types.js";
675
- /** Options for a batch material read. */
676
- export type MaterialsBatchOptions = {
677
- /** Projection depth. `tile` omits fields rather than reporting them absent. */
678
- view?: "tile" | "detail";
679
- /**
680
- * Which id space `ids` are in — MaterialGraph's own opaque `variantId`
681
- * (default) or Material Bank's `materialBankId`. Both are opaque; they are NOT
682
- * interchangeable, and passing one under the other's `idKind` returns
683
- * `missingIds`, not an error.
684
- */
685
- idKind?: OpenEnum<"variantId" | "materialBankId">;
686
- };
575
+ export type { SearchGetParams, SearchIndexFilter, SearchInput, } from "./search-request-types.js";
576
+ export type { SearchIndexPlan, SearchPlanReceipt, } from "./search-index-plan-types.js";
687
577
  /** Options for a pairings read. */
688
578
  export type PairingsOptions = {
689
579
  /** Comma-separated coarse category display names; filters both arms. */
@@ -694,26 +584,25 @@ export type PairingsOptions = {
694
584
  sampledOnly?: boolean;
695
585
  /** Graph-owned order applied within fixed cospec → considered → colour tiers. */
696
586
  order?: PairingOrderMode;
697
- /** Optional Material Bank/orderable gate; requires an explicit `order`. */
698
- corpus?: PairingCorpus;
587
+ /** Flags each item's `inScope` against this scope; never narrows the rail. */
588
+ corpusScope?: CorpusScope;
699
589
  /** The previous response's `nextCursor`, passed back verbatim. */
700
590
  cursor?: Cursor;
701
591
  };
702
- /** Options for `catalog/attributes/resolve`. */
703
- export type ResolveAttributeOptions = {
592
+ /** Options for `listAttributes()` (GET `/api/v1/catalog/attributes`). */
593
+ export type ListAttributesOptions = {
594
+ /** An attribute id, name, misspelling or meaning, e.g. "reduces echo". */
595
+ query?: string;
596
+ /** Keep attributes in any of these blocks. */
597
+ blockIds?: string[];
598
+ /** Category ids from `categories.list()`; keeps attributes their templates carry. */
599
+ categoryIds?: string[];
600
+ /** Keep only attributes `listVariants()` can filter on. */
601
+ filterableOnly?: boolean;
602
+ /** Items per page, 1 to 50. Default 20. */
704
603
  limit?: number;
705
- facetableOnly?: boolean;
706
- };
707
- /** Options for `search/specifications`. */
708
- export type BySpecOptions = {
709
- limit?: number;
710
- /**
711
- * Numeric range criteria as `attribute:min..max` strings, ANDed with the
712
- * `spec` criteria. Build them with `specRangeCriterion` rather than by hand —
713
- * both bounds are inclusive and at least one is required, and a hand-built
714
- * token with neither is refused by the server rather than matching everything.
715
- */
716
- ranges?: string[];
604
+ /** `pageInfo.endCursor` from the previous page, with the same query and scope. */
605
+ cursor?: string;
717
606
  };
718
607
  export type * from "./demand-types.js";
719
608
  /** One quarter's tally. Sparse across a series — an absent quarter is not a zero. */
@@ -755,91 +644,6 @@ export type ExtractPaletteResponse = {
755
644
  palette: ExtractedPalette | null;
756
645
  };
757
646
  export type * from "./spec-facet-types.js";
758
- export type ApplicableSpecAttribute = {
759
- id: string;
760
- name: string;
761
- valueKind: string;
762
- required: boolean;
763
- };
764
- export type ApplicableSpecBlock = {
765
- blockId: string;
766
- name: string;
767
- applicability: OpenEnum<"required" | "recommended" | "optional" | "unusual">;
768
- attributes: ApplicableSpecAttribute[];
769
- };
770
- export type ApplicableSpecsResponse = {
771
- found: boolean;
772
- productType: {
773
- id: string;
774
- name: string;
775
- } | null;
776
- blocks: ApplicableSpecBlock[];
777
- hint?: string;
778
- };
779
- export type GlossaryAttributeMatch = {
780
- kind: "attribute";
781
- id: string;
782
- name: string;
783
- description: string;
784
- valueKind: string;
785
- unit?: string;
786
- facetable: boolean;
787
- comparable: boolean;
788
- dictionaryId?: string;
789
- usedInBlocks: string[];
790
- };
791
- export type GlossaryValueMatch = {
792
- kind: "value";
793
- id: string;
794
- label: string;
795
- description?: string;
796
- aliases: string[];
797
- dictionaryId: string;
798
- dictionaryName: string;
799
- attributeIds: string[];
800
- };
801
- export type GlossaryTermResponse = {
802
- found: boolean;
803
- term: string;
804
- match: GlossaryAttributeMatch | GlossaryValueMatch | null;
805
- alternates?: {
806
- kind: OpenEnum<"attribute" | "value">;
807
- id: string;
808
- label: string;
809
- }[];
810
- };
811
- export type BrandProfile = {
812
- id: string;
813
- slug: string;
814
- name: string;
815
- marketSegment: string;
816
- headquartersCity: string | null;
817
- headquartersCountry: string | null;
818
- parentBrand: {
819
- id: string;
820
- name: string;
821
- } | null;
822
- positioning: string | null;
823
- ownershipType: string | null;
824
- foundedYear: number | null;
825
- description: string | null;
826
- website: string | null;
827
- };
828
- export type BrandProfileResponse = {
829
- found: boolean;
830
- query: string;
831
- brand: BrandProfile | null;
832
- /** Trade / spec signals ON RECORD, keyed by attribute id. Absent = no value held (never infer false). */
833
- trade: Record<string, boolean | string>;
834
- links: {
835
- label: string;
836
- url: string;
837
- }[];
838
- alternates: {
839
- id: string;
840
- name: string;
841
- }[];
842
- };
843
647
  /** Body for `POST /api/v1/search/palettes/extract`. */
844
648
  export type ExtractPaletteInput = {
845
649
  /** The query image as a data URI or bare base64 string. */
@@ -847,10 +651,3 @@ export type ExtractPaletteInput = {
847
651
  /** Max palette swatches to return (default 6). */
848
652
  max?: number;
849
653
  };
850
- /** Options for a cacheable GET batch read (`materialsCached`). */
851
- export type MaterialsBatchGetOptions = MaterialsBatchOptions;
852
- /** Options for `brands/{brandSlug}`. */
853
- export type BrandOptions = {
854
- /** Alternate fuzzy query; takes precedence over the path slug when present. */
855
- q?: string;
856
- };
package/webmcp.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Canonical operation contracts. Consumers validate both boundaries and supply their own transport. */
2
2
  export type WebMcpToolContract = {
3
- readonly name: "identify" | "search" | "compare" | "create_correction";
3
+ readonly name: "search";
4
4
  readonly operationId: string;
5
5
  readonly path: string;
6
6
  readonly method: "POST";