@canonry/canonry 5.33.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +15 -3
- package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +1 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +16 -11
- package/assets/assets/{AuditHistoryPanel-CcCIqJzU.js → AuditHistoryPanel-Bz2NVB6-.js} +1 -1
- package/assets/assets/{BacklinksPage-B4r9SLIP.js → BacklinksPage-Dte5-usf.js} +1 -1
- package/assets/assets/{HistoryPage-DIpbpqWc.js → HistoryPage-C1S4nw3I.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BF93li8O.js → MeasurementPropertyPage-DWjsHZdt.js} +1 -1
- package/assets/assets/ProjectPage-CAJ9XvNt.js +8 -0
- package/assets/assets/RunsPage-D9wEJFfV.js +1 -0
- package/assets/assets/SettingsPage-CN3Gew7Y.js +1 -0
- package/assets/assets/SiteHealthSection-Bft5TfoU.js +4 -0
- package/assets/assets/TrafficPage-tkNvySQH.js +40 -0
- package/assets/assets/{TrafficSourceDetailPage-psddSEHZ.js → TrafficSourceDetailPage-RhRfPizo.js} +1 -1
- package/assets/assets/{extract-error-message-BbHnZTOx.js → extract-error-message-tfl_SU7q.js} +1 -1
- package/assets/assets/index-B11OXdEC.css +1 -0
- package/assets/assets/index-Cu_xgdb1.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min-6EgecOc3.js → react-sigma_core.esm.min-BUu3mcQJ.js} +1 -1
- package/assets/assets/{v2-overview-adapter-0pVxmjG8.js → v2-overview-adapter-CzeGhaXn.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-UDEZZMV5.js → chunk-4NC5ZAC2.js} +3 -3
- package/dist/{chunk-AQFRQTKY.js → chunk-6M7HSNXJ.js} +2 -2
- package/dist/{chunk-XICZEXE5.js → chunk-F4G7I7T7.js} +23 -135
- package/dist/{chunk-SJX7XJB6.js → chunk-HAALQW4R.js} +121 -18
- package/dist/{chunk-Y23FQK6O.js → chunk-K4356AGP.js} +10 -10
- package/dist/{chunk-G3CMWO26.js → chunk-XGERCNUR.js} +6 -3
- package/dist/{chunk-GIKWL2BQ.js → chunk-ZO62Z2WN.js} +137 -21
- package/dist/cli.js +52 -16
- package/dist/{demo-server-G4BFXA6G.js → demo-server-54OUTZMY.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-FVOFIJP3.js → intelligence-service-MEIAMMLT.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +14 -14
- package/assets/assets/ProjectPage-CjadJKsG.js +0 -8
- package/assets/assets/RunRow-C-d8luIA.js +0 -1
- package/assets/assets/RunsPage-DEbjaNYY.js +0 -1
- package/assets/assets/SettingsPage-B5h3wM7i.js +0 -1
- package/assets/assets/SiteHealthSection-Cj2lo1GY.js +0 -4
- package/assets/assets/TrafficPage-CFDh9Rx4.js +0 -40
- package/assets/assets/index-CiWV331j.js +0 -86
- package/assets/assets/index-DX5M4MHr.css +0 -1
|
@@ -291,6 +291,7 @@ import {
|
|
|
291
291
|
compareSemver,
|
|
292
292
|
competitorBatchRequestSchema,
|
|
293
293
|
competitorDtoSchema,
|
|
294
|
+
competitorLabelFromDomain,
|
|
294
295
|
competitorLandscapeQuerySchema,
|
|
295
296
|
competitorLandscapeResponseSchema,
|
|
296
297
|
compileBrandAliases,
|
|
@@ -720,6 +721,7 @@ import {
|
|
|
720
721
|
resolveMeasurementRunQueryScope,
|
|
721
722
|
resolveMeasurementRunScope,
|
|
722
723
|
resolveNegativeReviewMaxStars,
|
|
724
|
+
resolveProjectQualifiedAliases,
|
|
723
725
|
resolveProviderModel,
|
|
724
726
|
resolveRunDispatchModes,
|
|
725
727
|
resolveSnapshotRequestQueries,
|
|
@@ -774,6 +776,7 @@ import {
|
|
|
774
776
|
shareOfVoiceReason,
|
|
775
777
|
shareOfVoiceSummary,
|
|
776
778
|
shiftIsoCalendarDate,
|
|
779
|
+
simpleMeasurementCompetitorNames,
|
|
777
780
|
simpleMeasurementDefinitionSchema,
|
|
778
781
|
siteAuditLivePageHealthSchema,
|
|
779
782
|
siteAuditPageFactorSchema,
|
|
@@ -866,7 +869,7 @@ import {
|
|
|
866
869
|
wordpressSchemaDeployResultDtoSchema,
|
|
867
870
|
wordpressSchemaStatusResultDtoSchema,
|
|
868
871
|
wordpressStatusDtoSchema
|
|
869
|
-
} from "./chunk-
|
|
872
|
+
} from "./chunk-HAALQW4R.js";
|
|
870
873
|
|
|
871
874
|
// src/intelligence-service.ts
|
|
872
875
|
import { eq as eq82, desc as desc36, asc as asc15, and as and69, ne as ne14, or as or20, inArray as inArray32, gte as gte22, lte as lte20, isNull as isNull16, sql as sql31, exists } from "drizzle-orm";
|
|
@@ -1017,6 +1020,12 @@ var projects = sqliteTable("projects", {
|
|
|
1017
1020
|
canonicalDomain: text("canonical_domain").notNull(),
|
|
1018
1021
|
ownedDomains: text("owned_domains", { mode: "json" }).$type().notNull().default([]),
|
|
1019
1022
|
aliases: text("aliases", { mode: "json" }).$type().notNull().default([]),
|
|
1023
|
+
/**
|
|
1024
|
+
* Operator-chosen subset of `aliases` that Simple sentiment tells the
|
|
1025
|
+
* evaluator are this brand's own names. Frozen into each Simple run's
|
|
1026
|
+
* sidecar at dispatch; never read by mention detection or query classes.
|
|
1027
|
+
*/
|
|
1028
|
+
qualifiedAliases: text("qualified_aliases", { mode: "json" }).$type().notNull().default([]),
|
|
1020
1029
|
country: text("country").notNull(),
|
|
1021
1030
|
language: text("language").notNull(),
|
|
1022
1031
|
tags: text("tags", { mode: "json" }).$type().notNull().default([]),
|
|
@@ -8172,6 +8181,16 @@ var MIGRATION_VERSIONS = [
|
|
|
8172
8181
|
name: "aero-ledger-conversation-ids",
|
|
8173
8182
|
statements: [],
|
|
8174
8183
|
run: dropAeroLedgerConversationKeys
|
|
8184
|
+
},
|
|
8185
|
+
{
|
|
8186
|
+
// Sentiment-only project setting: the aliases the evaluator is told are
|
|
8187
|
+
// the brand's own names. Defaulted, so an older writer that omits it and
|
|
8188
|
+
// every existing project read as not opted in.
|
|
8189
|
+
version: 166,
|
|
8190
|
+
name: "projects-qualified-aliases",
|
|
8191
|
+
statements: [
|
|
8192
|
+
`ALTER TABLE projects ADD COLUMN qualified_aliases TEXT NOT NULL DEFAULT '[]'`
|
|
8193
|
+
]
|
|
8175
8194
|
}
|
|
8176
8195
|
];
|
|
8177
8196
|
var AERO_LEDGER_REBUILDS = [
|
|
@@ -11275,7 +11294,7 @@ function gapTone(gapCount, totalCount) {
|
|
|
11275
11294
|
|
|
11276
11295
|
// ../intelligence/src/visibility-score.ts
|
|
11277
11296
|
function buildVisibilityScore(snapshots, options) {
|
|
11278
|
-
const tooltip = "An
|
|
11297
|
+
const tooltip = "An AI engine used a page on your domain as a source for its answer.";
|
|
11279
11298
|
if (snapshots.length === 0) {
|
|
11280
11299
|
return {
|
|
11281
11300
|
label: "Citation Coverage",
|
|
@@ -11307,13 +11326,13 @@ function buildVisibilityScore(snapshots, options) {
|
|
|
11307
11326
|
tooltip,
|
|
11308
11327
|
trend: [],
|
|
11309
11328
|
progress: score,
|
|
11310
|
-
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length}
|
|
11329
|
+
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length} engines` : void 0
|
|
11311
11330
|
};
|
|
11312
11331
|
}
|
|
11313
11332
|
|
|
11314
11333
|
// ../intelligence/src/mention-coverage.ts
|
|
11315
11334
|
function buildMentionCoverage(snapshots, options) {
|
|
11316
|
-
const tooltip = "Your domain or company name was in the answer returned by the
|
|
11335
|
+
const tooltip = "Your domain or company name was in the answer returned by the AI engine.";
|
|
11317
11336
|
if (snapshots.length === 0) {
|
|
11318
11337
|
return {
|
|
11319
11338
|
label: "Mention Coverage",
|
|
@@ -11345,7 +11364,7 @@ function buildMentionCoverage(snapshots, options) {
|
|
|
11345
11364
|
tooltip,
|
|
11346
11365
|
trend: [],
|
|
11347
11366
|
progress: score,
|
|
11348
|
-
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length}
|
|
11367
|
+
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length} engines` : void 0
|
|
11349
11368
|
};
|
|
11350
11369
|
}
|
|
11351
11370
|
|
|
@@ -11379,7 +11398,7 @@ function buildGapQueryScore(snapshots) {
|
|
|
11379
11398
|
return {
|
|
11380
11399
|
label: "Citation Gaps",
|
|
11381
11400
|
value: `${gapCount}`,
|
|
11382
|
-
delta: `${gapCount} of ${totalCount}
|
|
11401
|
+
delta: `${gapCount} of ${totalCount} ${totalCount === 1 ? "query" : "queries"}`,
|
|
11383
11402
|
tone: gapTone(gapCount, totalCount),
|
|
11384
11403
|
description: gapCount > 0 ? `${gapCount} tracked ${gapQueryLabel} currently cite competitors without citing your domain.` : "No competitive citation gaps detected in the latest visibility run.",
|
|
11385
11404
|
tooltip,
|
|
@@ -11416,7 +11435,7 @@ function buildMentionGapScore(snapshots) {
|
|
|
11416
11435
|
return {
|
|
11417
11436
|
label: "Mention Gaps",
|
|
11418
11437
|
value: `${gapCount}`,
|
|
11419
|
-
delta: `${gapCount} of ${totalCount}
|
|
11438
|
+
delta: `${gapCount} of ${totalCount} ${totalCount === 1 ? "query" : "queries"}`,
|
|
11420
11439
|
tone: gapTone(gapCount, totalCount),
|
|
11421
11440
|
description: gapCount > 0 ? `${gapCount} tracked ${gapQueryLabel} mention competitors but never your brand.` : "No competitive mention gaps detected in the latest visibility run.",
|
|
11422
11441
|
tooltip,
|
|
@@ -13638,6 +13657,7 @@ function selectSentimentSources(db, projectId, filters) {
|
|
|
13638
13657
|
continue;
|
|
13639
13658
|
}
|
|
13640
13659
|
const identity = definition.identity;
|
|
13660
|
+
const qualifiedAliases = [...identity.qualifiedAliases ?? []].sort();
|
|
13641
13661
|
const names = effectiveBrandNames(identity);
|
|
13642
13662
|
const urls = [identity.canonicalDomain, ...identity.ownedDomains];
|
|
13643
13663
|
const mentionNotApplicable = !names.length && !urls.some((url) => url.trim());
|
|
@@ -13659,7 +13679,7 @@ function selectSentimentSources(db, projectId, filters) {
|
|
|
13659
13679
|
language: definition.language,
|
|
13660
13680
|
queryText: query.queryText,
|
|
13661
13681
|
revision: null,
|
|
13662
|
-
subject: { key: projectId, name: identity.displayName, aliases: [...names], identityAliases: [], urls: [...urls], mentionNotApplicable },
|
|
13682
|
+
subject: { key: projectId, name: identity.displayName, aliases: [...names], identityAliases: [...qualifiedAliases], urls: [...urls], mentionNotApplicable },
|
|
13663
13683
|
edges: [edge]
|
|
13664
13684
|
});
|
|
13665
13685
|
}
|
|
@@ -13844,7 +13864,7 @@ function sentimentClassifierInput(source, definition) {
|
|
|
13844
13864
|
}
|
|
13845
13865
|
|
|
13846
13866
|
// ../api-routes/src/sentiment-service.ts
|
|
13847
|
-
var DISCLOSURE = "Experimental English sentiment classification. Enabling sends each stored answer's text to TypeSafe with the frozen subject identity (name, aliases and URLs), the tracked query text and query class, the answer engine with its requested and served models, the location, and internal query, subject, Property, group and market identifiers.";
|
|
13867
|
+
var DISCLOSURE = "Experimental English sentiment classification. Enabling sends each stored answer's text to TypeSafe with the frozen subject identity (name, aliases, qualified aliases and URLs), the tracked query text and query class, the answer engine with its requested and served models, the location, and internal query, subject, Property, group and market identifiers.";
|
|
13848
13868
|
var FULL_ROWS = { include: /* @__PURE__ */ new Set(["assessments", "locations"]), limit: null, after: null };
|
|
13849
13869
|
var COMPACT_ROWS = { include: /* @__PURE__ */ new Set(), limit: null, after: null };
|
|
13850
13870
|
var OPERATIONAL_OUTCOMES = /* @__PURE__ */ new Set([SentimentOutcomes.pending, SentimentOutcomes.running, SentimentOutcomes["waiting-to-retry"], SentimentOutcomes.failed, SentimentOutcomes.canceled]);
|
|
@@ -16866,6 +16886,7 @@ async function projectRoutes(app, opts) {
|
|
|
16866
16886
|
const id = crypto9.randomUUID();
|
|
16867
16887
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16868
16888
|
const nextAliases = normalizeProjectAliases(body.displayName, body.aliases ?? []);
|
|
16889
|
+
const qualifiedAliases = body.qualifiedAliases === void 0 ? [] : requireQualifiedAliases({ displayName: body.displayName, aliases: nextAliases }, body.qualifiedAliases, []);
|
|
16869
16890
|
const inserted = app.db.transaction((tx) => {
|
|
16870
16891
|
const result = tx.insert(projects).values({
|
|
16871
16892
|
id,
|
|
@@ -16874,6 +16895,7 @@ async function projectRoutes(app, opts) {
|
|
|
16874
16895
|
canonicalDomain,
|
|
16875
16896
|
ownedDomains: body.ownedDomains ?? [],
|
|
16876
16897
|
aliases: nextAliases,
|
|
16898
|
+
qualifiedAliases,
|
|
16877
16899
|
country: body.country,
|
|
16878
16900
|
language: body.language,
|
|
16879
16901
|
tags: body.tags ?? [],
|
|
@@ -16960,6 +16982,9 @@ async function projectRoutes(app, opts) {
|
|
|
16960
16982
|
const nextNegativeReviewMaxStars = body.negativeReviewMaxStars !== void 0 ? body.negativeReviewMaxStars : existing ? readNegativeReviewMaxStars(app.db, existing.id) : null;
|
|
16961
16983
|
const nextMeasurement = body.measurement ?? existing?.measurement ?? DEFAULT_MEASUREMENT_CONFIG;
|
|
16962
16984
|
const nextAliases = normalizeProjectAliases(body.displayName, body.aliases ?? []);
|
|
16985
|
+
const nextIdentity = { displayName: body.displayName, aliases: nextAliases };
|
|
16986
|
+
const liveCompetitors = existing ? liveCompetitorNames(app.db, existing.id) : [];
|
|
16987
|
+
const nextQualifiedAliases = body.qualifiedAliases !== void 0 ? requireQualifiedAliases(nextIdentity, body.qualifiedAliases, liveCompetitors) : resolveProjectQualifiedAliases(nextIdentity, existing?.qualifiedAliases ?? [], liveCompetitors).value;
|
|
16963
16988
|
if (existing) {
|
|
16964
16989
|
const prevAliases = existing.aliases;
|
|
16965
16990
|
const aliasesChanged = !aliasArraysEqual(prevAliases, nextAliases);
|
|
@@ -16969,6 +16994,7 @@ async function projectRoutes(app, opts) {
|
|
|
16969
16994
|
canonicalDomain: body.canonicalDomain,
|
|
16970
16995
|
ownedDomains: body.ownedDomains ?? [],
|
|
16971
16996
|
aliases: nextAliases,
|
|
16997
|
+
qualifiedAliases: nextQualifiedAliases,
|
|
16972
16998
|
country: body.country,
|
|
16973
16999
|
language: body.language,
|
|
16974
17000
|
tags: body.tags ?? [],
|
|
@@ -17007,6 +17033,7 @@ async function projectRoutes(app, opts) {
|
|
|
17007
17033
|
canonicalDomain: body.canonicalDomain,
|
|
17008
17034
|
ownedDomains: body.ownedDomains ?? [],
|
|
17009
17035
|
aliases: nextAliases,
|
|
17036
|
+
qualifiedAliases: nextQualifiedAliases,
|
|
17010
17037
|
country: body.country,
|
|
17011
17038
|
language: body.language,
|
|
17012
17039
|
tags: body.tags ?? [],
|
|
@@ -17195,6 +17222,11 @@ async function projectRoutes(app, opts) {
|
|
|
17195
17222
|
eq15(schedules.kind, SchedulableRunKinds["answer-visibility"])
|
|
17196
17223
|
)).get();
|
|
17197
17224
|
const notificationRows = app.db.select().from(notifications).where(eq15(notifications.projectId, project.id)).all();
|
|
17225
|
+
const exportedQualifiedAliases = resolveProjectQualifiedAliases(
|
|
17226
|
+
project,
|
|
17227
|
+
project.qualifiedAliases,
|
|
17228
|
+
comps.map((c) => competitorLabelFromDomain(c.domain))
|
|
17229
|
+
).value;
|
|
17198
17230
|
const config = {
|
|
17199
17231
|
apiVersion: "canonry/v1",
|
|
17200
17232
|
kind: "Project",
|
|
@@ -17207,6 +17239,7 @@ async function projectRoutes(app, opts) {
|
|
|
17207
17239
|
canonicalDomain: project.canonicalDomain,
|
|
17208
17240
|
ownedDomains: project.ownedDomains,
|
|
17209
17241
|
aliases: project.aliases,
|
|
17242
|
+
...exportedQualifiedAliases.length ? { qualifiedAliases: exportedQualifiedAliases } : {},
|
|
17210
17243
|
country: project.country,
|
|
17211
17244
|
language: project.language,
|
|
17212
17245
|
queries: qs.map((q) => q.query),
|
|
@@ -17246,6 +17279,32 @@ function assertProviderModelScope(request, current, next, nextProviders) {
|
|
|
17246
17279
|
if (providerModelsEqual(effectiveCurrent, effectiveNext)) return;
|
|
17247
17280
|
requireScope(request, SETTINGS_WRITE_SCOPE);
|
|
17248
17281
|
}
|
|
17282
|
+
function requireQualifiedAliases(identity, requested, competitorNames) {
|
|
17283
|
+
const resolved = resolveProjectQualifiedAliases(identity, requested, competitorNames);
|
|
17284
|
+
if (resolved.rejected.length > 0) {
|
|
17285
|
+
const reasons = resolved.rejected.map((entry) => `${entry.name} (${entry.reason})`).join(", ");
|
|
17286
|
+
throw validationError(`Rejected qualifiedAliases: ${reasons}`, {
|
|
17287
|
+
rejectedQualifiedAliases: resolved.rejected
|
|
17288
|
+
});
|
|
17289
|
+
}
|
|
17290
|
+
return resolved.value;
|
|
17291
|
+
}
|
|
17292
|
+
function liveCompetitorNames(db, projectId) {
|
|
17293
|
+
return db.select({ domain: competitors.domain }).from(competitors).where(eq15(competitors.projectId, projectId)).all().map((row) => competitorLabelFromDomain(row.domain));
|
|
17294
|
+
}
|
|
17295
|
+
function pruneQualifiedAliasesForCompetitors(tx, projectId, now) {
|
|
17296
|
+
const project = tx.select({
|
|
17297
|
+
displayName: projects.displayName,
|
|
17298
|
+
aliases: projects.aliases,
|
|
17299
|
+
qualifiedAliases: projects.qualifiedAliases
|
|
17300
|
+
}).from(projects).where(eq15(projects.id, projectId)).get();
|
|
17301
|
+
if (!project?.qualifiedAliases.length) return [];
|
|
17302
|
+
const kept = resolveProjectQualifiedAliases(project, project.qualifiedAliases, liveCompetitorNames(tx, projectId)).value;
|
|
17303
|
+
const dropped = project.qualifiedAliases.filter((name) => !kept.includes(name));
|
|
17304
|
+
if (dropped.length === 0) return [];
|
|
17305
|
+
tx.update(projects).set({ qualifiedAliases: kept, updatedAt: now }).where(eq15(projects.id, projectId)).run();
|
|
17306
|
+
return dropped;
|
|
17307
|
+
}
|
|
17249
17308
|
function providerModelsEqual(a, b) {
|
|
17250
17309
|
const aKeys = Object.keys(a);
|
|
17251
17310
|
if (aKeys.length !== Object.keys(b).length) return false;
|
|
@@ -17259,6 +17318,7 @@ function formatProject(row, negativeReviewMaxStars) {
|
|
|
17259
17318
|
canonicalDomain: row.canonicalDomain,
|
|
17260
17319
|
ownedDomains: row.ownedDomains,
|
|
17261
17320
|
aliases: row.aliases,
|
|
17321
|
+
qualifiedAliases: row.qualifiedAliases,
|
|
17262
17322
|
country: row.country,
|
|
17263
17323
|
language: row.language,
|
|
17264
17324
|
tags: row.tags,
|
|
@@ -17977,12 +18037,16 @@ async function competitorRoutes(app) {
|
|
|
17977
18037
|
createdAt: now
|
|
17978
18038
|
}).run();
|
|
17979
18039
|
}
|
|
18040
|
+
const droppedQualifiedAliases = pruneQualifiedAliasesForCompetitors(tx, project.id, now);
|
|
17980
18041
|
writeAuditLog(tx, {
|
|
17981
18042
|
projectId: project.id,
|
|
17982
18043
|
actor: "api",
|
|
17983
18044
|
action: "competitors.replaced",
|
|
17984
18045
|
entityType: "competitor",
|
|
17985
|
-
diff: {
|
|
18046
|
+
diff: {
|
|
18047
|
+
competitors: normalizedCompetitors,
|
|
18048
|
+
...droppedQualifiedAliases.length ? { droppedQualifiedAliases } : {}
|
|
18049
|
+
}
|
|
17986
18050
|
});
|
|
17987
18051
|
});
|
|
17988
18052
|
const rows = app.db.select().from(competitors).where(eq19(competitors.projectId, project.id)).all();
|
|
@@ -18009,12 +18073,13 @@ async function competitorRoutes(app) {
|
|
|
18009
18073
|
target: [competitors.projectId, competitors.domain]
|
|
18010
18074
|
}).run();
|
|
18011
18075
|
}
|
|
18076
|
+
const droppedQualifiedAliases = pruneQualifiedAliasesForCompetitors(tx, project.id, now);
|
|
18012
18077
|
writeAuditLog(tx, {
|
|
18013
18078
|
projectId: project.id,
|
|
18014
18079
|
actor: "api",
|
|
18015
18080
|
action: "competitors.appended",
|
|
18016
18081
|
entityType: "competitor",
|
|
18017
|
-
diff: { added }
|
|
18082
|
+
diff: { added, ...droppedQualifiedAliases.length ? { droppedQualifiedAliases } : {} }
|
|
18018
18083
|
});
|
|
18019
18084
|
});
|
|
18020
18085
|
const rows = app.db.select().from(competitors).where(eq19(competitors.projectId, project.id)).all();
|
|
@@ -25670,7 +25735,7 @@ function mentionRanking(rows, limit) {
|
|
|
25670
25735
|
truncated: eligible.length > limit
|
|
25671
25736
|
};
|
|
25672
25737
|
}
|
|
25673
|
-
function portfolioResponse(db, active, plan, query) {
|
|
25738
|
+
function portfolioResponse(db, active, plan, query, ownDomains) {
|
|
25674
25739
|
const group = query.groupKey === void 0 ? void 0 : requireGroup2(plan, query.groupKey);
|
|
25675
25740
|
const run = selectMeasurementQuestionRun(db, active.version.projectId, active, query.runId);
|
|
25676
25741
|
const requestedTargetKeys = group?.targetKeys ?? plan.targets.map((target) => target.stableKey);
|
|
@@ -25766,7 +25831,7 @@ function portfolioResponse(db, active, plan, query) {
|
|
|
25766
25831
|
},
|
|
25767
25832
|
weakestProperties: rows,
|
|
25768
25833
|
tiedAtWeakest: tie.summary,
|
|
25769
|
-
weakestAnswerSources: weakestAnswerSources([...displayed, ...tie.rows]),
|
|
25834
|
+
weakestAnswerSources: weakestAnswerSources([...displayed, ...tie.rows], ownDomains),
|
|
25770
25835
|
mentionRanking: mentionRanking(ranked, limit),
|
|
25771
25836
|
...marketRollup(plan, { run, evaluator }, group, includeNestedMarkets),
|
|
25772
25837
|
totalProperties: ranked.length,
|
|
@@ -25827,7 +25892,7 @@ function tieNamedInstead(rows) {
|
|
|
25827
25892
|
const counted = [...grouped.values()].map((row) => ({ name: row.name, answers: row.answers.size })).sort((left, right) => right.answers - left.answers || compareText13(left.name, right.name));
|
|
25828
25893
|
return { rows: counted.slice(0, MEASUREMENT_PORTFOLIO_TIE_NAMED_INSTEAD_LIMIT), total: counted.length };
|
|
25829
25894
|
}
|
|
25830
|
-
function weakestAnswerSources(rows) {
|
|
25895
|
+
function weakestAnswerSources(rows, ownDomains) {
|
|
25831
25896
|
const properties = /* @__PURE__ */ new Set();
|
|
25832
25897
|
const answers = /* @__PURE__ */ new Map();
|
|
25833
25898
|
for (const row of rows) {
|
|
@@ -25835,7 +25900,11 @@ function weakestAnswerSources(rows) {
|
|
|
25835
25900
|
for (const answer of row.population.sourceAnswers) answers.set(answer.snapshot.id, answer);
|
|
25836
25901
|
}
|
|
25837
25902
|
const domains = domainRows([...answers.values()], MEASUREMENT_PORTFOLIO_ANSWER_SOURCES_LIMIT);
|
|
25838
|
-
|
|
25903
|
+
let ownDomainAnswers = 0;
|
|
25904
|
+
for (const answer of answers.values()) {
|
|
25905
|
+
if ([...answerDomains(answer)].some((host) => hostMatchesAnyDomain(host, ownDomains))) ownDomainAnswers++;
|
|
25906
|
+
}
|
|
25907
|
+
return { properties: properties.size, answers: answers.size, domains: domains.rows, domainTotal: domains.total, ownDomainAnswers };
|
|
25839
25908
|
}
|
|
25840
25909
|
function propertyCompetitorsResponse(db, active, plan, query) {
|
|
25841
25910
|
const target = requireTarget3(plan, query.targetKey);
|
|
@@ -26047,11 +26116,14 @@ function classifyPropertyMove(mention, citation) {
|
|
|
26047
26116
|
const down = directions.some((move) => move < 0);
|
|
26048
26117
|
bucket = up && down ? "mixed" : up ? "improved" : "declined";
|
|
26049
26118
|
}
|
|
26119
|
+
const signalWithinNoise = (move) => move === null ? null : Math.abs(move) <= MEASUREMENT_CHANGES_NOISE_ANSWERS;
|
|
26050
26120
|
return {
|
|
26051
26121
|
mentionAnswersDelta: answersDelta(mention),
|
|
26052
26122
|
citationAnswersDelta: answersDelta(citation),
|
|
26053
26123
|
denominatorChanged: baseChanged(mention) || baseChanged(citation),
|
|
26054
26124
|
withinNoise: changed && withinNoise,
|
|
26125
|
+
mentionWithinNoise: signalWithinNoise(mentionMove),
|
|
26126
|
+
citationWithinNoise: signalWithinNoise(citationMove),
|
|
26055
26127
|
bucket,
|
|
26056
26128
|
mentionSize: mentionMove === null ? -1 : Math.abs(mentionMove),
|
|
26057
26129
|
citationSize: citationMove === null ? -1 : Math.abs(citationMove)
|
|
@@ -26140,7 +26212,9 @@ function changesResponse(db, active, plan, query) {
|
|
|
26140
26212
|
mentionAnswersDelta: move.mentionAnswersDelta,
|
|
26141
26213
|
citationAnswersDelta: move.citationAnswersDelta,
|
|
26142
26214
|
denominatorChanged: move.denominatorChanged,
|
|
26143
|
-
withinNoise: move.withinNoise
|
|
26215
|
+
withinNoise: move.withinNoise,
|
|
26216
|
+
mentionWithinNoise: move.mentionWithinNoise,
|
|
26217
|
+
citationWithinNoise: move.citationWithinNoise
|
|
26144
26218
|
},
|
|
26145
26219
|
move
|
|
26146
26220
|
}];
|
|
@@ -26321,7 +26395,7 @@ async function measurementPortfolioReadRoutes(app) {
|
|
|
26321
26395
|
const raw = nested === "true" || nested === "false" ? { ...request.query, includeNestedMarkets: nested === "true" } : request.query;
|
|
26322
26396
|
const query = parseLimitQuery(raw, measurementPortfolioSummaryQuerySchema, "Invalid measurement portfolio summary query");
|
|
26323
26397
|
const { active, plan } = activeV2Plan2(app.db, project.id, "Portfolio summary");
|
|
26324
|
-
return portfolioResponse(app.db, active, plan, query);
|
|
26398
|
+
return portfolioResponse(app.db, active, plan, query, effectiveDomains(project));
|
|
26325
26399
|
}
|
|
26326
26400
|
);
|
|
26327
26401
|
app.get(
|
|
@@ -29708,6 +29782,15 @@ async function applyRoutes(app, opts) {
|
|
|
29708
29782
|
specProviders
|
|
29709
29783
|
);
|
|
29710
29784
|
const specDispatchModes = config.spec.providerDispatchModes === void 0 ? void 0 : validateProviderDispatchModes(config.spec.providerDispatchModes, opts?.providerAdapters);
|
|
29785
|
+
const specCompetitorNames = normalizeCompetitorList2(config.spec.competitors).map(competitorLabelFromDomain);
|
|
29786
|
+
const specQualifiedAliases = config.spec.qualifiedAliases === void 0 ? void 0 : requireQualifiedAliases(
|
|
29787
|
+
{
|
|
29788
|
+
displayName: config.spec.displayName,
|
|
29789
|
+
aliases: normalizeProjectAliases(config.spec.displayName, config.spec.aliases)
|
|
29790
|
+
},
|
|
29791
|
+
config.spec.qualifiedAliases,
|
|
29792
|
+
specCompetitorNames
|
|
29793
|
+
);
|
|
29711
29794
|
let resolvedSchedule = null;
|
|
29712
29795
|
let deleteSchedule = false;
|
|
29713
29796
|
if (config.spec.schedule) {
|
|
@@ -29766,6 +29849,11 @@ async function applyRoutes(app, opts) {
|
|
|
29766
29849
|
}
|
|
29767
29850
|
if (existing) {
|
|
29768
29851
|
projectId = existing.id;
|
|
29852
|
+
const qualifiedAliases = specQualifiedAliases ?? resolveProjectQualifiedAliases(
|
|
29853
|
+
{ displayName: config.spec.displayName, aliases: nextAliases },
|
|
29854
|
+
existing.qualifiedAliases,
|
|
29855
|
+
specCompetitorNames
|
|
29856
|
+
).value;
|
|
29769
29857
|
const providerDispatchModes = pruneProviderDispatchModes(
|
|
29770
29858
|
specDispatchModes ?? existing.providerDispatchModes,
|
|
29771
29859
|
specProviders,
|
|
@@ -29776,6 +29864,7 @@ async function applyRoutes(app, opts) {
|
|
|
29776
29864
|
canonicalDomain: config.spec.canonicalDomain,
|
|
29777
29865
|
ownedDomains: config.spec.ownedDomains ?? [],
|
|
29778
29866
|
aliases: nextAliases,
|
|
29867
|
+
qualifiedAliases,
|
|
29779
29868
|
country: config.spec.country,
|
|
29780
29869
|
language: config.spec.language,
|
|
29781
29870
|
labels: config.metadata.labels,
|
|
@@ -29807,6 +29896,7 @@ async function applyRoutes(app, opts) {
|
|
|
29807
29896
|
canonicalDomain: config.spec.canonicalDomain,
|
|
29808
29897
|
ownedDomains: config.spec.ownedDomains ?? [],
|
|
29809
29898
|
aliases: nextAliases,
|
|
29899
|
+
qualifiedAliases: specQualifiedAliases ?? [],
|
|
29810
29900
|
country: config.spec.country,
|
|
29811
29901
|
language: config.spec.language,
|
|
29812
29902
|
tags: [],
|
|
@@ -29951,6 +30041,7 @@ async function applyRoutes(app, opts) {
|
|
|
29951
30041
|
canonicalDomain: project.canonicalDomain,
|
|
29952
30042
|
ownedDomains: project.ownedDomains,
|
|
29953
30043
|
aliases: project.aliases,
|
|
30044
|
+
qualifiedAliases: project.qualifiedAliases,
|
|
29954
30045
|
country: project.country,
|
|
29955
30046
|
language: project.language,
|
|
29956
30047
|
tags: project.tags,
|
|
@@ -31284,8 +31375,11 @@ function buildRankedList(domains, limit, answerTotal, answersWithSources) {
|
|
|
31284
31375
|
const domainTotal = all.length;
|
|
31285
31376
|
all.sort((a, b) => b.count - a.count || a.domain.localeCompare(b.domain));
|
|
31286
31377
|
const shownEntries = limit != null && limit < all.length ? all.slice(0, limit) : all;
|
|
31287
|
-
|
|
31378
|
+
let rank = 0;
|
|
31379
|
+
const ranks = all.map((d, i) => rank = i > 0 && d.count === all[i - 1].count ? rank : i + 1);
|
|
31380
|
+
const entries = shownEntries.map((d, i) => ({
|
|
31288
31381
|
domain: d.domain,
|
|
31382
|
+
rank: ranks[i],
|
|
31289
31383
|
count: d.count,
|
|
31290
31384
|
percentage: totalCitedSlots > 0 ? roundRatio(d.count / totalCitedSlots, RatioUnits.fraction) : 0,
|
|
31291
31385
|
answerShare: answerTotal > 0 ? roundRatio(d.count / answerTotal, RatioUnits.fraction) : 0,
|
|
@@ -39426,6 +39520,7 @@ function formatProject2(row, negativeReviewMaxStars) {
|
|
|
39426
39520
|
canonicalDomain: row.canonicalDomain,
|
|
39427
39521
|
ownedDomains: row.ownedDomains,
|
|
39428
39522
|
aliases: row.aliases,
|
|
39523
|
+
qualifiedAliases: row.qualifiedAliases,
|
|
39429
39524
|
country: row.country,
|
|
39430
39525
|
language: row.language,
|
|
39431
39526
|
tags: row.tags,
|
|
@@ -86021,7 +86116,7 @@ var agentProvidersConfiguredCheck = {
|
|
|
86021
86116
|
status: CheckStatuses.warn,
|
|
86022
86117
|
code: "agent-providers.none-configured",
|
|
86023
86118
|
summary: "No agent LLM provider has credentials configured \u2014 the built-in Aero agent cannot run.",
|
|
86024
|
-
remediation: "Add a key for one of the agent providers (claude, openai, gemini, zai
|
|
86119
|
+
remediation: "Add a key for one of the agent providers (claude, openai, gemini, zai) under `providers.<name>.apiKey` in ~/.canonry/config.yaml, or export its env var (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY).",
|
|
86025
86120
|
details
|
|
86026
86121
|
};
|
|
86027
86122
|
}
|
|
@@ -87666,13 +87761,18 @@ async function discoveryRoutes(app, opts) {
|
|
|
87666
87761
|
createdAt: now
|
|
87667
87762
|
}).run();
|
|
87668
87763
|
}
|
|
87764
|
+
const droppedQualifiedAliases = promotedCompetitors.length ? pruneQualifiedAliasesForCompetitors(tx, project.id, now) : [];
|
|
87669
87765
|
writeAuditLog(tx, {
|
|
87670
87766
|
projectId: project.id,
|
|
87671
87767
|
actor: "api",
|
|
87672
87768
|
action: "discovery.promoted",
|
|
87673
87769
|
entityType: "discovery_session",
|
|
87674
87770
|
entityId: session.id,
|
|
87675
|
-
diff: {
|
|
87771
|
+
diff: {
|
|
87772
|
+
queries: promotedQueries,
|
|
87773
|
+
competitors: promotedCompetitors,
|
|
87774
|
+
...droppedQualifiedAliases.length ? { droppedQualifiedAliases } : {}
|
|
87775
|
+
}
|
|
87676
87776
|
});
|
|
87677
87777
|
});
|
|
87678
87778
|
}
|
|
@@ -90373,12 +90473,28 @@ function captureSimpleMeasurementDefinition(db, input) {
|
|
|
90373
90473
|
if (definition.queries.some((query) => query.queryClass !== (classifier?.classify(query.queryText) ?? null))) {
|
|
90374
90474
|
throw validationError("Query classes must match the captured identity and text.");
|
|
90375
90475
|
}
|
|
90476
|
+
if (definition.identity.qualifiedAliases !== void 0) {
|
|
90477
|
+
const expected = resolveProjectQualifiedAliases(
|
|
90478
|
+
definition.identity,
|
|
90479
|
+
definition.identity.qualifiedAliases,
|
|
90480
|
+
simpleMeasurementCompetitorNames(definition.competitors)
|
|
90481
|
+
).value;
|
|
90482
|
+
const captured = definition.identity.qualifiedAliases;
|
|
90483
|
+
if (captured.length !== expected.length || captured.some((name, index2) => name !== expected[index2])) {
|
|
90484
|
+
throw validationError("Qualified aliases must be a sorted subset of the captured aliases.");
|
|
90485
|
+
}
|
|
90486
|
+
}
|
|
90376
90487
|
const existing = tx.select().from(simpleMeasurementDefinitions).where(and68(eq81(simpleMeasurementDefinitions.projectId, input.projectId), eq81(simpleMeasurementDefinitions.runId, input.runId))).get();
|
|
90377
90488
|
if (existing) {
|
|
90378
90489
|
const frozen = simpleMeasurementDefinitionSchema.parse(existing.definition);
|
|
90490
|
+
const { qualifiedAliases: _liveQualifiedAliases, ...liveIdentity } = definition.identity;
|
|
90379
90491
|
const replayDefinition = {
|
|
90380
90492
|
...definition,
|
|
90381
|
-
capturedAt: frozen.capturedAt
|
|
90493
|
+
capturedAt: frozen.capturedAt,
|
|
90494
|
+
identity: {
|
|
90495
|
+
...liveIdentity,
|
|
90496
|
+
...frozen.identity.qualifiedAliases?.length ? { qualifiedAliases: frozen.identity.qualifiedAliases } : {}
|
|
90497
|
+
}
|
|
90382
90498
|
};
|
|
90383
90499
|
const replayChecksum = crypto51.createHash("sha256").update(canonicalSimpleMeasurementDefinitionJson(replayDefinition)).digest("hex");
|
|
90384
90500
|
if (existing.checksum !== replayChecksum) {
|