@qloo/qloo-harness 0.1.18 → 0.1.20

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.
@@ -1,6 +1,599 @@
1
1
  import { createRequire as __qlooCreateRequire } from "node:module";
2
2
  const require = __qlooCreateRequire(import.meta.url);
3
3
 
4
+ // packages/qloo-tool-schema/dist/upstream.lock.json
5
+ var upstream_lock_default = {
6
+ lockVersion: 1,
7
+ source: {
8
+ documentationUrl: "https://docs.qloo.com",
9
+ registryUrl: "https://dash.readme.com/api/v1/api-registry/4svkj1svmrti5tfx",
10
+ registryId: "4svkj1svmrti5tfx",
11
+ retrievedAt: "2026-08-20",
12
+ apiTitle: "Qloo API",
13
+ apiVersion: "2.0",
14
+ openapiVersion: "3.1.0",
15
+ sha256: "da8d4b830f2a646815d25afcaa653f81f1ad939707e2e2560d2d0b9f103076f1"
16
+ },
17
+ expectations: {
18
+ productionServer: "https://api.qloo.com",
19
+ authentication: {
20
+ scheme: "ApiKey",
21
+ type: "apiKey",
22
+ location: "header",
23
+ header: "X-Api-Key"
24
+ },
25
+ getPaths: [
26
+ "/",
27
+ "/entities",
28
+ "/search",
29
+ "/v2/analysis/compare",
30
+ "/v2/audiences",
31
+ "/v2/audiences/types",
32
+ "/v2/insights",
33
+ "/v2/tags",
34
+ "/v2/tags/types",
35
+ "/v2/trending"
36
+ ]
37
+ },
38
+ documentedExceptions: [
39
+ {
40
+ value: "urn:demographics",
41
+ scope: "/v2/insights filter.type",
42
+ status: "documented-but-not-enumerated",
43
+ documentationUrl: "https://docs.qloo.com/reference/demographics-use-case",
44
+ treatment: "Preserve as an explicit documentation exception; do not synthesize an endpoint payload schema from it."
45
+ },
46
+ {
47
+ value: "urn:tag",
48
+ scope: "/v2/insights filter.type",
49
+ status: "documented-but-not-enumerated",
50
+ documentationUrl: "https://docs.qloo.com/reference/taste-analysis",
51
+ treatment: "The pinned specification mentions this value in diversify.by documentation but omits it from the filter.type enum; preserve the discrepancy for review."
52
+ }
53
+ ]
54
+ };
55
+
56
+ // packages/qloo-tool-schema/dist/src/catalog.js
57
+ var QLOO_TOOL_CATALOG_VERSION = "1.0.0";
58
+ var QLOO_TOOL_RESULT_SCHEMA_VERSION = "1.0-preview.1";
59
+ var QLOO_WORKFLOW_OPERATION_IDS = [
60
+ "recommend",
61
+ "rank",
62
+ "describe",
63
+ "where_popular",
64
+ "compare_audiences",
65
+ "entity_tags",
66
+ "audience_demographics",
67
+ "trends",
68
+ "find_tags"
69
+ ];
70
+ var QLOO_CAPABILITIES_TOOL_NAME = "qloo_capabilities";
71
+ var QLOO_INSIGHTS_ENTITY_TYPES = [
72
+ { id: "artist", urn: "urn:entity:artist" },
73
+ { id: "book", urn: "urn:entity:book" },
74
+ { id: "brand", urn: "urn:entity:brand" },
75
+ { id: "movie", urn: "urn:entity:movie" },
76
+ { id: "person", urn: "urn:entity:person" },
77
+ { id: "place", urn: "urn:entity:place" },
78
+ { id: "podcast", urn: "urn:entity:podcast" },
79
+ { id: "tv_show", urn: "urn:entity:tv_show" },
80
+ { id: "videogame", urn: "urn:entity:videogame" }
81
+ ];
82
+ var QLOO_SEARCH_ENTITY_TYPES = [
83
+ ...QLOO_INSIGHTS_ENTITY_TYPES,
84
+ { id: "locality", urn: "urn:entity:locality" },
85
+ { id: "actor", urn: "urn:entity:actor" },
86
+ { id: "album", urn: "urn:entity:album" },
87
+ { id: "author", urn: "urn:entity:author" },
88
+ { id: "director", urn: "urn:entity:director" }
89
+ ];
90
+ function buildEntityTypeAliases(entityTypes) {
91
+ const aliases = /* @__PURE__ */ new Map();
92
+ const urnById = new Map(entityTypes.map(({ id, urn }) => [id, urn]));
93
+ for (const entityType of entityTypes) {
94
+ aliases.set(entityType.id, entityType.urn);
95
+ aliases.set(entityType.urn, entityType.urn);
96
+ aliases.set(`${entityType.id}s`, entityType.urn);
97
+ }
98
+ const addAlias = (alias, id) => {
99
+ const urn = urnById.get(id);
100
+ if (urn)
101
+ aliases.set(alias, urn);
102
+ };
103
+ addAlias("people", "person");
104
+ addAlias("localities", "locality");
105
+ addAlias("tv", "tv_show");
106
+ addAlias("show", "tv_show");
107
+ addAlias("shows", "tv_show");
108
+ addAlias("tv show", "tv_show");
109
+ addAlias("tv shows", "tv_show");
110
+ addAlias("game", "videogame");
111
+ addAlias("games", "videogame");
112
+ addAlias("video game", "videogame");
113
+ addAlias("video games", "videogame");
114
+ return aliases;
115
+ }
116
+ var insightsEntityTypeByAlias = buildEntityTypeAliases(QLOO_INSIGHTS_ENTITY_TYPES);
117
+ var searchEntityTypeByAlias = buildEntityTypeAliases(QLOO_SEARCH_ENTITY_TYPES);
118
+ function resolveQlooInsightsEntityType(value) {
119
+ return insightsEntityTypeByAlias.get(value.trim().toLowerCase());
120
+ }
121
+ function resolveQlooSearchEntityType(value) {
122
+ return searchEntityTypeByAlias.get(value.trim().toLowerCase());
123
+ }
124
+ var operationSummaries = {
125
+ recommend: "Find entities for a taste profile, audience, or collection of signals.",
126
+ rank: "Rank a supplied set of options for signals, location, or audience.",
127
+ describe: "Resolve and describe one Qloo entity.",
128
+ where_popular: "Find geographic areas where an entity has comparatively strong affinity.",
129
+ compare_audiences: "Contrast two signal groups and identify shared or differentiating affinities.",
130
+ entity_tags: "Retrieve concepts and tags that characterize entities.",
131
+ audience_demographics: "Retrieve aggregate audience segments associated with an entity.",
132
+ trends: "Retrieve interest or trend information over a time range.",
133
+ find_tags: "Search the Qloo tag ontology by natural-language concept."
134
+ };
135
+ var QLOO_WORKFLOW_OPERATIONS = Object.freeze(QLOO_WORKFLOW_OPERATION_IDS.map((id) => Object.freeze({
136
+ id,
137
+ toolName: `qloo_${id}`,
138
+ summary: operationSummaries[id],
139
+ since: QLOO_TOOL_CATALOG_VERSION
140
+ })));
141
+ var upstreamLock = upstream_lock_default;
142
+ var upstreamGetPaths = Object.freeze([
143
+ ...upstreamLock.expectations.getPaths
144
+ ]);
145
+ var documentedExceptions = Object.freeze(upstreamLock.documentedExceptions.map((exception) => Object.freeze({ ...exception })));
146
+ var QLOO_CAPABILITIES_TOOL = Object.freeze({
147
+ name: QLOO_CAPABILITIES_TOOL_NAME,
148
+ description: "Return the versioned Qloo workflow catalog and pinned upstream API contract metadata without making a network request.",
149
+ inputSchema: Object.freeze({
150
+ $schema: "https://json-schema.org/draft/2020-12/schema",
151
+ type: "object",
152
+ additionalProperties: false,
153
+ properties: Object.freeze({
154
+ operation_ids: Object.freeze({
155
+ type: "array",
156
+ description: "Optional canonical operation IDs to include. Omit for the complete catalog.",
157
+ uniqueItems: true,
158
+ items: Object.freeze({
159
+ type: "string",
160
+ enum: QLOO_WORKFLOW_OPERATION_IDS
161
+ })
162
+ })
163
+ })
164
+ })
165
+ });
166
+ var operationIds = new Set(QLOO_WORKFLOW_OPERATION_IDS);
167
+
168
+ // packages/qloo-tool-schema/dist/src/workflow-contracts.js
169
+ import { Type } from "typebox";
170
+ import { Value } from "typebox/value";
171
+ var QLOO_TOOL_DEFAULT_LIMIT = 10;
172
+ var QLOO_TOOL_MAX_RESULTS = 20;
173
+ var QLOO_TAG_BOOLEAN_OPERATORS = ["union", "intersection"];
174
+ var insightsEntityTypeIds = QLOO_INSIGHTS_ENTITY_TYPES.map(({ id }) => id);
175
+ var searchEntityTypeIds = QLOO_SEARCH_ENTITY_TYPES.map(({ id }) => id);
176
+ var QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA = Type.String({
177
+ enum: insightsEntityTypeIds,
178
+ description: `Qloo Insights entity type: ${insightsEntityTypeIds.join(", ")}.`
179
+ });
180
+ var QLOO_SEARCH_ENTITY_TYPE_SCHEMA = Type.String({
181
+ enum: searchEntityTypeIds,
182
+ description: `Qloo search entity type: ${searchEntityTypeIds.join(", ")}.`
183
+ });
184
+ var QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA = Type.String({
185
+ enum: QLOO_TAG_BOOLEAN_OPERATORS,
186
+ description: "Combine tag values with union (any) or intersection (all)."
187
+ });
188
+ var QLOO_LIMIT_SCHEMA = Type.Optional(Type.Integer({
189
+ minimum: 1,
190
+ maximum: QLOO_TOOL_MAX_RESULTS,
191
+ default: QLOO_TOOL_DEFAULT_LIMIT,
192
+ description: `Maximum results to return (1-${QLOO_TOOL_MAX_RESULTS}).`
193
+ }));
194
+ var QLOO_ENTITY_INPUTS_SCHEMA = Type.Array(Type.String({ minLength: 1 }), {
195
+ minItems: 1,
196
+ maxItems: 10,
197
+ description: "Named entities or Qloo entity UUIDs; use tag fields for abstract concepts."
198
+ });
199
+ var QLOO_TAG_INPUTS_SCHEMA = Type.Array(Type.String({ minLength: 1 }), {
200
+ minItems: 1,
201
+ maxItems: 10,
202
+ description: "Genres, moods, styles, traits, or other concepts, or Qloo tag URNs; not named entities."
203
+ });
204
+ var QLOO_DEMOGRAPHIC_SCHEMA = Type.String({
205
+ minLength: 1,
206
+ description: "Audience demographic in natural language, such as 'young women', 'men 25-34', or 'millennials'. It is converted to documented Qloo demographic signals.",
207
+ examples: ["young women", "men 25-34", "millennials"]
208
+ });
209
+ var QLOO_REQUEST_PREVIEW_SCHEMA = Type.Object({
210
+ method: Type.Literal("GET"),
211
+ path: Type.String({ minLength: 1 }),
212
+ query: Type.Unknown()
213
+ }, { additionalProperties: false });
214
+ var QLOO_RESULT_ENVELOPE_SCHEMA = Type.Object({
215
+ schema_version: Type.Literal(QLOO_TOOL_RESULT_SCHEMA_VERSION),
216
+ operation: Type.String(),
217
+ status: Type.Union([
218
+ Type.Literal("ok"),
219
+ Type.Literal("empty"),
220
+ Type.Literal("needs_input"),
221
+ Type.Literal("partial"),
222
+ Type.Literal("degraded"),
223
+ Type.Literal("error")
224
+ ]),
225
+ summary: Type.Optional(Type.String()),
226
+ interpretation: Type.Optional(Type.Unknown()),
227
+ results: Type.Optional(Type.Unknown()),
228
+ result_count: Type.Optional(Type.Integer({ minimum: 0 })),
229
+ warnings: Type.Optional(Type.Array(Type.String())),
230
+ query_intent: Type.Optional(Type.Unknown()),
231
+ error: Type.Optional(Type.Object({
232
+ code: Type.String(),
233
+ layer: Type.String(),
234
+ retryable: Type.Boolean(),
235
+ recovery: Type.String()
236
+ }, { additionalProperties: false })),
237
+ execution: Type.Object({
238
+ transport: Type.String({ enum: ["direct", "cli", "mcp"] }),
239
+ transport_name: Type.String(),
240
+ correlation_id: Type.String(),
241
+ duration_ms: Type.Number({ minimum: 0 })
242
+ }, { additionalProperties: false }),
243
+ provenance: Type.Optional(Type.Object({
244
+ source: Type.String(),
245
+ endpoint: Type.String(),
246
+ documentation: Type.String(),
247
+ transport: Type.Optional(Type.String()),
248
+ requests: Type.Optional(Type.Array(QLOO_REQUEST_PREVIEW_SCHEMA, {
249
+ maxItems: 20
250
+ }))
251
+ }, { additionalProperties: false }))
252
+ }, { additionalProperties: true });
253
+ var QLOO_WORKFLOW_INPUT_SCHEMAS = {
254
+ recommend: Type.Object({
255
+ target_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
256
+ signals: Type.Optional(QLOO_ENTITY_INPUTS_SCHEMA),
257
+ signal_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
258
+ signal_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
259
+ signal_location: Type.Optional(Type.String({ minLength: 1 })),
260
+ demographic: Type.Optional(QLOO_DEMOGRAPHIC_SCHEMA),
261
+ filter_location: Type.Optional(Type.String({ minLength: 1 })),
262
+ include_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
263
+ include_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
264
+ exclude_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
265
+ exclude_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
266
+ explain: Type.Optional(Type.Boolean({ default: true })),
267
+ limit: QLOO_LIMIT_SCHEMA
268
+ }, { additionalProperties: false }),
269
+ rank: Type.Object({
270
+ options: QLOO_ENTITY_INPUTS_SCHEMA,
271
+ option_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
272
+ signals: Type.Optional(QLOO_ENTITY_INPUTS_SCHEMA),
273
+ signal_location: Type.Optional(Type.String({ minLength: 1 })),
274
+ demographic: Type.Optional(QLOO_DEMOGRAPHIC_SCHEMA),
275
+ include_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
276
+ exclude_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA)
277
+ }, { additionalProperties: false }),
278
+ describe: Type.Object({
279
+ entity: Type.String({ minLength: 1 }),
280
+ type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA)
281
+ }, { additionalProperties: false }),
282
+ where_popular: Type.Object({
283
+ entity: Type.String({ minLength: 1 }),
284
+ entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
285
+ within: Type.String({
286
+ minLength: 1,
287
+ description: "Required geographic area that constrains the heatmap."
288
+ }),
289
+ limit: QLOO_LIMIT_SCHEMA
290
+ }, { additionalProperties: false }),
291
+ compare_audiences: Type.Object({
292
+ group_a: QLOO_ENTITY_INPUTS_SCHEMA,
293
+ group_b: QLOO_ENTITY_INPUTS_SCHEMA,
294
+ target_type: Type.Optional(QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA),
295
+ limit: QLOO_LIMIT_SCHEMA
296
+ }, { additionalProperties: false }),
297
+ entity_tags: Type.Object({
298
+ entities: QLOO_ENTITY_INPUTS_SCHEMA,
299
+ entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
300
+ limit: QLOO_LIMIT_SCHEMA
301
+ }, { additionalProperties: false }),
302
+ audience_demographics: Type.Object({
303
+ entity: Type.String({ minLength: 1 }),
304
+ entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
305
+ limit: QLOO_LIMIT_SCHEMA
306
+ }, { additionalProperties: false }),
307
+ trends: Type.Object({
308
+ entities: Type.Array(Type.String({ minLength: 1 }), { minItems: 1, maxItems: 5 }),
309
+ entity_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
310
+ start_date: Type.String({
311
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
312
+ description: "Inclusive start date in YYYY-MM-DD format."
313
+ }),
314
+ end_date: Type.String({
315
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
316
+ description: "Inclusive end date in YYYY-MM-DD format."
317
+ }),
318
+ limit: QLOO_LIMIT_SCHEMA
319
+ }, { additionalProperties: false }),
320
+ find_tags: Type.Object({
321
+ query: Type.String({ minLength: 1 }),
322
+ semantic: Type.Optional(Type.Boolean({ default: true })),
323
+ limit: QLOO_LIMIT_SCHEMA
324
+ }, { additionalProperties: false })
325
+ };
326
+ var QLOO_WORKFLOW_RESOLUTION_FIELDS = {
327
+ recommend: [
328
+ { field: "signals", inputKind: "entity" },
329
+ {
330
+ field: "signal_tags",
331
+ inputKind: "tag",
332
+ scopeField: "target_type",
333
+ scopeKind: "insights",
334
+ purpose: "signal"
335
+ },
336
+ {
337
+ field: "include_tags",
338
+ inputKind: "tag",
339
+ scopeField: "target_type",
340
+ scopeKind: "insights",
341
+ purpose: "include_filter"
342
+ },
343
+ {
344
+ field: "exclude_tags",
345
+ inputKind: "tag",
346
+ scopeField: "target_type",
347
+ scopeKind: "insights",
348
+ purpose: "exclude_filter"
349
+ }
350
+ ],
351
+ rank: [
352
+ {
353
+ field: "options",
354
+ inputKind: "entity",
355
+ scopeField: "option_type",
356
+ scopeKind: "insights"
357
+ },
358
+ { field: "signals", inputKind: "entity" },
359
+ {
360
+ field: "include_tags",
361
+ inputKind: "tag",
362
+ scopeField: "option_type",
363
+ scopeKind: "insights",
364
+ purpose: "include_filter"
365
+ },
366
+ {
367
+ field: "exclude_tags",
368
+ inputKind: "tag",
369
+ scopeField: "option_type",
370
+ scopeKind: "insights",
371
+ purpose: "exclude_filter"
372
+ }
373
+ ],
374
+ describe: [
375
+ { field: "entity", inputKind: "entity", scopeField: "type", scopeKind: "search" }
376
+ ],
377
+ where_popular: [
378
+ {
379
+ field: "entity",
380
+ inputKind: "entity",
381
+ scopeField: "entity_type",
382
+ scopeKind: "search"
383
+ }
384
+ ],
385
+ compare_audiences: [
386
+ { field: "group_a", inputKind: "entity" },
387
+ { field: "group_b", inputKind: "entity" }
388
+ ],
389
+ entity_tags: [
390
+ {
391
+ field: "entities",
392
+ inputKind: "entity",
393
+ scopeField: "entity_type",
394
+ scopeKind: "search"
395
+ }
396
+ ],
397
+ audience_demographics: [
398
+ {
399
+ field: "entity",
400
+ inputKind: "entity",
401
+ scopeField: "entity_type",
402
+ scopeKind: "search"
403
+ }
404
+ ],
405
+ trends: [
406
+ {
407
+ field: "entities",
408
+ inputKind: "entity",
409
+ scopeField: "entity_type",
410
+ scopeKind: "insights"
411
+ }
412
+ ],
413
+ find_tags: []
414
+ };
415
+ var sharedWorkflowBehavior = Object.freeze({
416
+ readOnly: true,
417
+ idempotent: true,
418
+ mutating: false,
419
+ destructive: false,
420
+ openWorld: true,
421
+ concurrency: "safe_for_independent_inputs",
422
+ requiredCredential: "qloo_api",
423
+ defaultResultBudget: QLOO_TOOL_DEFAULT_LIMIT,
424
+ maximumResultBudget: QLOO_TOOL_MAX_RESULTS,
425
+ safeRetryClasses: Object.freeze(["dns", "connect", "timeout", "rate_limit", "upstream_5xx"]),
426
+ supportedTransports: Object.freeze(["direct", "mcp"])
427
+ });
428
+ var mediumLatencyOperations = /* @__PURE__ */ new Set([
429
+ "recommend",
430
+ "rank",
431
+ "compare_audiences",
432
+ "entity_tags",
433
+ "audience_demographics",
434
+ "trends"
435
+ ]);
436
+ var QLOO_WORKFLOW_BEHAVIORS = Object.freeze(Object.fromEntries(Object.keys(QLOO_WORKFLOW_INPUT_SCHEMAS).map((operation) => [
437
+ operation,
438
+ Object.freeze({
439
+ ...sharedWorkflowBehavior,
440
+ expectedLatency: mediumLatencyOperations.has(operation) ? "medium" : "low"
441
+ })
442
+ ])));
443
+ var QLOO_WORKFLOW_METADATA = {
444
+ recommend: {
445
+ toolName: "qloo_recommend",
446
+ label: "Qloo recommendations",
447
+ description: "Find Qloo entities related to one combined entity, tag, location, and demographic taste profile. Signals influence affinity; filters constrain output.",
448
+ promptSnippet: "Recommend entities from a combined Qloo taste profile",
449
+ promptGuidelines: [
450
+ "Pass every related taste and audience signal together in one qloo_recommend call.",
451
+ "Put named things in signals; put genres, moods, styles, traits, and concepts such as quiet luxury in signal_tags; reuse returned Qloo UUIDs and tag URNs.",
452
+ "Use signal_location for audience location and filter_location to constrain result geography.",
453
+ "Include demographic and location in the same call when both describe the audience."
454
+ ],
455
+ documentation: ["https://docs.qloo.com/reference/insights-api-deep-dive"]
456
+ },
457
+ rank: {
458
+ toolName: "qloo_rank",
459
+ label: "Qloo rank",
460
+ description: "Rank one supplied option set against one shared entity, location, demographic, and tag profile. Scores from separate calls are not comparable.",
461
+ promptSnippet: "Rank a caller-supplied shortlist with Qloo",
462
+ promptGuidelines: [
463
+ "Pass every option in one qloo_rank call so scores remain comparable.",
464
+ "Put named things in entity fields and concepts in tag fields; reuse returned Qloo UUIDs and tag URNs."
465
+ ],
466
+ documentation: ["https://docs.qloo.com/reference/insights-api-deep-dive"]
467
+ },
468
+ describe: {
469
+ toolName: "qloo_describe",
470
+ label: "Qloo entity",
471
+ description: "Resolve one named entity or Qloo UUID and return only metadata present in Qloo.",
472
+ promptSnippet: "Resolve and describe one Qloo entity",
473
+ documentation: ["https://docs.qloo.com/reference/get-search", "https://docs.qloo.com/reference/get-entities"]
474
+ },
475
+ where_popular: {
476
+ toolName: "qloo_where_popular",
477
+ label: "Qloo geographic affinity",
478
+ description: "Find geographic areas where one entity has strong query-relative affinity. This is distinct from filtering recommendations to a place.",
479
+ promptSnippet: "Find where an entity is comparatively popular",
480
+ documentation: ["https://docs.qloo.com/reference/heatmaps-use-case"]
481
+ },
482
+ compare_audiences: {
483
+ toolName: "qloo_compare_audiences",
484
+ label: "Qloo audience comparison",
485
+ description: "Compare two complete groups of entity signals in one Qloo analysis request.",
486
+ promptSnippet: "Compare two Qloo taste-signal groups",
487
+ documentation: ["https://docs.qloo.com/reference/analysis-compare"]
488
+ },
489
+ entity_tags: {
490
+ toolName: "qloo_entity_tags",
491
+ label: "Qloo entity tags",
492
+ description: "Retrieve Qloo concepts that jointly characterize one or more entities using the documented urn:tag Insights workflow.",
493
+ promptSnippet: "Find concepts that characterize Qloo entities",
494
+ documentation: ["https://docs.qloo.com/reference/taste-analysis"]
495
+ },
496
+ audience_demographics: {
497
+ toolName: "qloo_audience_demographics",
498
+ label: "Qloo audience demographics",
499
+ description: "Retrieve aggregate demographic distributions associated with one entity through the documented urn:demographics Insights workflow.",
500
+ promptSnippet: "Inspect aggregate demographics associated with a Qloo entity",
501
+ documentation: ["https://docs.qloo.com/reference/demographics-use-case"]
502
+ },
503
+ trends: {
504
+ toolName: "qloo_trends",
505
+ label: "Qloo trends",
506
+ description: "Retrieve Qloo time-series popularity metrics for one to five resolved entities over an ISO date range.",
507
+ promptSnippet: "Track Qloo entity popularity metrics over time",
508
+ documentation: ["https://docs.qloo.com/reference/get-trending"]
509
+ },
510
+ find_tags: {
511
+ toolName: "qloo_find_tags",
512
+ label: "Qloo tag search",
513
+ description: "Search Qloo's tag ontology by natural-language concept and return stable tag URNs.",
514
+ promptSnippet: "Search the Qloo tag ontology",
515
+ documentation: ["https://docs.qloo.com/reference/get-tags-1"]
516
+ }
517
+ };
518
+ var QLOO_WORKFLOW_CONTRACTS = Object.freeze(Object.fromEntries(Object.entries(QLOO_WORKFLOW_METADATA).map(([id, metadata]) => [
519
+ id,
520
+ Object.freeze({
521
+ id,
522
+ ...metadata,
523
+ behavior: QLOO_WORKFLOW_BEHAVIORS[id],
524
+ inputSchema: QLOO_WORKFLOW_INPUT_SCHEMAS[id],
525
+ resultSchema: QLOO_RESULT_ENVELOPE_SCHEMA
526
+ })
527
+ ])));
528
+
529
+ // packages/qloo-tool-schema/dist/src/resolution-contracts.js
530
+ import { Type as Type2 } from "typebox";
531
+ var QLOO_RESOLUTION_CONTRACT_VERSION = "1.0-preview.1";
532
+ var QLOO_RESOLUTION_PROVIDER_DESCRIPTOR_SCHEMA = Type2.Object({
533
+ contract_version: Type2.Literal(QLOO_RESOLUTION_CONTRACT_VERSION),
534
+ provider_id: Type2.String({ minLength: 1 }),
535
+ name: Type2.String({ minLength: 1 }),
536
+ entity_strategy: Type2.String({ minLength: 1 }),
537
+ tag_strategy: Type2.String({ minLength: 1 }),
538
+ remote: Type2.Boolean(),
539
+ uses_model: Type2.Boolean(),
540
+ fallback: Type2.Literal("none")
541
+ }, { additionalProperties: false });
542
+ var QLOO_RESOLUTION_CANDIDATE_SCHEMA = Type2.Object({
543
+ id: Type2.String({ minLength: 1 }),
544
+ name: Type2.String({ minLength: 1 }),
545
+ type: Type2.Optional(Type2.String({ minLength: 1 })),
546
+ rank: Type2.Optional(Type2.Integer({ minimum: 1 })),
547
+ score: Type2.Optional(Type2.Number()),
548
+ popularity: Type2.Optional(Type2.Number()),
549
+ parent_types: Type2.Optional(Type2.Array(Type2.String({ minLength: 1 }), {
550
+ maxItems: 20,
551
+ uniqueItems: true
552
+ })),
553
+ description: Type2.Optional(Type2.String({ minLength: 1, maxLength: 500 })),
554
+ release_year: Type2.Optional(Type2.Integer()),
555
+ address: Type2.Optional(Type2.Unknown())
556
+ }, { additionalProperties: false });
557
+ var QLOO_RESOLUTION_PROVENANCE_SCHEMA = Type2.Object({
558
+ contract_version: Type2.Literal(QLOO_RESOLUTION_CONTRACT_VERSION),
559
+ provider_id: Type2.String({ minLength: 1 }),
560
+ provider_name: Type2.String({ minLength: 1 }),
561
+ endpoint: Type2.String({ minLength: 1 }),
562
+ strategy: Type2.String({ minLength: 1 }),
563
+ remote: Type2.Boolean(),
564
+ uses_model: Type2.Boolean(),
565
+ provider_version: Type2.Optional(Type2.String({ minLength: 1 })),
566
+ cache_status: Type2.Optional(Type2.String({ minLength: 1 }))
567
+ }, { additionalProperties: false });
568
+ var QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA = {
569
+ input: Type2.String({ minLength: 1 }),
570
+ provenance: QLOO_RESOLUTION_PROVENANCE_SCHEMA,
571
+ warnings: Type2.Optional(Type2.Array(Type2.String()))
572
+ };
573
+ var QLOO_RESOLUTION_OUTCOME_SCHEMA = Type2.Union([
574
+ Type2.Object({
575
+ ...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
576
+ status: Type2.Literal("resolved"),
577
+ match: Type2.Union([
578
+ Type2.Literal("identifier"),
579
+ Type2.Literal("exact"),
580
+ Type2.Literal("semantic")
581
+ ]),
582
+ selected: QLOO_RESOLUTION_CANDIDATE_SCHEMA,
583
+ alternatives: Type2.Optional(Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA))
584
+ }, { additionalProperties: false }),
585
+ Type2.Object({
586
+ ...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
587
+ status: Type2.Literal("ambiguous"),
588
+ candidates: Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA)
589
+ }, { additionalProperties: false }),
590
+ Type2.Object({
591
+ ...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
592
+ status: Type2.Literal("not_found"),
593
+ candidates: Type2.Optional(Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA))
594
+ }, { additionalProperties: false })
595
+ ]);
596
+
4
597
  // apps/qloo-harness/dist/qloo-presentation.js
5
598
  var OPERATION_LABELS = {
6
599
  recommend: "recommendations",
@@ -284,21 +877,40 @@ function parseQlooRawPage(argument, currentPage) {
284
877
  return Number(value);
285
878
  return void 0;
286
879
  }
287
- var RESOLUTION_INPUT_FIELDS = {
288
- qloo_recommend: ["signals", "signal_tags", "include_tags", "exclude_tags"],
289
- qloo_rank: ["options", "signals", "include_tags", "exclude_tags"],
290
- qloo_describe: ["entity"],
291
- qloo_where_popular: ["entity"],
292
- qloo_compare_audiences: ["group_a", "group_b"],
293
- qloo_entity_tags: ["entities"],
294
- qloo_audience_demographics: ["entities"],
295
- qloo_trends: ["entities"]
296
- };
297
- function resolutionCandidates(detailsValue) {
880
+ var resolutionFieldsByToolName = new Map(Object.keys(QLOO_WORKFLOW_CONTRACTS).map((operation) => [
881
+ QLOO_WORKFLOW_CONTRACTS[operation].toolName,
882
+ QLOO_WORKFLOW_RESOLUTION_FIELDS[operation]
883
+ ]));
884
+ function canonicalResolutionScope(field, input) {
885
+ if (!field.scopeField || !field.scopeKind)
886
+ return void 0;
887
+ const value = input[field.scopeField];
888
+ if (typeof value !== "string" || value.trim().length === 0)
889
+ return void 0;
890
+ return field.scopeKind === "insights" ? resolveQlooInsightsEntityType(value) ?? safeText(value) : resolveQlooSearchEntityType(value) ?? safeText(value);
891
+ }
892
+ function fieldContainsInput(input, field, issueInput) {
893
+ const value = input[field];
894
+ return typeof value === "string" ? value === issueInput : Array.isArray(value) && value.some((item) => item === issueInput);
895
+ }
896
+ function resolutionInputContexts(toolName, input) {
897
+ return (resolutionFieldsByToolName.get(toolName) ?? []).map((field) => {
898
+ const scope = canonicalResolutionScope(field, input);
899
+ return {
900
+ field: field.field,
901
+ inputKind: field.inputKind,
902
+ ...scope ? { scope } : {},
903
+ ...field.purpose ? { purpose: field.purpose } : {}
904
+ };
905
+ });
906
+ }
907
+ function resolutionCandidates(detailsValue, toolName, toolInput = {}) {
298
908
  const details = asRecord(detailsValue);
299
909
  if (details?.status !== "needs_input")
300
910
  return [];
301
911
  const resolution = asRecord(details.resolution);
912
+ const fieldDefinitions = toolName ? resolutionFieldsByToolName.get(toolName) ?? [] : [];
913
+ const usedFields = /* @__PURE__ */ new Map();
302
914
  return asArray(resolution?.issues).slice(0, 5).flatMap((value) => {
303
915
  const issue = asRecord(value);
304
916
  const input = safeText(issue?.input);
@@ -310,17 +922,41 @@ function resolutionCandidates(detailsValue) {
310
922
  seenIds.add(id);
311
923
  return true;
312
924
  }).slice(0, 5);
313
- return input && candidates.length > 0 ? [{ input, candidates }] : [];
925
+ if (!input || candidates.length === 0)
926
+ return [];
927
+ const explicitField = safeText(issue?.field);
928
+ const explicitKind = issue?.input_kind === "entity" || issue?.input_kind === "tag" ? issue.input_kind : void 0;
929
+ const matchingFields = fieldDefinitions.filter((field) => fieldContainsInput(toolInput, field.field, input));
930
+ const used = usedFields.get(input) ?? /* @__PURE__ */ new Set();
931
+ const inferredField = matchingFields.find((field) => field.field === explicitField && (!explicitKind || field.inputKind === explicitKind)) ?? matchingFields.find((field) => !used.has(field.field));
932
+ if (inferredField) {
933
+ used.add(inferredField.field);
934
+ usedFields.set(input, used);
935
+ }
936
+ const scope = safeText(issue?.scope) || (inferredField ? canonicalResolutionScope(inferredField, toolInput) : void 0);
937
+ const purpose = issue?.purpose === "signal" || issue?.purpose === "include_filter" || issue?.purpose === "exclude_filter" ? issue.purpose : inferredField?.purpose;
938
+ return [{
939
+ input,
940
+ candidates,
941
+ ...inferredField ? { field: inferredField.field } : explicitField ? { field: explicitField } : {},
942
+ ...inferredField ? { inputKind: inferredField.inputKind } : explicitKind ? { inputKind: explicitKind } : {},
943
+ ...scope ? { scope } : {},
944
+ ...purpose ? { purpose } : {}
945
+ }];
314
946
  });
315
947
  }
316
948
  function applyResolutionChoices(toolName, input, choices) {
317
- const fields = RESOLUTION_INPUT_FIELDS[toolName];
949
+ const fields = resolutionFieldsByToolName.get(toolName);
318
950
  if (!fields || choices.length === 0)
319
951
  return void 0;
320
- const byInput = new Map(choices.map((choice) => [choice.input, choice.selectedId]));
321
952
  let changed = false;
322
953
  const patched = { ...input };
323
- for (const field of fields) {
954
+ for (const fieldDefinition of fields) {
955
+ const field = fieldDefinition.field;
956
+ const fieldChoices = choices.filter((choice) => (!choice.field || choice.field === field) && (!choice.inputKind || choice.inputKind === fieldDefinition.inputKind));
957
+ if (fieldChoices.length === 0)
958
+ continue;
959
+ const byInput = new Map(fieldChoices.map((choice) => [choice.input, choice.selectedId]));
324
960
  const value = input[field];
325
961
  if (typeof value === "string") {
326
962
  const replacement = byInput.get(value);
@@ -354,5 +990,6 @@ export {
354
990
  parseQlooRawPage,
355
991
  renderQlooCallLines,
356
992
  renderQlooResultLines,
357
- resolutionCandidates
993
+ resolutionCandidates,
994
+ resolutionInputContexts
358
995
  };