@canonry/canonry 6.0.0 → 6.1.2

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 (33) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +1 -1
  2. package/assets/assets/{AuditHistoryPanel-Bz2NVB6-.js → AuditHistoryPanel-D-W_kwEY.js} +1 -1
  3. package/assets/assets/{BacklinksPage-Dte5-usf.js → BacklinksPage-BeqCFRO3.js} +1 -1
  4. package/assets/assets/{HistoryPage-C1S4nw3I.js → HistoryPage-wAPbY0z4.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-DWjsHZdt.js → MeasurementPropertyPage-BSOrkHCB.js} +1 -1
  6. package/assets/assets/ProjectPage-v0C7CwyA.js +8 -0
  7. package/assets/assets/{RunsPage-D9wEJFfV.js → RunsPage-CxolaIps.js} +1 -1
  8. package/assets/assets/{SettingsPage-CN3Gew7Y.js → SettingsPage-BQqJz0Ms.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-Bft5TfoU.js → SiteHealthSection-C56kNcQQ.js} +3 -3
  10. package/assets/assets/{TrafficPage-tkNvySQH.js → TrafficPage-BDmWdYWf.js} +2 -2
  11. package/assets/assets/{TrafficSourceDetailPage-RhRfPizo.js → TrafficSourceDetailPage-DZl3YNBs.js} +1 -1
  12. package/assets/assets/{extract-error-message-tfl_SU7q.js → extract-error-message-D0mBVEGn.js} +1 -1
  13. package/assets/assets/index-C17E5ZcA.css +1 -0
  14. package/assets/assets/index-DZ__RzGa.js +86 -0
  15. package/assets/assets/{react-sigma_core.esm.min-BUu3mcQJ.js → react-sigma_core.esm.min-BQNp33-g.js} +1 -1
  16. package/assets/assets/{v2-overview-adapter-CzeGhaXn.js → v2-overview-adapter-XZbD7swG.js} +1 -1
  17. package/assets/index.html +3 -3
  18. package/dist/{chunk-F4G7I7T7.js → chunk-3QBRDFI5.js} +29 -14
  19. package/dist/{chunk-HAALQW4R.js → chunk-4MWLHTRS.js} +29 -2
  20. package/dist/{chunk-K4356AGP.js → chunk-6CR7DWLU.js} +13 -10
  21. package/dist/{chunk-6M7HSNXJ.js → chunk-HB6XZOZ6.js} +2 -2
  22. package/dist/{chunk-ZO62Z2WN.js → chunk-N7MRAN3G.js} +64 -26
  23. package/dist/{chunk-4NC5ZAC2.js → chunk-TBAVVIVI.js} +3 -3
  24. package/dist/{chunk-XGERCNUR.js → chunk-YOMLHHJS.js} +2 -2
  25. package/dist/cli.js +41 -14
  26. package/dist/{demo-server-54OUTZMY.js → demo-server-2622GU2H.js} +4 -4
  27. package/dist/index.js +6 -6
  28. package/dist/{intelligence-service-MEIAMMLT.js → intelligence-service-QUAOWEIA.js} +2 -2
  29. package/dist/mcp.js +4 -4
  30. package/package.json +15 -15
  31. package/assets/assets/ProjectPage-CAJ9XvNt.js +0 -8
  32. package/assets/assets/index-B11OXdEC.css +0 -1
  33. package/assets/assets/index-Cu_xgdb1.js +0 -86
@@ -869,7 +869,7 @@ import {
869
869
  wordpressSchemaDeployResultDtoSchema,
870
870
  wordpressSchemaStatusResultDtoSchema,
871
871
  wordpressStatusDtoSchema
872
- } from "./chunk-HAALQW4R.js";
872
+ } from "./chunk-4MWLHTRS.js";
873
873
 
874
874
  // src/intelligence-service.ts
875
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";
@@ -13868,6 +13868,7 @@ var DISCLOSURE = "Experimental English sentiment classification. Enabling sends
13868
13868
  var FULL_ROWS = { include: /* @__PURE__ */ new Set(["assessments", "locations"]), limit: null, after: null };
13869
13869
  var COMPACT_ROWS = { include: /* @__PURE__ */ new Set(), limit: null, after: null };
13870
13870
  var OPERATIONAL_OUTCOMES = /* @__PURE__ */ new Set([SentimentOutcomes.pending, SentimentOutcomes.running, SentimentOutcomes["waiting-to-retry"], SentimentOutcomes.failed, SentimentOutcomes.canceled]);
13871
+ var RATED_OUTCOMES = /* @__PURE__ */ new Set([SentimentOutcomes.favorable, SentimentOutcomes.mixed, SentimentOutcomes.unfavorable]);
13871
13872
  var SentimentService = class {
13872
13873
  constructor(db, options) {
13873
13874
  this.db = db;
@@ -14108,10 +14109,15 @@ var SentimentService = class {
14108
14109
  return [{ ...item2, input, evidence }];
14109
14110
  });
14110
14111
  }
14111
- aggregate(selection, items, source, settings, rows) {
14112
+ /**
14113
+ * `ratedPopulation` is the stored items whose rated assessments count toward every row's Rated
14114
+ * share. It defaults to `items`; a comparison scores only its matched units but passes the whole
14115
+ * period, so each period's Rated share is the one its own summary read shows.
14116
+ */
14117
+ aggregate(selection, items, source, settings, rows, ratedPopulation = items) {
14112
14118
  const definition = selection.evaluationDefinitionId ? this.definition(selection.evaluationDefinitionId) : null;
14113
14119
  const disabled = !settings.enabled || !settings.installEnabled;
14114
- const options = { disabled, incomplete: Boolean(source.skipped["incomplete-run"]) };
14120
+ const options = { disabled, incomplete: Boolean(source.skipped["incomplete-run"]), rated: ratedAssessments(ratedPopulation) };
14115
14121
  const base = { ...scoreAssessments(items, source.assessments, { ...options, slots: source.sourceCoverage }), configured: settings.enabled && settings.installEnabled, selection, evaluationDefinition: definition };
14116
14122
  if (!rows) return { summary: { ...base, breakdowns: [] }, queries: [], total: 0, next: null };
14117
14123
  const filter = sourceFilter(selection);
@@ -14265,8 +14271,8 @@ var SentimentService = class {
14265
14271
  const fromUnits = units(fromItems), toUnits = units(toItems);
14266
14272
  const common = new Set([...fromUnits.byUnit.keys()].filter((key) => toUnits.byUnit.has(key)));
14267
14273
  const settings = this.settings(projectId);
14268
- const fromSummary = withoutNodeKeys(this.aggregate(from.selection, fromItems.filter((item2) => common.has(fromUnits.byItem.get(item2))), from.source, settings, COMPACT_ROWS));
14269
- const toSummary = withoutNodeKeys(this.aggregate(to.selection, toItems.filter((item2) => common.has(toUnits.byItem.get(item2))), to.source, settings, COMPACT_ROWS));
14274
+ const fromSummary = withoutNodeKeys(this.aggregate(from.selection, fromItems.filter((item2) => common.has(fromUnits.byItem.get(item2))), from.source, settings, COMPACT_ROWS, fromItems));
14275
+ const toSummary = withoutNodeKeys(this.aggregate(to.selection, toItems.filter((item2) => common.has(toUnits.byItem.get(item2))), to.source, settings, COMPACT_ROWS, toItems));
14270
14276
  const refusalReasons = [];
14271
14277
  const expectedFrom = from.source.assessments.length, expectedTo = to.source.assessments.length;
14272
14278
  if (fromItems.length < expectedFrom || toItems.length < expectedTo) refusalReasons.push("classification-coverage-gap");
@@ -14315,7 +14321,6 @@ function incompatibleSelectionReason(selections, assessments, filter) {
14315
14321
  return reason;
14316
14322
  }
14317
14323
  function overallSentiment(reads, disabled) {
14318
- const assessmentKey = (item2) => JSON.stringify([item2.snapshotId, item2.subject.key]);
14319
14324
  const assessments = reads.flatMap((read) => read.source.assessments);
14320
14325
  const eligible = [...new Map(assessments.map((item2) => [assessmentKey(item2), item2])).values()];
14321
14326
  const selected = [...new Map(reads.flatMap((read) => read.items).map((item2) => [assessmentKey(item2.member), item2])).values()];
@@ -14353,12 +14358,20 @@ function compareRows(left, right) {
14353
14358
  function withheldCounts(counts2) {
14354
14359
  return Object.fromEntries(Object.entries(counts2).map(([key, value]) => [key, OPERATIONAL_OUTCOMES.has(key) ? value : 0]));
14355
14360
  }
14361
+ function assessmentKey(item2) {
14362
+ return JSON.stringify([item2.snapshotId, item2.subject.key]);
14363
+ }
14364
+ function ratedAssessments(items) {
14365
+ return new Set(items.filter((item2) => RATED_OUTCOMES.has(item2.outcome)).map((item2) => assessmentKey(item2.member)));
14366
+ }
14356
14367
  function scoreAssessments(selected, eligible, options) {
14357
- const distinct = new Set(eligible.map((item2) => item2.snapshotId)).size;
14358
- const slots = options.slots ?? { expected: distinct, completed: distinct };
14359
- const result = aggregateSentiment(selected.map((item2) => ({ assessmentId: item2.work.id, sourceSnapshotId: item2.work.snapshotId, outcome: item2.outcome })), { disabled: options.disabled, eligibleAssessments: eligible.length, expectedProviderSlots: slots.expected, completedProviderSlots: slots.completed });
14368
+ const eligibleAnswers = new Set(eligible.map((item2) => item2.snapshotId)).size;
14369
+ const rated = options.rated ?? ratedAssessments(selected);
14370
+ const ratedAnswers = new Set(eligible.filter((item2) => rated.has(assessmentKey(item2))).map((item2) => item2.snapshotId)).size;
14371
+ const slots = options.slots ?? { expected: eligibleAnswers, completed: eligibleAnswers };
14372
+ const result = aggregateSentiment(selected.map((item2) => ({ assessmentId: item2.work.id, sourceSnapshotId: item2.work.snapshotId, outcome: item2.outcome })), { disabled: options.disabled, eligibleAssessments: eligible.length, eligibleAnswers, ratedAnswers, expectedProviderSlots: slots.expected, completedProviderSlots: slots.completed });
14360
14373
  const gap = selected.length > 0 && selected.length < eligible.length;
14361
- const coverage = options.disabled ? { ...result.coverage, judged: 0, counts: withheldCounts(result.coverage.counts) } : result.coverage;
14374
+ const coverage = options.disabled ? { ...result.coverage, judged: 0, ratedAnswers: 0, counts: withheldCounts(result.coverage.counts) } : result.coverage;
14362
14375
  return { ...result, coverage, ...(gap || options.incomplete) && !options.disabled ? { state: selected.length ? "partial" : "not-measured", provisional: true } : {}, reason: options.disabled ? "Sentiment is disabled." : options.incomplete ? "Source sweep is incomplete." : gap ? "classification-coverage-gap: some source assessments have not been admitted." : null };
14363
14376
  }
14364
14377
  function jobReceipt(row, tally, attempts) {
@@ -46904,7 +46917,7 @@ var routeCatalog = [
46904
46917
  method: "get",
46905
46918
  path: "/api/v1/projects/{name}/technical-aeo/crawl",
46906
46919
  summary: "Get persisted Technical AEO crawl metadata",
46907
- description: "Returns the latest complete non-probe site-audit crawl, or the selected historical run (which may be partial). `hasCrawlData=false` never synthesizes a graph from legacy scorecard rows; `legacyAuditAvailable` says that the old score/pages/trend data can still be read separately.",
46920
+ description: "Returns the newest non-probe site-audit crawl, or the selected historical run. The default is the newest scan that published a crawl, whether it finished (`completed`) or stopped at its page, edge, depth, or duration budget (`partial`); `complete` and `termination` say which. Every crawl-scoped read without a `runId` resolves this same scan. `hasCrawlData=false` never synthesizes a graph from legacy scorecard rows; `legacyAuditAvailable` says that the old score/pages/trend data can still be read separately.",
46908
46921
  tags: ["technical-aeo"],
46909
46922
  parameters: [
46910
46923
  nameParameter,
@@ -46919,11 +46932,11 @@ var routeCatalog = [
46919
46932
  method: "get",
46920
46933
  path: "/api/v1/projects/{name}/technical-aeo/graph",
46921
46934
  summary: "Get a persisted Site Health graph projection",
46922
- description: "Returns the deterministic graph projection computed once when the latest complete or selected historical crawl was published. ForceAtlas2 positions and the exact internal-anchor edge sample are persisted, so reads run no layout physics and never rescan the crawl edge table. Nav, header, and footer links are excluded from the layout physics but retained in the sample and tagged `isTemplate`, so a viewer can draw them without a refetch and without any node moving. The response is bounded to 20,000 nodes / 50,000 edges; `layout`, `omittedNodes`, `omittedEdges`, and `sampled` disclose legacy/unavailable layouts and intentional truncation, and `templateDetection` says whether template links could be told apart at all and by which rule.",
46935
+ description: "Returns the deterministic graph projection computed once when the latest or selected historical crawl was published. ForceAtlas2 positions and the exact internal-anchor edge sample are persisted, so reads run no layout physics and never rescan the crawl edge table. Nav, header, and footer links are excluded from the layout physics but retained in the sample and tagged `isTemplate`, so a viewer can draw them without a refetch and without any node moving. The response is bounded to 20,000 nodes / 50,000 edges; `layout`, `omittedNodes`, `omittedEdges`, and `sampled` disclose legacy/unavailable layouts and intentional truncation, and `templateDetection` says whether template links could be told apart at all and by which rule.",
46923
46936
  tags: ["technical-aeo"],
46924
46937
  parameters: [
46925
46938
  nameParameter,
46926
- { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest complete crawl.", schema: stringSchema },
46939
+ { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest crawl.", schema: stringSchema },
46927
46940
  { name: "maxNodes", in: "query", description: "Maximum graph nodes. Defaults to and is capped at 20,000.", schema: { type: "integer", minimum: 1, maximum: 2e4 } },
46928
46941
  { name: "maxEdges", in: "query", description: "Maximum graph edges. Defaults to and is capped at 50,000.", schema: { type: "integer", minimum: 1, maximum: 5e4 } },
46929
46942
  linkKindParameter
@@ -46941,7 +46954,7 @@ var routeCatalog = [
46941
46954
  tags: ["technical-aeo"],
46942
46955
  parameters: [
46943
46956
  nameParameter,
46944
- { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest complete crawl.", schema: stringSchema },
46957
+ { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest crawl.", schema: stringSchema },
46945
46958
  { name: "nodeKey", in: "query", description: "Focus page canonical node key. Omit nodeKey and url to use the crawl root.", schema: stringSchema },
46946
46959
  { name: "url", in: "query", description: "Focus page canonical URL. Omit nodeKey and url to use the crawl root.", schema: stringSchema },
46947
46960
  { name: "hops", in: "query", description: "Undirected neighborhood radius. Defaults to 1; maximum 3.", schema: { type: "integer", minimum: 0, maximum: 3 } },
@@ -46961,7 +46974,7 @@ var routeCatalog = [
46961
46974
  tags: ["technical-aeo"],
46962
46975
  parameters: [
46963
46976
  nameParameter,
46964
- { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest complete crawl.", schema: stringSchema },
46977
+ { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest crawl.", schema: stringSchema },
46965
46978
  { name: "fromNodeKey", in: "query", description: "Source canonical node key. Omit both source selectors to start at the crawl root.", schema: stringSchema },
46966
46979
  { name: "fromUrl", in: "query", description: "Source canonical URL. Omit both source selectors to start at the crawl root.", schema: stringSchema },
46967
46980
  { name: "toNodeKey", in: "query", description: "Target canonical node key. Required when toUrl is omitted.", schema: stringSchema },
@@ -46978,7 +46991,7 @@ var routeCatalog = [
46978
46991
  method: "get",
46979
46992
  path: "/api/v1/projects/{name}/technical-aeo/changes",
46980
46993
  summary: "Compare canonical Site Health snapshots",
46981
- description: "Returns exact page and internal-link additions, removals, and semantic field changes between immutable complete crawl snapshots. Omitted toRunId selects the latest complete crawl; omitted fromRunId selects the complete crawl immediately before the target. ForceAtlas2 positions are presentation data and never count as a change. The first page includes exact post-filter summary counts; cursor pages set summary and total to null so they never repeat full snapshot joins or trust caller-carried counts. Results use a snapshot- and filter-bound keyset cursor.",
46994
+ description: "Returns exact page and internal-link additions, removals, and semantic field changes between immutable complete crawl snapshots. Omitted toRunId selects the latest complete crawl; omitted fromRunId selects the complete crawl immediately before the target. A crawl stopped at its budget is never compared: when every crawl is partial, the result is `partial-not-comparable` with the newest crawl as `toRunId`, and `no-crawl` means no scan published a crawl. ForceAtlas2 positions are presentation data and never count as a change. The first page includes exact post-filter summary counts; cursor pages set summary and total to null so they never repeat full snapshot joins or trust caller-carried counts. Results use a snapshot- and filter-bound keyset cursor.",
46982
46995
  tags: ["technical-aeo"],
46983
46996
  parameters: [
46984
46997
  nameParameter,
@@ -47003,7 +47016,7 @@ var routeCatalog = [
47003
47016
  tags: ["technical-aeo"],
47004
47017
  parameters: [
47005
47018
  nameParameter,
47006
- { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest complete crawl.", schema: stringSchema },
47019
+ { name: "runId", in: "query", description: "Historical site-audit run ID. Omit for the latest crawl.", schema: stringSchema },
47007
47020
  { name: "nodeKey", in: "query", description: "Exact canonical crawl node key. Required when url is omitted.", schema: stringSchema },
47008
47021
  { name: "url", in: "query", description: "Exact persisted crawl URL. Required when nodeKey is omitted.", schema: stringSchema }
47009
47022
  ],
@@ -88438,14 +88451,15 @@ async function technicalAeoRoutes(app, opts) {
88438
88451
  inArray31(runs.status, SURFACEABLE_STATUSES),
88439
88452
  notProbeRun()
88440
88453
  ];
88441
- if (runId) {
88442
- filters.push(eq78(siteCrawlSnapshots.runId, runId));
88443
- } else {
88444
- filters.push(eq78(siteCrawlSnapshots.complete, true), eq78(runs.status, RunStatuses.completed));
88445
- }
88454
+ if (runId) filters.push(eq78(siteCrawlSnapshots.runId, runId));
88446
88455
  return app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq78(siteCrawlSnapshots.runId, runs.id)).where(and66(...filters)).orderBy(desc34(siteCrawlSnapshots.createdAt), desc34(siteCrawlSnapshots.runId)).limit(1).get();
88447
88456
  };
88448
88457
  const detailScopeFor = (projectId, snapshot) => snapshot.detailsAvailable && snapshot.attemptId ? { projectId, runId: snapshot.runId, attemptId: snapshot.attemptId } : null;
88458
+ const completenessOf = (snapshot) => ({
88459
+ complete: snapshot.complete,
88460
+ termination: snapshot.termination
88461
+ });
88462
+ const NO_CRAWL_COMPLETENESS = { complete: false, termination: null };
88449
88463
  const pageInScope = (scope, selector) => {
88450
88464
  if (!selector.nodeKey && !selector.url) return null;
88451
88465
  return app.db.select().from(siteCrawlPages).where(and66(
@@ -88633,6 +88647,7 @@ async function technicalAeoRoutes(app, opts) {
88633
88647
  project: project.name,
88634
88648
  hasCrawlData: false,
88635
88649
  runId: null,
88650
+ ...NO_CRAWL_COMPLETENESS,
88636
88651
  rootNodeKey: null,
88637
88652
  layout: { state: "unavailable", version: null, reason: "no-crawl" },
88638
88653
  // Nothing was classified because nothing was crawled.
@@ -88656,6 +88671,7 @@ async function technicalAeoRoutes(app, opts) {
88656
88671
  project: project.name,
88657
88672
  hasCrawlData: true,
88658
88673
  runId: snapshot.runId,
88674
+ ...completenessOf(snapshot),
88659
88675
  rootNodeKey: null,
88660
88676
  layout: { state: "unavailable", version: null, reason: "details-unavailable" },
88661
88677
  templateDetection,
@@ -88685,6 +88701,7 @@ async function technicalAeoRoutes(app, opts) {
88685
88701
  project: project.name,
88686
88702
  hasCrawlData: true,
88687
88703
  runId: snapshot.runId,
88704
+ ...completenessOf(snapshot),
88688
88705
  rootNodeKey,
88689
88706
  layout: { state: "unavailable", version: null, reason: "legacy-snapshot" },
88690
88707
  templateDetection,
@@ -88707,6 +88724,7 @@ async function technicalAeoRoutes(app, opts) {
88707
88724
  project: project.name,
88708
88725
  hasCrawlData: true,
88709
88726
  runId: snapshot.runId,
88727
+ ...completenessOf(snapshot),
88710
88728
  rootNodeKey,
88711
88729
  layout: {
88712
88730
  state: "unavailable",
@@ -88796,6 +88814,7 @@ async function technicalAeoRoutes(app, opts) {
88796
88814
  project: project.name,
88797
88815
  hasCrawlData: true,
88798
88816
  runId: snapshot.runId,
88817
+ ...completenessOf(snapshot),
88799
88818
  rootNodeKey,
88800
88819
  layout: {
88801
88820
  state: "ready",
@@ -89140,6 +89159,16 @@ async function technicalAeoRoutes(app, opts) {
89140
89159
  )).orderBy(desc34(siteCrawlSnapshots.createdAt), desc34(siteCrawlSnapshots.runId)).limit(1).get() : void 0;
89141
89160
  if (request.query.fromRunId && !beforeTarget) throw notFound("Site crawl run", request.query.fromRunId);
89142
89161
  if (!afterTarget) {
89162
+ const latestCrawl = resolveCrawl(project.id);
89163
+ if (latestCrawl) {
89164
+ return {
89165
+ project: project.name,
89166
+ state: "unavailable",
89167
+ reason: "partial-not-comparable",
89168
+ fromRunId: null,
89169
+ toRunId: latestCrawl.snapshot.runId
89170
+ };
89171
+ }
89143
89172
  return { project: project.name, state: "unavailable", reason: "no-crawl", fromRunId: null, toRunId: null };
89144
89173
  }
89145
89174
  if (!beforeTarget) {
@@ -89497,11 +89526,11 @@ async function technicalAeoRoutes(app, opts) {
89497
89526
  const target = resolveCrawl(project.id, request.query.runId);
89498
89527
  if (!target) {
89499
89528
  assertKnownAuditRun(project.id, request.query.runId);
89500
- return { project: project.name, hasCrawlData: false, runId: null, total: 0, nextCursor: null, healthStateFilter: null, pages: [] };
89529
+ return { project: project.name, hasCrawlData: false, runId: null, ...NO_CRAWL_COMPLETENESS, total: 0, nextCursor: null, healthStateFilter: null, pages: [] };
89501
89530
  }
89502
89531
  const snapshot = target.snapshot;
89503
89532
  if (!snapshot.detailsAvailable || !snapshot.attemptId) {
89504
- return { project: project.name, hasCrawlData: true, runId: snapshot.runId, total: 0, nextCursor: null, healthStateFilter: null, pages: [] };
89533
+ return { project: project.name, hasCrawlData: true, runId: snapshot.runId, ...completenessOf(snapshot), total: 0, nextCursor: null, healthStateFilter: null, pages: [] };
89505
89534
  }
89506
89535
  const filters = [
89507
89536
  eq78(siteCrawlPages.projectId, project.id),
@@ -89534,6 +89563,7 @@ async function technicalAeoRoutes(app, opts) {
89534
89563
  project: project.name,
89535
89564
  hasCrawlData: true,
89536
89565
  runId: snapshot.runId,
89566
+ ...completenessOf(snapshot),
89537
89567
  total: 0,
89538
89568
  nextCursor: null,
89539
89569
  healthStateFilter,
@@ -89548,6 +89578,7 @@ async function technicalAeoRoutes(app, opts) {
89548
89578
  project: project.name,
89549
89579
  hasCrawlData: true,
89550
89580
  runId: snapshot.runId,
89581
+ ...completenessOf(snapshot),
89551
89582
  total,
89552
89583
  nextCursor: nextOffset < total ? encodeCursor2(nextOffset) : null,
89553
89584
  healthStateFilter,
@@ -89560,11 +89591,11 @@ async function technicalAeoRoutes(app, opts) {
89560
89591
  const target = resolveCrawl(project.id, request.query.runId);
89561
89592
  if (!target) {
89562
89593
  assertKnownAuditRun(project.id, request.query.runId);
89563
- return { project: project.name, hasCrawlData: false, runId: null, parentPath, nextCursor: null, children: [] };
89594
+ return { project: project.name, hasCrawlData: false, runId: null, ...NO_CRAWL_COMPLETENESS, parentPath, nextCursor: null, children: [] };
89564
89595
  }
89565
89596
  const snapshot = target.snapshot;
89566
89597
  if (!snapshot.detailsAvailable || !snapshot.attemptId) {
89567
- return { project: project.name, hasCrawlData: true, runId: snapshot.runId, parentPath, nextCursor: null, children: [] };
89598
+ return { project: project.name, hasCrawlData: true, runId: snapshot.runId, ...completenessOf(snapshot), parentPath, nextCursor: null, children: [] };
89568
89599
  }
89569
89600
  const sourceRows = app.db.select({
89570
89601
  path: siteCrawlPages.path,
@@ -89609,6 +89640,7 @@ async function technicalAeoRoutes(app, opts) {
89609
89640
  project: project.name,
89610
89641
  hasCrawlData: true,
89611
89642
  runId: snapshot.runId,
89643
+ ...completenessOf(snapshot),
89612
89644
  parentPath,
89613
89645
  nextCursor: hasMore ? encodeCursor2(offset + visible.length) : null,
89614
89646
  children: visible.map(([path8, child]) => ({
@@ -89631,6 +89663,7 @@ async function technicalAeoRoutes(app, opts) {
89631
89663
  project: project.name,
89632
89664
  hasCrawlData: false,
89633
89665
  runId: null,
89666
+ ...NO_CRAWL_COMPLETENESS,
89634
89667
  total: 0,
89635
89668
  nextCursor: null,
89636
89669
  templateDetection: SiteHealthTemplateDetections["unavailable-legacy-scan"],
@@ -89645,6 +89678,7 @@ async function technicalAeoRoutes(app, opts) {
89645
89678
  project: project.name,
89646
89679
  hasCrawlData: true,
89647
89680
  runId: snapshot.runId,
89681
+ ...completenessOf(snapshot),
89648
89682
  total: 0,
89649
89683
  nextCursor: null,
89650
89684
  templateDetection,
@@ -89673,6 +89707,7 @@ async function technicalAeoRoutes(app, opts) {
89673
89707
  project: project.name,
89674
89708
  hasCrawlData: true,
89675
89709
  runId: snapshot.runId,
89710
+ ...completenessOf(snapshot),
89676
89711
  total,
89677
89712
  nextCursor: nextOffset < total ? encodeCursor2(nextOffset) : null,
89678
89713
  templateDetection,
@@ -89693,6 +89728,7 @@ async function technicalAeoRoutes(app, opts) {
89693
89728
  project: project.name,
89694
89729
  hasCrawlData: false,
89695
89730
  runId: null,
89731
+ ...NO_CRAWL_COMPLETENESS,
89696
89732
  nodeKey: request.query.nodeKey ?? null,
89697
89733
  url: request.query.url ?? null,
89698
89734
  templateDetection: SiteHealthTemplateDetections["unavailable-legacy-scan"],
@@ -89710,6 +89746,7 @@ async function technicalAeoRoutes(app, opts) {
89710
89746
  project: project.name,
89711
89747
  hasCrawlData: true,
89712
89748
  runId: snapshot.runId,
89749
+ ...completenessOf(snapshot),
89713
89750
  nodeKey: request.query.nodeKey ?? null,
89714
89751
  url: request.query.url ?? null,
89715
89752
  templateDetection,
@@ -89736,6 +89773,7 @@ async function technicalAeoRoutes(app, opts) {
89736
89773
  project: project.name,
89737
89774
  hasCrawlData: true,
89738
89775
  runId: snapshot.runId,
89776
+ ...completenessOf(snapshot),
89739
89777
  nodeKey: request.query.nodeKey ?? null,
89740
89778
  url: request.query.url ?? null,
89741
89779
  templateDetection,
@@ -4,14 +4,14 @@ import {
4
4
  installSkills,
5
5
  loadConfigRaw,
6
6
  saveConfigPatch
7
- } from "./chunk-XGERCNUR.js";
7
+ } from "./chunk-YOMLHHJS.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
- } from "./chunk-K4356AGP.js";
10
+ } from "./chunk-6CR7DWLU.js";
11
11
  import {
12
12
  SKILL_MANIFEST_FILENAME,
13
13
  SkillsClients
14
- } from "./chunk-HAALQW4R.js";
14
+ } from "./chunk-4MWLHTRS.js";
15
15
 
16
16
  // src/skills-autosync.ts
17
17
  import fs from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-K4356AGP.js";
8
+ } from "./chunk-6CR7DWLU.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -50,7 +50,7 @@ import {
50
50
  skillsClientSchema,
51
51
  upgradeCaveatFor,
52
52
  upgradeCommandFor
53
- } from "./chunk-HAALQW4R.js";
53
+ } from "./chunk-4MWLHTRS.js";
54
54
 
55
55
  // src/config.ts
56
56
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -35,11 +35,11 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-F4G7I7T7.js";
38
+ } from "./chunk-3QBRDFI5.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
42
- } from "./chunk-4NC5ZAC2.js";
42
+ } from "./chunk-TBAVVIVI.js";
43
43
  import {
44
44
  configExists,
45
45
  createApiClient,
@@ -56,13 +56,13 @@ import {
56
56
  saveConfig,
57
57
  saveConfigPatch,
58
58
  shouldPrintServeSkillsNudge
59
- } from "./chunk-XGERCNUR.js";
59
+ } from "./chunk-YOMLHHJS.js";
60
60
  import {
61
61
  DEFAULT_DEMO_TRUSTED_PROXIES,
62
62
  trustedProxiesProblem,
63
63
  trustedProxyProblem
64
64
  } from "./chunk-RB2F4D3Z.js";
65
- import "./chunk-6M7HSNXJ.js";
65
+ import "./chunk-HB6XZOZ6.js";
66
66
  import {
67
67
  CliError,
68
68
  EXIT_SYSTEM_ERROR,
@@ -74,7 +74,7 @@ import {
74
74
  runMeasurementDraftAction,
75
75
  systemError,
76
76
  usageError
77
- } from "./chunk-K4356AGP.js";
77
+ } from "./chunk-6CR7DWLU.js";
78
78
  import {
79
79
  CLOUDFLARE_WORKER_BINDINGS,
80
80
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -89,7 +89,7 @@ import {
89
89
  queries,
90
90
  renderReportHtml,
91
91
  runs
92
- } from "./chunk-ZO62Z2WN.js";
92
+ } from "./chunk-N7MRAN3G.js";
93
93
  import {
94
94
  AdsDeliverySnapshotStatuses,
95
95
  AdsHistoricalCampaignRollupStatuses,
@@ -196,7 +196,7 @@ import {
196
196
  visibilityCompareSelectionSchema,
197
197
  visibilityReportRequestSchema,
198
198
  winnabilityClassSchema
199
- } from "./chunk-HAALQW4R.js";
199
+ } from "./chunk-4MWLHTRS.js";
200
200
 
201
201
  // src/cli.ts
202
202
  import { pathToFileURL } from "url";
@@ -461,13 +461,21 @@ async function showSentiment(project2, selection2, format) {
461
461
  if (machine(value, format)) return;
462
462
  printSummary(value);
463
463
  }
464
+ function ratedAnswersLine({ ratedAnswers, eligibleAnswers, ratedAnswerRate }) {
465
+ if (ratedAnswers === void 0 || eligibleAnswers === void 0) return null;
466
+ if (eligibleAnswers === 0) return "Rated: no answers yet";
467
+ if (ratedAnswerRate === null || ratedAnswerRate === void 0) return `Rated: Unavailable \xB7 ${eligibleAnswers} eligible ${eligibleAnswers === 1 ? "answer" : "answers"}`;
468
+ return `Rated: ${formatPercent(ratedAnswerRate, RatioUnits.fraction)} \xB7 ${ratedAnswers} of ${eligibleAnswers} ${eligibleAnswers === 1 ? "answer" : "answers"} rated`;
469
+ }
464
470
  function printSummary(value) {
465
471
  const label = value.selection.queryClass === "branded" ? "Branded" : "Non-brand";
466
472
  console.log(`${label} sentiment: ${value.state}${value.provisional ? " \xB7 provisional" : ""}`);
467
473
  if (value.reason) console.log(value.reason);
468
474
  console.log(`Favorable: ${value.score.favorableDisplay} \xB7 Mixed: ${value.score.mixedDisplay} \xB7 Unfavorable: ${value.score.unfavorableDisplay}`);
469
475
  console.log(`${label} favorable % = favorable / (favorable + mixed + unfavorable).`);
470
- console.log(`${value.coverage.judged} of ${value.coverage.selected} assessments judged \xB7 ${value.coverage.distinctSourceAnswers} distinct source answers`);
476
+ const rated = ratedAnswersLine(value.coverage);
477
+ if (rated) console.log(rated);
478
+ console.log(`${value.coverage.judged} of ${value.coverage.selected} assessments judged \xB7 ${value.coverage.distinctSourceAnswers} admitted source answers`);
471
479
  console.log(`${value.coverage.eligibleAssessments} eligible assessments \xB7 ${value.coverage.unadmittedAssessments} not yet admitted`);
472
480
  console.log(`Source provider slots: ${value.coverage.completedProviderSlots} of ${value.coverage.expectedProviderSlots}`);
473
481
  console.log(`Evaluation definition: ${value.selection.evaluationDefinitionId ?? "not measured"}`);
@@ -722,7 +730,7 @@ function parseDemoTrustedProxies(values) {
722
730
  async function demoCommand(options) {
723
731
  const { host, port } = parseDemoListenOptions(options);
724
732
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
725
- const { createDemoServer } = await import("./demo-server-54OUTZMY.js");
733
+ const { createDemoServer } = await import("./demo-server-2622GU2H.js");
726
734
  const app = await createDemoServer({ trustProxy });
727
735
  let stopping = false;
728
736
  const stop = () => {
@@ -17348,6 +17356,10 @@ function formatSiteHealthChangeRecord(record) {
17348
17356
  const fields = record.changedFields.length > 0 ? ` (${record.changedFields.join(", ")})` : "";
17349
17357
  return `${record.entity} ${record.change}: ${target}${fields}`;
17350
17358
  }
17359
+ function partialCrawlNote(res) {
17360
+ if (!res.hasCrawlData || res.complete !== false) return null;
17361
+ return `Partial crawl (${res.termination ?? "incomplete"}): pages and links beyond the crawl budget were not observed, so a missing page or link is not proof it does not exist.`;
17362
+ }
17351
17363
  async function technicalAeoCrawlPages(project2, opts) {
17352
17364
  const res = await getClient27().getTechnicalAeoCrawlPages(project2, opts);
17353
17365
  if (opts.format === "jsonl") {
@@ -17355,6 +17367,8 @@ async function technicalAeoCrawlPages(project2, opts) {
17355
17367
  kind: "technical-aeo-crawl-pages-header",
17356
17368
  project: project2,
17357
17369
  runId: res.runId,
17370
+ complete: res.complete,
17371
+ termination: res.termination,
17358
17372
  total: res.total,
17359
17373
  nextCursor: res.nextCursor
17360
17374
  }]);
@@ -17365,8 +17379,9 @@ async function technicalAeoCrawlPages(project2, opts) {
17365
17379
  console.log(JSON.stringify(res, null, 2));
17366
17380
  return;
17367
17381
  }
17382
+ const note = partialCrawlNote(res);
17368
17383
  if (res.pages.length === 0) {
17369
- console.log(`No persisted crawl pages for "${project2}".`);
17384
+ console.log([`No persisted crawl pages for "${project2}".`, ...note ? [note] : []].join("\n"));
17370
17385
  return;
17371
17386
  }
17372
17387
  const lines = [`${res.pages.length} of ${res.total} crawl page(s)${res.nextCursor ? " (more available)" : ""}:`, ""];
@@ -17376,6 +17391,7 @@ async function technicalAeoCrawlPages(project2, opts) {
17376
17391
  }
17377
17392
  if (res.nextCursor) lines.push(`
17378
17393
  Next cursor: ${res.nextCursor}`);
17394
+ if (note) lines.push("", note);
17379
17395
  console.log(lines.join("\n"));
17380
17396
  }
17381
17397
  async function technicalAeoPageAudit(project2, opts) {
@@ -17445,6 +17461,8 @@ async function technicalAeoStructure(project2, opts) {
17445
17461
  kind: "technical-aeo-structure-header",
17446
17462
  project: project2,
17447
17463
  runId: res.runId,
17464
+ complete: res.complete,
17465
+ termination: res.termination,
17448
17466
  parentPath: res.parentPath,
17449
17467
  returned: res.children.length,
17450
17468
  nextCursor: res.nextCursor
@@ -17456,8 +17474,9 @@ async function technicalAeoStructure(project2, opts) {
17456
17474
  console.log(JSON.stringify(res, null, 2));
17457
17475
  return;
17458
17476
  }
17477
+ const note = partialCrawlNote(res);
17459
17478
  if (res.children.length === 0) {
17460
- console.log(`No crawl structure below ${res.parentPath} for "${project2}".`);
17479
+ console.log([`No crawl structure below ${res.parentPath} for "${project2}".`, ...note ? [note] : []].join("\n"));
17461
17480
  return;
17462
17481
  }
17463
17482
  const lines = [`${res.children.length} child path(s) below ${res.parentPath}${res.nextCursor ? " (more available)" : ""}:`, ""];
@@ -17466,6 +17485,7 @@ async function technicalAeoStructure(project2, opts) {
17466
17485
  }
17467
17486
  if (res.nextCursor) lines.push(`
17468
17487
  Next cursor: ${res.nextCursor}`);
17488
+ if (note) lines.push("", note);
17469
17489
  console.log(lines.join("\n"));
17470
17490
  }
17471
17491
  async function technicalAeoInternalLinks(project2, opts) {
@@ -17475,6 +17495,8 @@ async function technicalAeoInternalLinks(project2, opts) {
17475
17495
  kind: "technical-aeo-internal-links-header",
17476
17496
  project: project2,
17477
17497
  runId: res.runId,
17498
+ complete: res.complete,
17499
+ termination: res.termination,
17478
17500
  total: res.total,
17479
17501
  nextCursor: res.nextCursor
17480
17502
  }]);
@@ -17485,8 +17507,9 @@ async function technicalAeoInternalLinks(project2, opts) {
17485
17507
  console.log(JSON.stringify(res, null, 2));
17486
17508
  return;
17487
17509
  }
17510
+ const note = partialCrawlNote(res);
17488
17511
  if (res.edges.length === 0) {
17489
- console.log(`No persisted internal links for "${project2}".`);
17512
+ console.log([`No persisted internal links for "${project2}".`, ...note ? [note] : []].join("\n"));
17490
17513
  return;
17491
17514
  }
17492
17515
  const lines = [`${res.edges.length} of ${res.total} internal link(s)${res.nextCursor ? " (more available)" : ""}:`, ""];
@@ -17495,6 +17518,7 @@ async function technicalAeoInternalLinks(project2, opts) {
17495
17518
  }
17496
17519
  if (res.nextCursor) lines.push(`
17497
17520
  Next cursor: ${res.nextCursor}`);
17521
+ if (note) lines.push("", note);
17498
17522
  console.log(lines.join("\n"));
17499
17523
  }
17500
17524
  async function technicalAeoLinkNeighbors(project2, opts) {
@@ -17504,14 +17528,17 @@ async function technicalAeoLinkNeighbors(project2, opts) {
17504
17528
  return;
17505
17529
  }
17506
17530
  const title = res.url ?? res.nodeKey ?? "page";
17531
+ const note = partialCrawlNote(res);
17532
+ const none = note ? " (none observed in this partial crawl)" : " (none)";
17507
17533
  const lines = [`Internal links for ${title}:`, "", "Inbound:"];
17508
17534
  for (const edge of res.inbound) lines.push(` ${edge.sourceUrl} \u2192 ${edge.targetUrl}`);
17509
- if (res.inbound.length === 0) lines.push(" (none)");
17535
+ if (res.inbound.length === 0) lines.push(none);
17510
17536
  if (res.inboundTruncated) lines.push(" (truncated)");
17511
17537
  lines.push("", "Outbound:");
17512
17538
  for (const edge of res.outbound) lines.push(` ${edge.sourceUrl} \u2192 ${edge.targetUrl}`);
17513
- if (res.outbound.length === 0) lines.push(" (none)");
17539
+ if (res.outbound.length === 0) lines.push(none);
17514
17540
  if (res.outboundTruncated) lines.push(" (truncated)");
17541
+ if (note) lines.push("", note);
17515
17542
  console.log(lines.join("\n"));
17516
17543
  }
17517
17544
  async function technicalAeoDeadLinks(project2, opts) {
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-6M7HSNXJ.js";
8
+ } from "./chunk-HB6XZOZ6.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-K4356AGP.js";
12
+ } from "./chunk-6CR7DWLU.js";
13
13
  import {
14
14
  HISTORY_WINDOW_RUNS,
15
15
  IntelligenceService,
@@ -85,7 +85,7 @@ import {
85
85
  siteCrawlPages,
86
86
  siteCrawlSnapshots,
87
87
  trafficSources
88
- } from "./chunk-ZO62Z2WN.js";
88
+ } from "./chunk-N7MRAN3G.js";
89
89
  import {
90
90
  RunKinds,
91
91
  SiteCrawlFetchStates,
@@ -112,7 +112,7 @@ import {
112
112
  roundPreservingTotal,
113
113
  siteAuditPageFactorSchema,
114
114
  wilsonInterval
115
- } from "./chunk-HAALQW4R.js";
115
+ } from "./chunk-4MWLHTRS.js";
116
116
 
117
117
  // src/demo-server.ts
118
118
  import { dirname, join as join2 } from "path";
package/dist/index.js CHANGED
@@ -3,14 +3,14 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-F4G7I7T7.js";
6
+ } from "./chunk-3QBRDFI5.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-XGERCNUR.js";
10
- import "./chunk-6M7HSNXJ.js";
11
- import "./chunk-K4356AGP.js";
12
- import "./chunk-ZO62Z2WN.js";
13
- import "./chunk-HAALQW4R.js";
9
+ } from "./chunk-YOMLHHJS.js";
10
+ import "./chunk-HB6XZOZ6.js";
11
+ import "./chunk-6CR7DWLU.js";
12
+ import "./chunk-N7MRAN3G.js";
13
+ import "./chunk-4MWLHTRS.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
16
16
  createGoogleMarketingCredentialStore,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-ZO62Z2WN.js";
5
- import "./chunk-HAALQW4R.js";
4
+ } from "./chunk-N7MRAN3G.js";
5
+ import "./chunk-4MWLHTRS.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
8
8
  IntelligenceService
package/dist/mcp.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-4NC5ZAC2.js";
3
+ } from "./chunk-TBAVVIVI.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-XGERCNUR.js";
8
- import "./chunk-K4356AGP.js";
7
+ } from "./chunk-YOMLHHJS.js";
8
+ import "./chunk-6CR7DWLU.js";
9
9
  import {
10
10
  isReadOnlyKey,
11
11
  updateCheckEnvOptOut
12
- } from "./chunk-HAALQW4R.js";
12
+ } from "./chunk-4MWLHTRS.js";
13
13
 
14
14
  // src/mcp/cli.ts
15
15
  import { randomUUID } from "crypto";