@ainyc/canonry 4.188.1 → 4.188.3

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 (29) hide show
  1. package/assets/assets/{AuditHistoryPanel-BCpFULQp.js → AuditHistoryPanel-C58OHinE.js} +1 -1
  2. package/assets/assets/{BacklinksPage-CFHiZFhZ.js → BacklinksPage-BXHARmkv.js} +1 -1
  3. package/assets/assets/{HistoryPage-THnDCEnv.js → HistoryPage-DGHy5AST.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-DWWwA2kL.js → MeasurementPropertyPage-Dhzw05Kj.js} +1 -1
  5. package/assets/assets/ProjectPage-BkXYHYxW.js +9 -0
  6. package/assets/assets/{RunRow-CDWArf9X.js → RunRow-DWVXPbMq.js} +1 -1
  7. package/assets/assets/{RunsPage-Ctsw14Vg.js → RunsPage-Bs-wYMIK.js} +1 -1
  8. package/assets/assets/{SettingsPage-IHYGgD0e.js → SettingsPage-wP-BxKXV.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-DjFHZqBF.js → SiteHealthSection-weOW2a99.js} +3 -3
  10. package/assets/assets/{TrafficPage-Cb1Zwgc5.js → TrafficPage-BKPq62Do.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-CRdjdiAa.js → TrafficSourceDetailPage-BfzMTwYB.js} +1 -1
  12. package/assets/assets/{extract-error-message-CNs6e1oz.js → extract-error-message-BoqECTcn.js} +1 -1
  13. package/assets/assets/index-KTeRanN1.css +1 -0
  14. package/assets/assets/{index-B64f9mVI.js → index-gmvpT868.js} +32 -32
  15. package/assets/assets/{react-sigma_core.esm.min-CzJ-HG1j.js → react-sigma_core.esm.min-Gf0WvNYq.js} +1 -1
  16. package/assets/assets/{vendor-lucide-BovNTc41.js → vendor-lucide-dNHw1I5G.js} +1 -1
  17. package/assets/index.html +3 -3
  18. package/dist/{chunk-HIIOVQSH.js → chunk-3HZELBZZ.js} +73 -14
  19. package/dist/{chunk-6YJQGOR6.js → chunk-7NMMQ7QO.js} +4 -4
  20. package/dist/{chunk-GPTEXYYL.js → chunk-AC3ZIRHR.js} +89 -35
  21. package/dist/{chunk-QPYMAXLJ.js → chunk-OXYM6VCJ.js} +1 -1
  22. package/dist/{chunk-A47HAZDJ.js → chunk-VJRQTKUB.js} +2 -2
  23. package/dist/cli.js +5 -5
  24. package/dist/index.js +4 -4
  25. package/dist/{intelligence-service-4KMWHEYP.js → intelligence-service-GMG2S55U.js} +2 -2
  26. package/dist/mcp.js +3 -3
  27. package/package.json +12 -12
  28. package/assets/assets/ProjectPage-CP57_Lqf.js +0 -9
  29. package/assets/assets/index-DTblya6d.css +0 -1
@@ -524,6 +524,7 @@ import {
524
524
  parseStoredMeasurementPlanAnyVersion,
525
525
  parseWindow,
526
526
  pickClusterRepresentative,
527
+ prepareBrandMatchText,
527
528
  projectConfigSchema,
528
529
  projectCreateRequestSchema,
529
530
  projectDtoSchema,
@@ -670,7 +671,7 @@ import {
670
671
  wordpressSchemaDeployResultDtoSchema,
671
672
  wordpressSchemaStatusResultDtoSchema,
672
673
  wordpressStatusDtoSchema
673
- } from "./chunk-HIIOVQSH.js";
674
+ } from "./chunk-3HZELBZZ.js";
674
675
 
675
676
  // src/intelligence-service.ts
676
677
  import { eq as eq67, desc as desc29, asc as asc12, and as and56, ne as ne10, or as or14, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql26, exists } from "drizzle-orm";
@@ -8486,7 +8487,7 @@ function compileCompetitiveSignalResolver(competitorDomains) {
8486
8487
  ...evidence.citedDomains ?? [],
8487
8488
  ...(evidence.groundingSources ?? []).map((source) => source.uri)
8488
8489
  ];
8489
- const answerDomains = extractDomainsFromText(evidence.answerText);
8490
+ const answerDomains = evidence.answerDomains ?? extractDomainsFromText(evidence.answerText);
8490
8491
  const mentionedBrandKeys = matchedAliasKeys(domainBrandMatcher, evidence.answerText);
8491
8492
  const citedCompetitorDomains = [];
8492
8493
  const mentionedCompetitorDomains = [];
@@ -13091,11 +13092,16 @@ function buildMentionShareInputs(opts) {
13091
13092
  competitors: mentionShareCompetitorsFromDomains(opts.competitorDomains),
13092
13093
  snapshots: opts.snapshots.map((snap) => {
13093
13094
  const queryText = (snap.queryId ? opts.queryTextById?.get(snap.queryId) : void 0) ?? snap.queryText ?? null;
13095
+ const cachedAnswerDomains = !snap.answerText || !opts.answerDomainsByText ? void 0 : opts.answerDomainsByText.get(snap.answerText) ?? (() => {
13096
+ const domains = extractDomainsFromText(snap.answerText);
13097
+ opts.answerDomainsByText.set(snap.answerText, domains);
13098
+ return domains;
13099
+ })();
13094
13100
  return {
13095
13101
  // Mention share is a current-identity metric. Recompute stored answer
13096
13102
  // text after an alias/domain rename; only text-less legacy rows need
13097
13103
  // the persisted run-time boolean as a fallback.
13098
- projectMentioned: snap.answerText ? determineAnswerMentioned(snap.answerText, projectBrandNames, projectDomains) : snap.answerMentioned === true,
13104
+ projectMentioned: snap.answerText ? determineAnswerMentioned(snap.answerText, projectBrandNames, projectDomains, cachedAnswerDomains) : snap.answerMentioned === true,
13099
13105
  answerText: snap.answerText,
13100
13106
  queryClass: classify ? classify(queryText) : null
13101
13107
  };
@@ -13173,32 +13179,41 @@ function parseSourceUrl(value) {
13173
13179
  function ownedBy(host, roots) {
13174
13180
  return roots.some((root) => host === root || host.endsWith(`.${root}`));
13175
13181
  }
13176
- function routeClaim(source, target, url) {
13177
- if (source.host !== normalizedHost(url.host)) return null;
13178
- if (url.mode === "host") return { targetId: target.id, urlId: url.id, modeRank: 1, pathLength: 0 };
13179
- const configuredPath = normalizedPath(url.path);
13182
+ function compiledTargetRoutes(targets) {
13183
+ const routes = /* @__PURE__ */ new Map();
13184
+ for (const target of targets) {
13185
+ for (const url of target.urls) {
13186
+ const host = normalizedHost(url.host);
13187
+ const path8 = url.mode === "host" ? "" : normalizedPath(url.path);
13188
+ const rows = routes.get(host) ?? [];
13189
+ rows.push({ targetId: target.id, url, path: url.pathCase === "insensitive" ? path8.toLocaleLowerCase("en") : path8 });
13190
+ routes.set(host, rows);
13191
+ }
13192
+ }
13193
+ return routes;
13194
+ }
13195
+ function routeClaim(source, route) {
13196
+ const { targetId, url, path: matcherPath } = route;
13197
+ if (url.mode === "host") return { targetId, urlId: url.id, modeRank: 1, pathLength: 0 };
13180
13198
  const sourcePath = url.pathCase === "insensitive" ? source.path.toLocaleLowerCase("en") : source.path;
13181
- const matcherPath = url.pathCase === "insensitive" ? configuredPath.toLocaleLowerCase("en") : configuredPath;
13182
13199
  const matches = url.mode === "exact" ? sourcePath === matcherPath : matcherPath === "/" || sourcePath === matcherPath || sourcePath.startsWith(`${matcherPath}/`);
13183
13200
  if (!matches) return null;
13184
13201
  return {
13185
- targetId: target.id,
13202
+ targetId,
13186
13203
  urlId: url.id,
13187
13204
  modeRank: url.mode === "exact" ? 3 : 2,
13188
13205
  pathLength: matcherPath.length
13189
13206
  };
13190
13207
  }
13191
- function classifySourceAttribution(value, targets, normalizedOwnedHosts) {
13208
+ function classifySourceAttribution(value, routes, normalizedOwnedHosts) {
13192
13209
  const source = parseSourceUrl(value);
13193
13210
  if (!source) {
13194
13211
  return { classification: "invalid", normalizedUrl: null, matchedTargetIds: [], matchedUrlIds: [] };
13195
13212
  }
13196
13213
  const claims = [];
13197
- for (const target of targets) {
13198
- for (const url of target.urls) {
13199
- const claim = routeClaim(source, target, url);
13200
- if (claim) claims.push(claim);
13201
- }
13214
+ for (const route of routes.get(source.host) ?? []) {
13215
+ const claim = routeClaim(source, route);
13216
+ if (claim) claims.push(claim);
13202
13217
  }
13203
13218
  claims.sort((left, right) => right.modeRank - left.modeRank || right.pathLength - left.pathLength || compareText2(left.targetId, right.targetId) || compareText2(left.urlId, right.urlId));
13204
13219
  const best = claims.at(0);
@@ -13268,12 +13283,22 @@ function containsAnyAlias(answerText, aliases) {
13268
13283
  function compiledMentionAliases(targets) {
13269
13284
  return targets.flatMap((target) => target.aliases.map((alias2) => ({ targetId: target.id, words: words(alias2) }))).filter((alias2) => alias2.words.length > 0);
13270
13285
  }
13286
+ function indexMentionAliases(aliases) {
13287
+ const byFirstWord = /* @__PURE__ */ new Map();
13288
+ for (const alias2 of aliases) {
13289
+ const firstWord = alias2.words[0];
13290
+ const rows = byFirstWord.get(firstWord) ?? [];
13291
+ rows.push(alias2);
13292
+ byFirstWord.set(firstWord, rows);
13293
+ }
13294
+ return byFirstWord;
13295
+ }
13271
13296
  function mentionedTargetsForAliases(answerText, aliases) {
13272
13297
  const result = /* @__PURE__ */ new Set();
13273
13298
  if (answerText === null) return result;
13274
13299
  const textWords = words(answerText);
13275
13300
  for (let start = 0; start < textWords.length; ) {
13276
- const matches = aliases.filter((alias2) => aliasMatchesAt(textWords, alias2.words, start));
13301
+ const matches = (aliases.get(textWords[start]) ?? []).filter((alias2) => aliasMatchesAt(textWords, alias2.words, start));
13277
13302
  if (matches.length === 0) {
13278
13303
  start++;
13279
13304
  continue;
@@ -13286,7 +13311,7 @@ function mentionedTargetsForAliases(answerText, aliases) {
13286
13311
  return result;
13287
13312
  }
13288
13313
  function mentionedTargets(answerText, targets) {
13289
- return mentionedTargetsForAliases(answerText, compiledMentionAliases(targets));
13314
+ return mentionedTargetsForAliases(answerText, indexMentionAliases(compiledMentionAliases(targets)));
13290
13315
  }
13291
13316
  function targetMentionedInAnswer(answerText, targetId, targets) {
13292
13317
  if (answerText === null) return null;
@@ -13312,6 +13337,7 @@ function prepareReport(input, options = {}) {
13312
13337
  const bridged = /* @__PURE__ */ new Set();
13313
13338
  const candidates = /* @__PURE__ */ new Map();
13314
13339
  const mentionAliases = compiledMentionAliases(input.targets);
13340
+ const aliasesByFirstWord = indexMentionAliases(mentionAliases);
13315
13341
  for (const observation of input.observations) {
13316
13342
  const slots = observation.executionId !== null ? input.expectedSlots.filter((slot2) => slot2.executionId === observation.executionId && slot2.provider === observation.provider) : input.expectedSlots.filter((slot2) => slot2.queryText === observation.queryText && slot2.provider === observation.provider && normalizeMeasurementLocation(slot2.location) === normalizeMeasurementLocation(observation.location));
13317
13343
  if (slots.length === 0) {
@@ -13350,7 +13376,7 @@ function prepareReport(input, options = {}) {
13350
13376
  historical: source.historical,
13351
13377
  sourceComplete: source.complete,
13352
13378
  sourceUrls: source.urls,
13353
- mentionedTargetIds: mentionedTargetsForAliases(observation.answerText, mentionAliases)
13379
+ mentionedTargetIds: mentionedTargetsForAliases(observation.answerText, aliasesByFirstWord)
13354
13380
  });
13355
13381
  }
13356
13382
  const edgesByExecution = /* @__PURE__ */ new Map();
@@ -13361,11 +13387,12 @@ function prepareReport(input, options = {}) {
13361
13387
  }
13362
13388
  const answers = [];
13363
13389
  const normalizedOwnedHosts = input.ownedHosts.map(normalizedHost);
13390
+ const targetRoutes = compiledTargetRoutes(input.targets);
13364
13391
  const sourceAttributions = /* @__PURE__ */ new Map();
13365
13392
  const sourceAttribution = (sourceUrl) => {
13366
13393
  const cached = sourceAttributions.get(sourceUrl);
13367
13394
  if (cached !== void 0) return cached;
13368
- const resolved = classifySourceAttribution(sourceUrl, input.targets, normalizedOwnedHosts);
13395
+ const resolved = classifySourceAttribution(sourceUrl, targetRoutes, normalizedOwnedHosts);
13369
13396
  sourceAttributions.set(sourceUrl, resolved);
13370
13397
  return resolved;
13371
13398
  };
@@ -17502,6 +17529,7 @@ function compileMeasurementDraft(authoring, context) {
17502
17529
  compiledGroups.push({
17503
17530
  stableKey: group.stableKey,
17504
17531
  label: group.label,
17532
+ ...group.parentGroupKey === void 0 ? {} : { parentGroupKey: group.parentGroupKey },
17505
17533
  targetKeys: canonicalStrings(group.targetKeys),
17506
17534
  competitors: [...group.competitors].map((competitor) => ({ ...competitor, aliases: canonicalStrings(competitor.aliases) })).sort((left, right) => compareText7(left.stableKey, right.stableKey))
17507
17535
  });
@@ -17793,12 +17821,13 @@ function plansAreLabelOnlyVariants(active, candidate) {
17793
17821
  const doc = canonicalMeasurementPlanV2(plan);
17794
17822
  const stripped = {
17795
17823
  ...doc,
17796
- // compiledChecksum is DERIVED over the full document, labels included,
17797
- // so keeping it would smuggle the stripped labels back into the
17798
- // comparison. Everything else in the doc is semantic and stays.
17824
+ // The checksum includes labels and navigation parents. Neutralize it
17825
+ // with those display fields; all measurement semantics stay in the comparison.
17799
17826
  compiledChecksum: "",
17800
17827
  targets: doc.targets.map((target) => ({ ...target, label: "" })),
17801
- groups: doc.groups.map((group) => ({
17828
+ groups: doc.groups.map(({ parentGroupKey: _parentGroupKey, ...group }) => ({
17829
+ // Parentage changes navigation only. It changes the frozen document and
17830
+ // checksum, but never an execution, target population, or metric.
17802
17831
  ...group,
17803
17832
  label: "",
17804
17833
  competitors: group.competitors.map((competitor) => ({ ...competitor, label: "" }))
@@ -18207,6 +18236,7 @@ function upsertGroup(authoring, body) {
18207
18236
  const next = {
18208
18237
  stableKey: group.stableKey,
18209
18238
  label: group.label,
18239
+ ...group.parentGroupKey === void 0 ? index2 === -1 || groups[index2].parentGroupKey === void 0 ? {} : { parentGroupKey: groups[index2].parentGroupKey } : group.parentGroupKey === null ? {} : { parentGroupKey: group.parentGroupKey },
18210
18240
  targetKeys: unique(group.targetKeys),
18211
18241
  competitors: group.competitors === void 0 ? index2 === -1 ? [] : groups[index2].competitors : group.competitors
18212
18242
  };
@@ -18221,6 +18251,10 @@ function upsertGroup(authoring, body) {
18221
18251
  }
18222
18252
  function removeGroup(authoring, body) {
18223
18253
  const { groupKey } = parseBody(measurementDraftRemoveGroupRequestSchema, body, "remove-group");
18254
+ const children = authoring.groups.filter((group) => group.parentGroupKey === groupKey);
18255
+ if (children.length) {
18256
+ throw validationError("Cannot remove group " + groupKey + " while it is the explicit parent of: " + children.map((group) => group.stableKey).join(", ") + ". Reparent or remove its child groups first.", { displayToOperator: true });
18257
+ }
18224
18258
  return {
18225
18259
  authoring: { ...authoring, groups: authoring.groups.filter((group) => group.stableKey !== groupKey) },
18226
18260
  warnings: []
@@ -19135,6 +19169,7 @@ function seedAuthoring(project, active, context, opts) {
19135
19169
  groups: active.groups.map((group) => ({
19136
19170
  stableKey: group.stableKey,
19137
19171
  label: group.label,
19172
+ ...group.parentGroupKey === void 0 ? {} : { parentGroupKey: group.parentGroupKey },
19138
19173
  targetKeys: [...group.targetKeys],
19139
19174
  competitors: group.competitors.map((competitor) => ({ ...competitor }))
19140
19175
  })),
@@ -22238,8 +22273,9 @@ function breakdown(candidates, definition, populationTargetKeys = selectedTarget
22238
22273
  ...metrics
22239
22274
  };
22240
22275
  }).sort((left, right) => compareText14(left.label, right.label) || compareText14(left.id, right.id));
22241
- const groups = definition.groups.map((group) => {
22242
- const groupTargetKeys = new Set(group.targetKeys);
22276
+ const populationTargetSet = new Set(populationTargetKeys);
22277
+ const groups = definition.groups.map((group) => ({ group, targetKeys: group.targetKeys.filter((targetKey) => populationTargetSet.has(targetKey)) })).filter(({ targetKeys }) => targetKeys.length > 0).map(({ group, targetKeys }) => {
22278
+ const groupTargetKeys = new Set(targetKeys);
22243
22279
  const own = candidates.map((candidate) => narrowedToTargetKeys(candidate, groupTargetKeys)).filter((candidate) => candidate !== null);
22244
22280
  const metrics = rate(own.map((candidate) => targetValues(candidate, targets).mention), own.length);
22245
22281
  const citations = rate(own.map((candidate) => targetValues(candidate, targets).citation), own.length);
@@ -22250,7 +22286,7 @@ function breakdown(candidates, definition, populationTargetKeys = selectedTarget
22250
22286
  mentionCoverage: metrics,
22251
22287
  citationCoverage: citations
22252
22288
  };
22253
- }).filter((group) => group.queryCount > 0).sort((left, right) => compareText14(left.label, right.label) || compareText14(left.id, right.id));
22289
+ }).sort((left, right) => compareText14(left.label, right.label) || compareText14(left.id, right.id));
22254
22290
  return { properties, groups };
22255
22291
  }
22256
22292
  function fingerprint(value) {
@@ -22533,8 +22569,8 @@ function frozenCompetitorMatchers(competitors2) {
22533
22569
  function exactCompetitorMatchers(plan) {
22534
22570
  return frozenCompetitorMatchers(plan.groups.flatMap((group) => group.competitors));
22535
22571
  }
22536
- function competitorSignals(snapshot, matchers) {
22537
- const mentioned = snapshot.answerText === null ? [] : [...matchers].filter(([, matcher]) => matcherMatchesText(matcher, snapshot.answerText)).map(([domain]) => domain);
22572
+ function competitorSignals(snapshot, matchers, preparedAnswerText = prepareBrandMatchText(snapshot.answerText)) {
22573
+ const mentioned = preparedAnswerText === null ? [] : [...matchers].filter(([, matcher]) => matcherMatchesText(matcher, preparedAnswerText)).map(([domain]) => domain);
22538
22574
  const citedDomains = new Set(snapshot.citedDomains.map((value) => {
22539
22575
  try {
22540
22576
  return normalizeMeasurementHost(value);
@@ -22611,16 +22647,20 @@ function v2Definition(plan, revision, manifest, run) {
22611
22647
  location: slot.context?.label ?? null
22612
22648
  };
22613
22649
  });
22650
+ const groupKeysForTarget = new Map(plan.targets.map((target) => [target.stableKey, []]));
22651
+ for (const group of plan.groups) {
22652
+ for (const targetKey of group.targetKeys) groupKeysForTarget.get(targetKey)?.push(group.stableKey);
22653
+ }
22614
22654
  const scopeOptions = [
22615
22655
  { id: "project", label: "Project", kind: "project", targetCount: plan.targets.length },
22616
- ...plan.groups.map((group) => ({ id: group.stableKey, label: group.label, kind: "group", targetCount: group.targetKeys.length })),
22656
+ ...plan.groups.map((group) => ({ id: group.stableKey, label: group.label, kind: "group", targetCount: group.targetKeys.length, ...group.parentGroupKey === void 0 ? {} : { parentGroupIds: [group.parentGroupKey] } })),
22617
22657
  ...plan.reportingScopes?.map((market) => ({
22618
22658
  id: market.stableKey,
22619
22659
  label: market.label,
22620
22660
  kind: "market",
22621
22661
  targetCount: new Set(market.usageEdges.map((edge) => edge.targetKey)).size
22622
22662
  })) ?? [],
22623
- ...plan.targets.map((target) => ({ id: target.stableKey, label: target.label, kind: "property", targetCount: 1 }))
22663
+ ...plan.targets.map((target) => ({ id: target.stableKey, label: target.label, kind: "property", targetCount: 1, parentGroupIds: [...new Set(groupKeysForTarget.get(target.stableKey) ?? [])].sort() }))
22624
22664
  ];
22625
22665
  return {
22626
22666
  revision,
@@ -22771,8 +22811,9 @@ function frozenSimpleRun(run, definition, snapshots) {
22771
22811
  }
22772
22812
  const slotId = `slot:simple:${query.queryId}:${provider}`;
22773
22813
  if (!slotKeys.has(slotId)) throw new Error(`Frozen simple definition has no slot for stored snapshot ${snapshot.id}`);
22774
- const mentioned = snapshot.answerText !== null ? matcherMatchesText(matcher, snapshot.answerText) : snapshot.answerMentioned === true;
22775
- const competitorSignalsForSnapshot = competitors2 === null ? { mentioned: [], cited: [] } : competitorSignals(snapshot, competitors2);
22814
+ const preparedAnswerText = prepareBrandMatchText(snapshot.answerText);
22815
+ const mentioned = snapshot.answerText !== null ? matcherMatchesText(matcher, preparedAnswerText) : snapshot.answerMentioned === true;
22816
+ const competitorSignalsForSnapshot = competitors2 === null ? { mentioned: [], cited: [] } : competitorSignals(snapshot, competitors2, preparedAnswerText);
22776
22817
  observations.push({
22777
22818
  slotId,
22778
22819
  answerId: snapshot.id,
@@ -23618,7 +23659,12 @@ function workspaceDto(db, state, opts) {
23618
23659
  active: activeDto2(state.active),
23619
23660
  defaultContexts: defaultContexts(state.project, opts, state.mode),
23620
23661
  targets: plan?.targets.map((target) => ({ stableKey: target.stableKey, label: target.label })) ?? [],
23621
- groups: plan?.groups.map((group) => ({ stableKey: group.stableKey, label: group.label, targetKeys: group.targetKeys })) ?? [],
23662
+ groups: plan?.groups.map((group) => ({
23663
+ stableKey: group.stableKey,
23664
+ label: group.label,
23665
+ targetKeys: group.targetKeys,
23666
+ ...group.parentGroupKey ? { parentGroupKey: group.parentGroupKey } : {}
23667
+ })) ?? [],
23622
23668
  markets: plan?.reportingScopes?.map((scope) => ({ stableKey: scope.stableKey, label: scope.label, usageEdges: scope.usageEdges })) ?? [],
23623
23669
  tracked: trackedRows(db, state, rows, plan, opts),
23624
23670
  savedSources: savedSources(db, state.project.id)
@@ -33556,11 +33602,18 @@ async function compositeRoutes(app) {
33556
33602
  }
33557
33603
  }
33558
33604
  const configuredApiProviders = project.providers.filter((p) => !p.startsWith("cdp:"));
33605
+ const answerDomainsByText = /* @__PURE__ */ new Map();
33606
+ for (const snapshot of trackedLatest) {
33607
+ if (snapshot.answerText && !answerDomainsByText.has(snapshot.answerText)) {
33608
+ answerDomainsByText.set(snapshot.answerText, extractDomainsFromText(snapshot.answerText));
33609
+ }
33610
+ }
33559
33611
  const mentionShareInputs = buildMentionShareInputs({
33560
33612
  project,
33561
33613
  competitorDomains: competitorRows2.map((c) => c.domain),
33562
33614
  snapshots: trackedLatest,
33563
- queryTextById: queryLookup.byId
33615
+ queryTextById: queryLookup.byId,
33616
+ answerDomainsByText
33564
33617
  });
33565
33618
  const competitiveSignalResolver = compileCompetitiveSignalResolver(
33566
33619
  competitorRows2.map((c) => c.domain)
@@ -33570,7 +33623,8 @@ async function compositeRoutes(app) {
33570
33623
  ...competitiveSignalResolver.resolve({
33571
33624
  citedDomains: snapshot.citedDomains,
33572
33625
  groundingSources: snapshot.groundingSources,
33573
- answerText: snapshot.answerText
33626
+ answerText: snapshot.answerText,
33627
+ answerDomains: snapshot.answerText ? answerDomainsByText.get(snapshot.answerText) : void 0
33574
33628
  })
33575
33629
  }));
33576
33630
  const scores = {
@@ -99,7 +99,7 @@ import {
99
99
  trafficEventKindSchema,
100
100
  trafficSeriesGranularitySchema,
101
101
  visibilityReportRequestSchema
102
- } from "./chunk-HIIOVQSH.js";
102
+ } from "./chunk-3HZELBZZ.js";
103
103
 
104
104
  // src/cli-error.ts
105
105
  function isMachineFormat(format) {
@@ -5,11 +5,11 @@ import {
5
5
  installSkills,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-QPYMAXLJ.js";
8
+ } from "./chunk-OXYM6VCJ.js";
9
9
  import {
10
10
  SKILL_MANIFEST_FILENAME,
11
11
  SkillsClients
12
- } from "./chunk-HIIOVQSH.js";
12
+ } from "./chunk-3HZELBZZ.js";
13
13
 
14
14
  // src/skills-autosync.ts
15
15
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -28,11 +28,11 @@ import {
28
28
  trackCliCommandFinished,
29
29
  trackEvent,
30
30
  waitForServerRuntimeStartup
31
- } from "./chunk-6YJQGOR6.js";
31
+ } from "./chunk-7NMMQ7QO.js";
32
32
  import {
33
33
  autoSyncSkills,
34
34
  formatAutoSyncNotice
35
- } from "./chunk-A47HAZDJ.js";
35
+ } from "./chunk-VJRQTKUB.js";
36
36
  import {
37
37
  CliError,
38
38
  EXIT_SYSTEM_ERROR,
@@ -58,7 +58,7 @@ import {
58
58
  saveConfigPatch,
59
59
  systemError,
60
60
  usageError
61
- } from "./chunk-QPYMAXLJ.js";
61
+ } from "./chunk-OXYM6VCJ.js";
62
62
  import {
63
63
  CLOUDFLARE_WORKER_BINDINGS,
64
64
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -72,7 +72,7 @@ import {
72
72
  projects,
73
73
  queries,
74
74
  renderReportHtml
75
- } from "./chunk-GPTEXYYL.js";
75
+ } from "./chunk-AC3ZIRHR.js";
76
76
  import {
77
77
  AdsDeliverySnapshotStatuses,
78
78
  AdsHistoricalCampaignRollupStatuses,
@@ -146,7 +146,7 @@ import {
146
146
  shareOfVoiceReason,
147
147
  visibilityReportRequestSchema,
148
148
  winnabilityClassSchema
149
- } from "./chunk-HIIOVQSH.js";
149
+ } from "./chunk-3HZELBZZ.js";
150
150
 
151
151
  // src/cli.ts
152
152
  import { pathToFileURL } from "url";
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-6YJQGOR6.js";
6
+ } from "./chunk-7NMMQ7QO.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-QPYMAXLJ.js";
10
- import "./chunk-GPTEXYYL.js";
11
- import "./chunk-HIIOVQSH.js";
9
+ } from "./chunk-OXYM6VCJ.js";
10
+ import "./chunk-AC3ZIRHR.js";
11
+ import "./chunk-3HZELBZZ.js";
12
12
  export {
13
13
  GoogleMarketingRuntimeError,
14
14
  createGoogleMarketingCredentialStore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-GPTEXYYL.js";
4
- import "./chunk-HIIOVQSH.js";
3
+ } from "./chunk-AC3ZIRHR.js";
4
+ import "./chunk-3HZELBZZ.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-A47HAZDJ.js";
3
+ } from "./chunk-VJRQTKUB.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-QPYMAXLJ.js";
7
+ } from "./chunk-OXYM6VCJ.js";
8
8
  import {
9
9
  isReadOnlyKey
10
- } from "./chunk-HIIOVQSH.js";
10
+ } from "./chunk-3HZELBZZ.js";
11
11
 
12
12
  // src/mcp/cli.ts
13
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "4.188.1",
3
+ "version": "4.188.3",
4
4
  "type": "module",
5
5
  "description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
6
6
  "license": "FSL-1.1-ALv2",
@@ -70,30 +70,30 @@
70
70
  "@types/node-cron": "^3.0.11",
71
71
  "tsup": "^8.5.1",
72
72
  "tsx": "^4.19.0",
73
- "@ainyc/canonry-web": "0.0.0",
74
- "@ainyc/canonry-contracts": "0.0.0",
75
73
  "@ainyc/canonry-api-client": "0.0.0",
76
74
  "@ainyc/canonry-api-routes": "0.0.0",
77
75
  "@ainyc/canonry-config": "0.0.0",
78
- "@ainyc/canonry-integration-bing": "0.0.0",
79
76
  "@ainyc/canonry-db": "0.0.0",
77
+ "@ainyc/canonry-contracts": "0.0.0",
78
+ "@ainyc/canonry-integration-bing": "0.0.0",
79
+ "@ainyc/canonry-web": "0.0.0",
80
80
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
81
81
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
82
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
83
- "@ainyc/canonry-integration-google": "0.0.0",
84
82
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
85
83
  "@ainyc/canonry-integration-google-ads": "0.0.0",
86
84
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
85
+ "@ainyc/canonry-integration-google": "0.0.0",
86
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
87
87
  "@ainyc/canonry-integration-google-places": "0.0.0",
88
- "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
89
- "@ainyc/canonry-integration-traffic": "0.0.0",
90
- "@ainyc/canonry-integration-wordpress": "0.0.0",
91
- "@ainyc/canonry-intelligence": "0.0.0",
92
- "@ainyc/canonry-provider-claude": "0.0.0",
93
- "@ainyc/canonry-provider-local": "0.0.0",
94
88
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
89
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
90
+ "@ainyc/canonry-integration-traffic": "0.0.0",
91
+ "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
95
92
  "@ainyc/canonry-provider-cdp": "0.0.0",
93
+ "@ainyc/canonry-provider-claude": "0.0.0",
96
94
  "@ainyc/canonry-provider-gemini": "0.0.0",
95
+ "@ainyc/canonry-provider-local": "0.0.0",
96
+ "@ainyc/canonry-intelligence": "0.0.0",
97
97
  "@ainyc/canonry-provider-openai": "0.0.0",
98
98
  "@ainyc/canonry-provider-perplexity": "0.0.0"
99
99
  },