@canonry/canonry 5.20.2 → 5.20.4

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.
@@ -9286,9 +9286,11 @@ function buildMentionShare(snapshots, options) {
9286
9286
  if (text2.length === 0) continue;
9287
9287
  tally.snapshotsWithAnswerText++;
9288
9288
  if (snap.projectMentioned) tally.projectMentionSnapshots++;
9289
+ if (options.competitors.length === 0) continue;
9290
+ const prepared = prepareBrandMatchText(text2);
9289
9291
  for (const competitor of options.competitors) {
9290
9292
  const matcher = competitorMatchers.get(competitor.domain);
9291
- if (matcher && matcherMatchesText(matcher, text2)) {
9293
+ if (matcher && matcherMatchesText(matcher, prepared)) {
9292
9294
  tally.competitorCounts.set(competitor.domain, (tally.competitorCounts.get(competitor.domain) ?? 0) + 1);
9293
9295
  }
9294
9296
  }
@@ -34885,13 +34887,21 @@ function metric(key, label, queryClass, driftRobust, from, to, continuityBlock)
34885
34887
  const rateRatio = from.point === null || from.point === 0 || to.point === null ? null : Math.round(to.point / from.point * 100) / 100;
34886
34888
  return { key, label, queryClass, driftRobust, from, to, rateRatio, direction, verdict };
34887
34889
  }
34888
- function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable, projectBrandNames) {
34890
+ function modelEvidenceOf(snaps) {
34891
+ const evidence = /* @__PURE__ */ new Map();
34892
+ for (const snap of snaps) {
34893
+ const models = evidence.get(snap.provider) ?? [];
34894
+ models.push(snap.model);
34895
+ evidence.set(snap.provider, models);
34896
+ }
34897
+ return evidence;
34898
+ }
34899
+ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable, projectMatcher) {
34889
34900
  let checked = 0;
34890
34901
  let mentioned = 0;
34891
34902
  let cited = 0;
34892
34903
  let competitorCited = 0;
34893
34904
  const perProvider = /* @__PURE__ */ new Map();
34894
- const modelEvidence = /* @__PURE__ */ new Map();
34895
34905
  const mentionedQueries = /* @__PURE__ */ new Set();
34896
34906
  const competitorHosts = competitors2.map((c) => hostOf(c.domain)).filter((h) => h !== null && h.length > 0);
34897
34907
  for (const snap of snaps) {
@@ -34908,9 +34918,6 @@ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable,
34908
34918
  if (isMentioned) pp.mentioned += 1;
34909
34919
  if (isCited) pp.cited += 1;
34910
34920
  perProvider.set(snap.provider, pp);
34911
- const models = modelEvidence.get(snap.provider) ?? [];
34912
- models.push(snap.model);
34913
- modelEvidence.set(snap.provider, models);
34914
34921
  if (competitorHosts.length > 0 && snap.citedDomains.length > 0) {
34915
34922
  const citedHosts = snap.citedDomains.map((d) => hostOf(d)).filter((h) => h !== null && h.length > 0);
34916
34923
  for (const compHost of competitorHosts) {
@@ -34922,7 +34929,7 @@ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable,
34922
34929
  snaps.map((s) => ({
34923
34930
  // Share uses current identity; named-rate counts above deliberately keep
34924
34931
  // their historical persisted-boolean semantics.
34925
- projectMentioned: s.answerText ? determineAnswerMentioned(s.answerText, [...projectBrandNames], []) : s.answerMentioned === true,
34932
+ projectMentioned: s.answerText ? matcherMatchesText(projectMatcher, s.answerText) : s.answerMentioned === true,
34926
34933
  answerText: s.answerText,
34927
34934
  queryClass: queryClassOf(s)
34928
34935
  })),
@@ -34937,7 +34944,6 @@ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable,
34937
34944
  competitorCited,
34938
34945
  queriesMentioned: mentionedQueries.size,
34939
34946
  perProvider,
34940
- modelEvidence,
34941
34947
  mentionShare,
34942
34948
  competitors: mentionShare.breakdown.perCompetitor.map((c) => ({ domain: c.domain, mentions: c.mentionSnapshots }))
34943
34949
  };
@@ -34956,6 +34962,7 @@ function computeVisibilityCompare(input) {
34956
34962
  const classificationAvailable = classifier !== null;
34957
34963
  const queryTextById = new Map(input.queries.map((q) => [q.id, q.query]));
34958
34964
  const queryClassOf = (snap) => classifier ? classifier.classify(queryTextById.get(snap.queryId) ?? snap.queryText) : null;
34965
+ const projectMatcher = compileBrandAliases(input.brandNames ?? []);
34959
34966
  const fromObs = observed(input.from.snapshots, attribution);
34960
34967
  const toObs = observed(input.to.snapshots, attribution);
34961
34968
  const queriesObservedBoth = new Set([...fromObs.queryIds].filter((q) => toObs.queryIds.has(q)));
@@ -34963,13 +34970,11 @@ function computeVisibilityCompare(input) {
34963
34970
  const toPairs = observedPairs(input.to.snapshots, attribution, queriesObservedBoth);
34964
34971
  const pairsBoth = new Map([...fromPairs].filter(([key]) => toPairs.has(key)));
34965
34972
  const candidateProviders = new Set([...pairsBoth.values()].map((pair) => pair.provider));
34966
- const fromCandidateSnaps = restrict(input.from.snapshots, attribution, pairsBoth);
34967
- const toCandidateSnaps = restrict(input.to.snapshots, attribution, pairsBoth);
34968
- const fromCandidateCounts = countPeriod(fromCandidateSnaps, input.competitors, queryClassOf, classificationAvailable, input.brandNames ?? []);
34969
- const toCandidateCounts = countPeriod(toCandidateSnaps, input.competitors, queryClassOf, classificationAvailable, input.brandNames ?? []);
34973
+ const fromCandidateModels = modelEvidenceOf(restrict(input.from.snapshots, attribution, pairsBoth));
34974
+ const toCandidateModels = modelEvidenceOf(restrict(input.to.snapshots, attribution, pairsBoth));
34970
34975
  const continuityProviders = [...candidateProviders].sort((a, b) => a.localeCompare(b)).map((provider) => {
34971
- const fromEvidence = classifyModelEvidence(fromCandidateCounts.modelEvidence.get(provider) ?? []);
34972
- const toEvidence = classifyModelEvidence(toCandidateCounts.modelEvidence.get(provider) ?? []);
34976
+ const fromEvidence = classifyModelEvidence(fromCandidateModels.get(provider) ?? []);
34977
+ const toEvidence = classifyModelEvidence(toCandidateModels.get(provider) ?? []);
34973
34978
  const fromModels = modelIds(fromEvidence);
34974
34979
  const toModels = modelIds(toEvidence);
34975
34980
  const status = isUnknownModelEvidence(fromEvidence) || isUnknownModelEvidence(toEvidence) ? "model-unknown" : fromEvidence.status === "known" && toEvidence.status === "known" && fromEvidence.model === toEvidence.model ? "included" : "model-discontinuous";
@@ -34985,8 +34990,8 @@ function computeVisibilityCompare(input) {
34985
34990
  const excludedProviders = [.../* @__PURE__ */ new Set([...fromObs.providers, ...toObs.providers])].filter((p) => !providersBoth.has(p)).sort((a, b) => a.localeCompare(b));
34986
34991
  const fromSnaps = restrict(input.from.snapshots, attribution, comparedPairs);
34987
34992
  const toSnaps = restrict(input.to.snapshots, attribution, comparedPairs);
34988
- const fromCounts = countPeriod(fromSnaps, input.competitors, queryClassOf, classificationAvailable, input.brandNames ?? []);
34989
- const toCounts = countPeriod(toSnaps, input.competitors, queryClassOf, classificationAvailable, input.brandNames ?? []);
34993
+ const fromCounts = countPeriod(fromSnaps, input.competitors, queryClassOf, classificationAvailable, projectMatcher);
34994
+ const toCounts = countPeriod(toSnaps, input.competitors, queryClassOf, classificationAvailable, projectMatcher);
34990
34995
  const shareCounts = (c) => ({
34991
34996
  proj: c.mentionShare.breakdown.projectMentionSnapshots,
34992
34997
  comp: c.mentionShare.breakdown.competitorMentionSnapshots
@@ -35037,8 +35042,8 @@ function computeVisibilityCompare(input) {
35037
35042
  )
35038
35043
  ];
35039
35044
  const modelChanges = [...candidateProviders].sort((a, b) => a.localeCompare(b)).map((provider) => {
35040
- const fromModels = modelIds(classifyModelEvidence(fromCandidateCounts.modelEvidence.get(provider) ?? []));
35041
- const toModels = modelIds(classifyModelEvidence(toCandidateCounts.modelEvidence.get(provider) ?? []));
35045
+ const fromModels = modelIds(classifyModelEvidence(fromCandidateModels.get(provider) ?? []));
35046
+ const toModels = modelIds(classifyModelEvidence(toCandidateModels.get(provider) ?? []));
35042
35047
  return { provider, fromModels, toModels };
35043
35048
  }).filter(
35044
35049
  (c) => c.fromModels.length > 0 && c.toModels.length > 0 && JSON.stringify(c.fromModels) !== JSON.stringify(c.toModels)
@@ -48037,6 +48042,8 @@ async function googleRoutes(app, opts) {
48037
48042
  tx.delete(gbpKeywordMonthly).where(eq46(gbpKeywordMonthly.projectId, projectId)).run();
48038
48043
  tx.delete(gbpPlaceActions).where(eq46(gbpPlaceActions.projectId, projectId)).run();
48039
48044
  tx.delete(gbpLodgingSnapshots).where(eq46(gbpLodgingSnapshots.projectId, projectId)).run();
48045
+ tx.delete(gbpAttributesSnapshots).where(eq46(gbpAttributesSnapshots.projectId, projectId)).run();
48046
+ tx.delete(gbpPlaceDetails).where(eq46(gbpPlaceDetails.projectId, projectId)).run();
48040
48047
  tx.delete(gbpLocations).where(eq46(gbpLocations.projectId, projectId)).run();
48041
48048
  }
48042
48049
  function currentProjectAccount(projectId) {
@@ -18,7 +18,7 @@ import {
18
18
  deleteSiteCrawlGraphLayout,
19
19
  persistSiteCrawlGraphLayout,
20
20
  prepareSiteCrawlGraphLayout
21
- } from "./chunk-KHNPBUUN.js";
21
+ } from "./chunk-TWLJRAWA.js";
22
22
  import {
23
23
  CanonryMcpToolNames,
24
24
  CliError,
@@ -186,7 +186,7 @@ import {
186
186
  siteLivenessState,
187
187
  toAlertView,
188
188
  usageCounters
189
- } from "./chunk-7YGOE4R2.js";
189
+ } from "./chunk-NSKFW3NF.js";
190
190
  import {
191
191
  AGENT_MEMORY_VALUE_MAX_BYTES,
192
192
  AGENT_NONE,
@@ -4483,7 +4483,23 @@ async function withRetry4(fn, options = {}) {
4483
4483
  // ../provider-claude/src/normalize.ts
4484
4484
  var DEFAULT_MODEL3 = "claude-sonnet-4-6";
4485
4485
  var VALIDATION_PATTERN = /^claude-/;
4486
- var CLAUDE_RETRIEVAL_CONTRACT = "search-required-v1";
4486
+ var CLAUDE_RETRIEVAL_CONTRACT = RetrievalContracts["search-required-v1"];
4487
+ var CLAUDE_MODELS_REJECTING_FORCED_TOOL_CHOICE = /* @__PURE__ */ new Set(["claude-opus-5-5", "claude-fable-5-1", "claude-mythos-5-1"]);
4488
+ function claudeModelRejectsForcedToolChoice(model) {
4489
+ return CLAUDE_MODELS_REJECTING_FORCED_TOOL_CHOICE.has(
4490
+ model.trim().toLowerCase()
4491
+ );
4492
+ }
4493
+ function claudeRetrievalContractForModel(model) {
4494
+ return claudeModelRejectsForcedToolChoice(model) ? RetrievalContracts["native-auto-v1"] : CLAUDE_RETRIEVAL_CONTRACT;
4495
+ }
4496
+ var CLAUDE_TOOL_CHOICE_BY_CONTRACT = {
4497
+ [RetrievalContracts["search-required-v1"]]: {
4498
+ type: "tool",
4499
+ name: "web_search"
4500
+ },
4501
+ [RetrievalContracts["native-auto-v1"]]: { type: "auto" }
4502
+ };
4487
4503
  function resolveModel2(config) {
4488
4504
  const m = config.model;
4489
4505
  if (!m) return DEFAULT_MODEL3;
@@ -4498,7 +4514,7 @@ function validateConfig3(config) {
4498
4514
  return { ok: false, provider: "claude", message: "missing api key" };
4499
4515
  }
4500
4516
  const model = resolveModel2(config);
4501
- const warning = config.model && !VALIDATION_PATTERN.test(config.model) ? ` (invalid model "${config.model}" replaced with default)` : "";
4517
+ const warning = config.model && !VALIDATION_PATTERN.test(config.model) ? ` (invalid model "${config.model}" replaced with default)` : claudeModelRejectsForcedToolChoice(model) ? ` (${model} rejects forced tool_choice, so tracked queries run under ${claudeRetrievalContractForModel(model)}: web_search is offered but retrieval is not guaranteed)` : "";
4502
4518
  return {
4503
4519
  ok: true,
4504
4520
  provider: "claude",
@@ -4537,6 +4553,7 @@ async function healthcheck3(config) {
4537
4553
  }
4538
4554
  async function executeTrackedQuery3(input) {
4539
4555
  const model = resolveModel2(input.config);
4556
+ const retrievalContract = claudeRetrievalContractForModel(model);
4540
4557
  const client = new Anthropic({ apiKey: input.config.apiKey });
4541
4558
  const webSearchTool = {
4542
4559
  type: "web_search_20250305",
@@ -4558,9 +4575,11 @@ async function executeTrackedQuery3(input) {
4558
4575
  model,
4559
4576
  max_tokens: 4096,
4560
4577
  tools: [webSearchTool],
4561
- // search-required-v1: retrieval is guaranteed by the API control, so the
4562
- // query text and answer substance stay untouched. See the contract note.
4563
- tool_choice: { type: "tool", name: "web_search" },
4578
+ // search-required-v1 forces web_search, so retrieval is guaranteed by the
4579
+ // API control and the query text and answer substance stay untouched.
4580
+ // native-auto-v1 (models that reject forcing) leaves the search to
4581
+ // Claude. See claudeRetrievalContractForModel.
4582
+ tool_choice: CLAUDE_TOOL_CHOICE_BY_CONTRACT[retrievalContract],
4564
4583
  messages: [{ role: "user", content: input.query }]
4565
4584
  })
4566
4585
  );
@@ -4577,7 +4596,7 @@ async function executeTrackedQuery3(input) {
4577
4596
  groundingSources: parsed.groundingSources,
4578
4597
  searchQueries: parsed.searchQueries,
4579
4598
  retrievalStatus: parsed.retrievalStatus,
4580
- retrievalContract: CLAUDE_RETRIEVAL_CONTRACT
4599
+ retrievalContract
4581
4600
  };
4582
4601
  } catch (err) {
4583
4602
  const msg = describeError(err);
@@ -4859,7 +4878,8 @@ var claudeAdapter = {
4859
4878
  // across this boundary. A reconstruction that predates the field falls
4860
4879
  // back to `unknown` rather than asserting an absence.
4861
4880
  retrievalStatus: raw.retrievalStatus ?? "unknown",
4862
- retrievalContract: raw.retrievalContract ?? CLAUDE_RETRIEVAL_CONTRACT
4881
+ // The contract is decided by the model the request was built for.
4882
+ retrievalContract: raw.retrievalContract ?? claudeRetrievalContractForModel(raw.model)
4863
4883
  };
4864
4884
  const normalized = normalizeResult3(claudeRaw);
4865
4885
  return {
@@ -6628,7 +6648,7 @@ function nonBlank(value) {
6628
6648
  return trimmed ? trimmed : void 0;
6629
6649
  }
6630
6650
  function resolveBuildCommit(env = process.env) {
6631
- const embedded = true ? "b00090f58284f63906eba753cace2b96a166ccfd" : void 0;
6651
+ const embedded = true ? "c87b5bdf06eeeab4fafa7742f07c1c91149c0b70" : void 0;
6632
6652
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
6633
6653
  }
6634
6654
  function resolveInstanceIdentity(env = process.env) {
@@ -12215,7 +12235,7 @@ function readStoredGroundingSources(rawResponse) {
12215
12235
  return result2;
12216
12236
  }
12217
12237
  async function backfillInsightsCommand(project, opts) {
12218
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-H4LGPJLZ.js");
12238
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-QHSR2H7F.js");
12219
12239
  const config = loadConfig();
12220
12240
  const db = createClient(config.database);
12221
12241
  migrate(db);
@@ -5,7 +5,7 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-7YGOE4R2.js";
8
+ } from "./chunk-NSKFW3NF.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
package/dist/cli.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-RV3PYKXP.js";
38
+ } from "./chunk-P73YBGZP.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
@@ -62,7 +62,7 @@ import {
62
62
  trustedProxiesProblem,
63
63
  trustedProxyProblem
64
64
  } from "./chunk-RB2F4D3Z.js";
65
- import "./chunk-KHNPBUUN.js";
65
+ import "./chunk-TWLJRAWA.js";
66
66
  import {
67
67
  CliError,
68
68
  EXIT_SYSTEM_ERROR,
@@ -89,7 +89,7 @@ import {
89
89
  queries,
90
90
  renderReportHtml,
91
91
  runs
92
- } from "./chunk-7YGOE4R2.js";
92
+ } from "./chunk-NSKFW3NF.js";
93
93
  import {
94
94
  AdsDeliverySnapshotStatuses,
95
95
  AdsHistoricalCampaignRollupStatuses,
@@ -442,7 +442,7 @@ function parseDemoTrustedProxies(values) {
442
442
  async function demoCommand(options) {
443
443
  const { host, port } = parseDemoListenOptions(options);
444
444
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
445
- const { createDemoServer } = await import("./demo-server-JYDAUEP2.js");
445
+ const { createDemoServer } = await import("./demo-server-FH2K6TCD.js");
446
446
  const app = await createDemoServer({ trustProxy });
447
447
  let stopping = false;
448
448
  const stop = () => {
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  layoutSiteCrawlGraphInput,
6
6
  persistSiteCrawlGraphLayout
7
- } from "./chunk-KHNPBUUN.js";
7
+ } from "./chunk-TWLJRAWA.js";
8
8
  import {
9
9
  PACKAGE_VERSION,
10
10
  canonryMcpTools
@@ -84,7 +84,7 @@ import {
84
84
  siteCrawlPages,
85
85
  siteCrawlSnapshots,
86
86
  trafficSources
87
- } from "./chunk-7YGOE4R2.js";
87
+ } from "./chunk-NSKFW3NF.js";
88
88
  import {
89
89
  RunKinds,
90
90
  SiteCrawlFetchStates,
package/dist/index.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-RV3PYKXP.js";
6
+ } from "./chunk-P73YBGZP.js";
7
7
  import {
8
8
  loadConfig
9
9
  } from "./chunk-PXVJ3P5G.js";
10
- import "./chunk-KHNPBUUN.js";
10
+ import "./chunk-TWLJRAWA.js";
11
11
  import "./chunk-JJPZ4OQ2.js";
12
- import "./chunk-7YGOE4R2.js";
12
+ import "./chunk-NSKFW3NF.js";
13
13
  import "./chunk-TOSPZ7I5.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-7YGOE4R2.js";
4
+ } from "./chunk-NSKFW3NF.js";
5
5
  import "./chunk-TOSPZ7I5.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "5.20.2",
3
+ "version": "5.20.4",
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
  "keywords": [
@@ -87,27 +87,27 @@
87
87
  "tsup": "^8.5.1",
88
88
  "tsx": "^4.19.0",
89
89
  "@ainyc/canonry-web": "0.0.0",
90
- "@ainyc/canonry-api-routes": "0.0.0",
91
90
  "@ainyc/canonry-api-client": "0.0.0",
91
+ "@ainyc/canonry-api-routes": "0.0.0",
92
92
  "@ainyc/canonry-config": "0.0.0",
93
- "@ainyc/canonry-contracts": "0.0.0",
94
93
  "@ainyc/canonry-db": "0.0.0",
94
+ "@ainyc/canonry-contracts": "0.0.0",
95
95
  "@ainyc/canonry-integration-bing": "0.0.0",
96
96
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
97
- "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
97
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
98
+ "@ainyc/canonry-integration-google": "0.0.0",
98
99
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
100
+ "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
99
101
  "@ainyc/canonry-integration-google-ads": "0.0.0",
100
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
101
- "@ainyc/canonry-integration-google": "0.0.0",
102
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
103
102
  "@ainyc/canonry-integration-google-places": "0.0.0",
104
103
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
104
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
105
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
105
106
  "@ainyc/canonry-integration-traffic": "0.0.0",
106
- "@ainyc/canonry-integration-wordpress": "0.0.0",
107
+ "@ainyc/canonry-intelligence": "0.0.0",
107
108
  "@ainyc/canonry-provider-cdp": "0.0.0",
108
109
  "@ainyc/canonry-provider-gemini": "0.0.0",
109
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
110
- "@ainyc/canonry-intelligence": "0.0.0",
110
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
111
111
  "@ainyc/canonry-provider-local": "0.0.0",
112
112
  "@ainyc/canonry-provider-perplexity": "0.0.0",
113
113
  "@ainyc/canonry-provider-openai": "0.0.0",