@canonry/canonry 7.18.1 → 7.19.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.
Files changed (32) hide show
  1. package/assets/assets/AuditHistoryPanel-DEEWm-4t.js +1 -0
  2. package/assets/assets/{BacklinksPage-BT3j4tQK.js → BacklinksPage-F5QBgXbF.js} +1 -1
  3. package/assets/assets/{HistoryPage-BHV_3wbQ.js → HistoryPage-CNp42sUO.js} +1 -1
  4. package/assets/assets/MeasurementPropertyPage-XpI9hVMb.js +4 -0
  5. package/assets/assets/ProjectPage-B_V4QpyI.js +8 -0
  6. package/assets/assets/{RunsPage-Cx5yMuzN.js → RunsPage-PTHloZl3.js} +1 -1
  7. package/assets/assets/{SettingsPage-DrZ-eswp.js → SettingsPage-C-aDo2zf.js} +1 -1
  8. package/assets/assets/{SiteHealthSection-XOUI_75e.js → SiteHealthSection-DvoT8IuX.js} +3 -3
  9. package/assets/assets/{TrafficPage-DciX-fl5.js → TrafficPage-DahJoMKF.js} +3 -3
  10. package/assets/assets/{TrafficSourceDetailPage-D1_lnRoS.js → TrafficSourceDetailPage-BGlHXOlk.js} +1 -1
  11. package/assets/assets/{extract-error-message-Cw5pcZtv.js → extract-error-message-CP_fyJLM.js} +1 -1
  12. package/assets/assets/{index-DIA9jvMi.js → index-C5V8xlr0.js} +36 -36
  13. package/assets/assets/{react-sigma_core.esm.min-Cc8jHlvQ.js → react-sigma_core.esm.min-DUS8srfg.js} +1 -1
  14. package/assets/assets/v2-overview-adapter-ycJ1guZb.js +1 -0
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-JMC7CEJB.js → chunk-35IIFDIT.js} +2 -2
  17. package/dist/{chunk-U7D7LNVJ.js → chunk-A75L7CUV.js} +2 -2
  18. package/dist/{chunk-X2S7D3MN.js → chunk-CYHUZ2JQ.js} +120 -7
  19. package/dist/{chunk-PRL6UQ7L.js → chunk-OMP5OLBK.js} +6 -6
  20. package/dist/{chunk-JUOFNJLV.js → chunk-PBTTPZZE.js} +3 -3
  21. package/dist/{chunk-COQOQYRZ.js → chunk-UUI4HOEL.js} +100 -24
  22. package/dist/{chunk-EAAM47H6.js → chunk-VSTKTQPS.js} +3 -3
  23. package/dist/cli.js +25 -15
  24. package/dist/{demo-server-XWRIXME5.js → demo-server-K5G54GOE.js} +4 -4
  25. package/dist/index.js +6 -6
  26. package/dist/{intelligence-service-332S7D7Y.js → intelligence-service-LV2TQMMR.js} +2 -2
  27. package/dist/mcp.js +4 -4
  28. package/package.json +12 -12
  29. package/assets/assets/AuditHistoryPanel-RAYlf3B4.js +0 -1
  30. package/assets/assets/MeasurementPropertyPage-9UFPKw-i.js +0 -1
  31. package/assets/assets/ProjectPage-C1_-qiHw.js +0 -8
  32. package/assets/assets/v2-overview-adapter-Ca5F20Kr.js +0 -1
@@ -556,6 +556,8 @@ import {
556
556
  measurementDraftUpsertTargetRequestSchema,
557
557
  measurementIdempotencyKeyConflict,
558
558
  measurementIdempotencyKeyRequired,
559
+ measurementNameKey,
560
+ measurementNameWords,
559
561
  measurementOverviewQuerySchema,
560
562
  measurementOverviewResponseSchema,
561
563
  measurementOverviewSortSchema,
@@ -599,6 +601,8 @@ import {
599
601
  measurementSegmentRetirementResponseSchema,
600
602
  measurementSetupResponseSchema,
601
603
  measurementStableKeySchema,
604
+ measurementTargetNameIssueMessage,
605
+ measurementTargetNameIssues,
602
606
  measurementV2UsageEdgeKey,
603
607
  mentionStateFromAnswerMentioned,
604
608
  missingDependency,
@@ -662,6 +666,7 @@ import {
662
666
  providerError,
663
667
  providerSummaryEntryDtoSchema,
664
668
  providersFailing,
669
+ qualifiedNameIncludesName,
665
670
  queryDtoSchema,
666
671
  queryGenerateRequestSchema,
667
672
  queryGenerateResponseSchema,
@@ -848,7 +853,7 @@ import {
848
853
  wordpressSchemaDeployResultDtoSchema,
849
854
  wordpressSchemaStatusResultDtoSchema,
850
855
  wordpressStatusDtoSchema
851
- } from "./chunk-X2S7D3MN.js";
856
+ } from "./chunk-CYHUZ2JQ.js";
852
857
 
853
858
  // src/intelligence-service.ts
854
859
  import { eq as eq89, desc as desc41, asc as asc18, and as and75, ne as ne16, or as or22, inArray as inArray36, gte as gte24, lte as lte20, isNull as isNull17, sql as sql32, exists as exists2 } from "drizzle-orm";
@@ -16090,11 +16095,9 @@ function normalizeMeasurementLocation(value) {
16090
16095
  if (value === null) return null;
16091
16096
  return normalizeIdentityText(value) || null;
16092
16097
  }
16093
- function words(value) {
16094
- return value.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
16095
- }
16098
+ var words = measurementNameWords;
16096
16099
  function measurementMentionAliasKey(value) {
16097
- return words(value).join("\0");
16100
+ return measurementNameKey(value);
16098
16101
  }
16099
16102
  function aliasMatchesAt(textWords, aliasWords, start) {
16100
16103
  if (aliasWords.length === 0 || start + aliasWords.length > textWords.length) return false;
@@ -23305,11 +23308,8 @@ function compileMeasurementDraft(authoring, context) {
23305
23308
  aliasClaims.set(identity, target.stableKey);
23306
23309
  });
23307
23310
  const aliases = sortedUnique(target.aliases);
23308
- const aliasKeys = aliases.map(measurementMentionAliasKey).filter(Boolean);
23309
23311
  target.identityAliases?.forEach((identityAlias, index2) => {
23310
- const identityKey = measurementMentionAliasKey(identityAlias);
23311
- const qualifiesAlias = aliasKeys.some((aliasKey) => identityKey !== aliasKey && `\0${identityKey}\0`.includes(`\0${aliasKey}\0`));
23312
- if (!qualifiesAlias) {
23312
+ if (!qualifiedNameIncludesName(identityAlias, aliases)) {
23313
23313
  sink.fail("target-identity-alias-unqualified", "An identity phrase must include a Property alias and additional identifying context.", ["targets", targetIndex, "identityAliases", index2]);
23314
23314
  }
23315
23315
  });
@@ -23827,13 +23827,18 @@ function withRequestedContext(assignment, contextOverride) {
23827
23827
  const { executionContexts: _frozenContexts, ...withoutFrozenContexts } = assignment;
23828
23828
  return { ...withoutFrozenContexts, contextOverride };
23829
23829
  }
23830
- function upsertTarget(authoring, body) {
23830
+ function upsertTarget(authoring, body, context) {
23831
23831
  const { target } = parseBody2(measurementDraftUpsertTargetRequestSchema, body, "upsert-target");
23832
23832
  const index2 = authoring.targets.findIndex((candidate) => candidate.stableKey === target.stableKey);
23833
23833
  const targets = [...authoring.targets];
23834
23834
  if (index2 === -1) targets.push(target);
23835
23835
  else targets[index2] = target;
23836
- return { authoring: { ...authoring, targets }, warnings: [] };
23836
+ const warnings = measurementTargetNameIssues({
23837
+ aliases: target.aliases,
23838
+ ...target.identityAliases === void 0 ? {} : { identityAliases: target.identityAliases },
23839
+ brandNames: context.brandNames
23840
+ }).map((issue) => warn(issue.code, measurementTargetNameIssueMessage(issue), ["target", issue.field, issue.index]));
23841
+ return { authoring: { ...authoring, targets }, warnings };
23837
23842
  }
23838
23843
  function renameTarget(authoring, body) {
23839
23844
  const { targetKey, label } = parseBody2(measurementDraftRenameTargetRequestSchema, body, "rename-target");
@@ -24156,7 +24161,7 @@ function applyDraftAction(action, authoring, body, context) {
24156
24161
  const result = (() => {
24157
24162
  switch (action) {
24158
24163
  case "upsert-target":
24159
- return upsertTarget(authoring, body);
24164
+ return upsertTarget(authoring, body, context);
24160
24165
  case "rename-target":
24161
24166
  return renameTarget(authoring, body);
24162
24167
  case "merge-targets":
@@ -26635,6 +26640,9 @@ function capSources(row) {
26635
26640
  function answerRowKey(row) {
26636
26641
  return [row.expectedSlotId, row.usageEdgeId].join("\0");
26637
26642
  }
26643
+ function otherQueryRowKey(row) {
26644
+ return [row.expectedSlotId, row.queryClass].join("\0");
26645
+ }
26638
26646
  function encodeCursor(cursor) {
26639
26647
  const { shape, ...rest } = cursor;
26640
26648
  return Buffer.from(
@@ -26653,7 +26661,7 @@ function parseCursor2(value) {
26653
26661
  if (typeof candidate !== "string" || candidate.length === 0) return null;
26654
26662
  }
26655
26663
  const shape = cursor.shape === void 0 ? MEASUREMENT_EVIDENCE_DEFAULT_SHAPE : cursor.shape;
26656
- if (shape !== MeasurementEvidenceShapes.sources && shape !== MeasurementEvidenceShapes.answers) return null;
26664
+ if (shape !== MeasurementEvidenceShapes.sources && shape !== MeasurementEvidenceShapes.answers && shape !== MeasurementEvidenceShapes["other-queries"]) return null;
26657
26665
  return {
26658
26666
  v: 1,
26659
26667
  key: cursor.key,
@@ -26686,12 +26694,57 @@ function propertyEvidenceRows(db, plan, active, run, query, queryClass) {
26686
26694
  const location = query.location === void 0 ? void 0 : normalizeMeasurementLocation(query.location);
26687
26695
  const owned = (row) => ownEdgeIds.has(row.usageEdgeId) && (provider === void 0 || row.provider === provider) && (location === void 0 || normalizeMeasurementLocation(row.location) === location);
26688
26696
  const built = buildMeasurementEvidence(input);
26697
+ const narrowed = (row) => (provider === void 0 || row.provider === provider) && (location === void 0 || normalizeMeasurementLocation(row.location) === location);
26689
26698
  return {
26690
26699
  sources: built.evidence.filter(owned),
26691
26700
  answers: built.answers.filter(owned),
26701
+ otherQueries: otherQueryCitations(input.usageEdges, built.answers, query.targetKey, queryClass).filter(narrowed),
26692
26702
  evidenceFingerprint: snapshotEvidenceFingerprint(snapshots)
26693
26703
  };
26694
26704
  }
26705
+ function otherQueryCitations(usageEdges, answers, targetKey, queryClass) {
26706
+ const edgesByExecution = /* @__PURE__ */ new Map();
26707
+ for (const edge of usageEdges) {
26708
+ if (edge.type !== "target") continue;
26709
+ const edges = edgesByExecution.get(edge.executionId) ?? [];
26710
+ edges.push({ targetId: edge.targetId, queryClass: edge.queryClass ?? null });
26711
+ edgesByExecution.set(edge.executionId, edges);
26712
+ }
26713
+ const rows = [];
26714
+ const seen2 = /* @__PURE__ */ new Set();
26715
+ for (const answer of answers) {
26716
+ if (seen2.has(answer.expectedSlotId)) continue;
26717
+ seen2.add(answer.expectedSlotId);
26718
+ const edges = edgesByExecution.get(answer.executionId) ?? [];
26719
+ if (edges.some((edge) => edge.targetId === targetKey)) continue;
26720
+ const own = answer.sources.filter((source) => source.classification === "sibling" && source.matchedTargetIds.length === 1 && source.matchedTargetIds[0] === targetKey);
26721
+ if (own.length === 0) continue;
26722
+ const sources = own.map((source) => ({
26723
+ sourceUrl: source.sourceUrl,
26724
+ normalizedUrl: source.normalizedUrl,
26725
+ matchedUrlIds: source.matchedUrlIds
26726
+ }));
26727
+ const classes = sortedUnique(edges.flatMap((edge) => edge.queryClass === null ? [] : [edge.queryClass]));
26728
+ for (const edgeClass of classes) {
26729
+ if (queryClass !== "all" && edgeClass !== queryClass) continue;
26730
+ rows.push({
26731
+ observationId: answer.observationId,
26732
+ expectedSlotId: answer.expectedSlotId,
26733
+ executionId: answer.executionId,
26734
+ provider: answer.provider,
26735
+ queryText: answer.queryText,
26736
+ location: answer.location,
26737
+ queryClass: edgeClass,
26738
+ assignedTargetKeys: sortedUnique(edges.filter((edge) => edge.queryClass === edgeClass).map((edge) => edge.targetId)),
26739
+ sources: sources.slice(0, MAX_SOURCES_PER_ANSWER),
26740
+ sourceCount: sources.length,
26741
+ sourcesTruncated: sources.length > MAX_SOURCES_PER_ANSWER,
26742
+ evidenceComplete: answer.evidenceComplete
26743
+ });
26744
+ }
26745
+ }
26746
+ return rows;
26747
+ }
26695
26748
  function pageOf2(rows, keyOf, shape, query, displayedRunId, planVersionId, evidenceFingerprint) {
26696
26749
  const limit = query.limit ?? MEASUREMENT_PAGE_DEFAULT_LIMIT;
26697
26750
  let offset = 0;
@@ -26766,10 +26819,10 @@ async function measurementPropertyEvidenceRoutes(app) {
26766
26819
  // The page still arrives under the key the caller's shape names. An
26767
26820
  // unmeasured Property has no rows in EITHER reading, and swapping the
26768
26821
  // key here would read as "that shape is unavailable" instead.
26769
- ...shape === MeasurementEvidenceShapes.answers ? { answers: empty } : { evidence: empty }
26822
+ ...shape === MeasurementEvidenceShapes.answers ? { answers: empty } : shape === MeasurementEvidenceShapes["other-queries"] ? { otherQueries: empty } : { evidence: empty }
26770
26823
  });
26771
26824
  }
26772
- const { sources, answers, evidenceFingerprint } = propertyEvidenceRows(app.db, plan, active, displayed, query, queryClass);
26825
+ const { sources, answers, otherQueries, evidenceFingerprint } = propertyEvidenceRows(app.db, plan, active, displayed, query, queryClass);
26773
26826
  const pageArgs = [shape, query, displayed.id, active.version.id, evidenceFingerprint];
26774
26827
  return measurementPropertyEvidenceResponseSchema.parse({
26775
26828
  property,
@@ -26779,7 +26832,7 @@ async function measurementPropertyEvidenceRoutes(app) {
26779
26832
  displayedRunId: displayed.id,
26780
26833
  ...displayed.finishedAt ? { completedAt: displayed.finishedAt } : {}
26781
26834
  },
26782
- ...shape === MeasurementEvidenceShapes.answers ? { answers: pageOf2(answers.map(capSources), answerRowKey, ...pageArgs) } : { evidence: pageOf2(sources, sourceRowKey, ...pageArgs) }
26835
+ ...shape === MeasurementEvidenceShapes.answers ? { answers: pageOf2(answers.map(capSources), answerRowKey, ...pageArgs) } : shape === MeasurementEvidenceShapes["other-queries"] ? { otherQueries: pageOf2(otherQueries, otherQueryRowKey, ...pageArgs) } : { evidence: pageOf2(sources, sourceRowKey, ...pageArgs) }
26783
26836
  });
26784
26837
  }
26785
26838
  );
@@ -28761,11 +28814,17 @@ function targetPresence(candidates, targets, targetKey) {
28761
28814
  const values = scoped.map((candidate) => targetValues(candidate, targets, targetKey));
28762
28815
  const citations = scoped.map((candidate, index2) => candidate.observation?.citationComplete === false ? null : values[index2].citation);
28763
28816
  const anyKnown = (signals) => signals.includes(true) ? true : signals.length === 0 || signals.includes(null) ? null : false;
28817
+ const mention = anyKnown(values.map((value) => value.mention));
28818
+ const citation = anyKnown(citations);
28764
28819
  return {
28765
28820
  targetKey,
28766
- mention: anyKnown(values.map((value) => value.mention)),
28767
- citation: anyKnown(citations),
28768
- identityAmbiguous: values.some((value) => value.mentionUnavailableReason === "identity-ambiguous")
28821
+ mention,
28822
+ citation,
28823
+ identityAmbiguous: values.some((value) => value.mentionUnavailableReason === "identity-ambiguous"),
28824
+ // Every answer was saved, so an unknown citation can only come from an
28825
+ // answer whose source list was partly saved. With the mention measured,
28826
+ // that is the one reason this Property reads not measured.
28827
+ uncheckedOnly: mention !== null && citation === null && scoped.length > 0 && scoped.every((candidate) => candidate.observation !== null)
28769
28828
  };
28770
28829
  }
28771
28830
  function outcomeCounts(rows) {
@@ -28785,6 +28844,19 @@ function outcomeCounts(rows) {
28785
28844
  }
28786
28845
  return counts2;
28787
28846
  }
28847
+ function uncheckedSourceEvidence(rows) {
28848
+ const unchecked = rows.filter((candidate) => candidate.observation !== null && !candidate.observation.citationComplete);
28849
+ if (unchecked.length === 0) return void 0;
28850
+ const cited = unchecked.map((candidate) => {
28851
+ const edgeTargets = new Set(candidate.edges.map((edge) => edge.targetKey));
28852
+ return candidate.observation.citedTargetKeys.filter((key) => edgeTargets.has(key));
28853
+ });
28854
+ return {
28855
+ answers: unchecked.length,
28856
+ citedAnswers: cited.filter((keys) => keys.length > 0).length,
28857
+ citedTargetKeys: sortedUnique(cited.flat())
28858
+ };
28859
+ }
28788
28860
  function coverageSummary(candidates, definition, targets = targetMap(definition)) {
28789
28861
  const unique3 = new Map(candidates.map((candidate) => [candidateKey(candidate), candidate]));
28790
28862
  const rows = [...unique3.values()];
@@ -28807,13 +28879,15 @@ function summary2(candidates, definition, populationTargetKeys = selectedTargetK
28807
28879
  eligibleTargets.length,
28808
28880
  eligibleTargets.some((row) => row.mention === null && row.identityAmbiguous) ? "identity-ambiguous" : "evidence-incomplete"
28809
28881
  );
28882
+ const notMeasuredUnchecked = targetRows.filter((row) => row.uncheckedOnly).length;
28810
28883
  return {
28811
28884
  queryCount: base.queryCount,
28812
28885
  answerCount: base.answerCount,
28813
28886
  mentionCoverage: base.mentionCoverage,
28814
28887
  citationCoverage: base.citationCoverage,
28815
28888
  propertyReach,
28816
- outcomes: outcomeCounts(targetRows)
28889
+ outcomes: outcomeCounts(targetRows),
28890
+ ...notMeasuredUnchecked > 0 ? { notMeasuredUnchecked } : {}
28817
28891
  };
28818
28892
  }
28819
28893
  function queryRows(candidates, definition, selection) {
@@ -28830,6 +28904,7 @@ function queryRows(candidates, definition, selection) {
28830
28904
  return [...groups.values()].map((rows) => {
28831
28905
  const first = rows[0];
28832
28906
  const value = coverageSummary(rows, definition, targets);
28907
+ const uncheckedSources = uncheckedSourceEvidence(value.rows);
28833
28908
  return {
28834
28909
  sourceSnapshotIds: sortedUnique(rows.flatMap((row) => row.observation ? [row.observation.answerId] : [])),
28835
28910
  queryKey: first.slot.queryKey,
@@ -28845,7 +28920,8 @@ function queryRows(candidates, definition, selection) {
28845
28920
  })(),
28846
28921
  answerCount: value.answerCount,
28847
28922
  mentionCoverage: value.mentionCoverage,
28848
- citationCoverage: value.citationCoverage
28923
+ citationCoverage: value.citationCoverage,
28924
+ ...uncheckedSources === void 0 ? {} : { uncheckedSources }
28849
28925
  };
28850
28926
  }).sort((left, right) => (selection.scope === "property" && selection.marketKey === void 0 ? compareText((left.marketKeys ?? []).join("\0"), (right.marketKeys ?? []).join("\0")) : 0) || compareText(normalizeIdentityText(left.query), normalizeIdentityText(right.query)) || compareText(left.provider, right.provider) || compareText(left.model ?? "", right.model ?? "") || compareText(left.location ?? "", right.location ?? "") || compareText(left.queryKey, right.queryKey));
28851
28927
  }
@@ -39284,8 +39360,8 @@ var measurementOverviewCursorParameter = {
39284
39360
  var measurementPropertyEvidenceShapeParameter = {
39285
39361
  name: "shape",
39286
39362
  in: "query",
39287
- description: "What one row is. sources (the default) returns one row per cited URL under evidence, which is what a caller written before this parameter existed reads. answers returns one row per measured answer under answers, with the cited URLs nested inside it, so the answers that cited nothing at all are present rather than missing. Exactly one of the two keys is returned; the other is absent, not empty.",
39288
- schema: { type: "string", enum: ["sources", "answers"], default: "sources" }
39363
+ description: "What one row is. sources (the default) returns one row per cited URL under evidence, which is what a caller written before this parameter existed reads. answers returns one row per measured answer under answers, with the cited URLs nested inside it, so the answers that cited nothing at all are present rather than missing. other-queries returns, under otherQueries, one row per answer to a query NOT assigned to this Property that cited one of its own pages, per assignment class; those answers are outside its rates. Exactly one of the keys is returned; the others are absent, not empty.",
39364
+ schema: { type: "string", enum: ["sources", "answers", "other-queries"], default: "sources" }
39289
39365
  };
39290
39366
  var measurementPropertyEvidenceCursorParameter = {
39291
39367
  name: "cursor",
@@ -39995,7 +40071,7 @@ var routeCatalog = [
39995
40071
  method: "get",
39996
40072
  path: "/api/v1/projects/{name}/measurement-property-evidence",
39997
40073
  summary: "Page one Property's evidence",
39998
- description: "Returns the evidence rows for exactly one Property out of one revision-pinned run, optionally narrowed to a question class, provider, or location. shape chooses what a row is: sources (the default) is one row per cited URL, answers is one row per measured answer with its cited URLs nested inside. Prefer answers to explain a gap \u2014 an answer that mentioned the Property without linking it, or that named nobody, has no URL to hang a source row on and is invisible in the default shape. Run selection matches the overview: the most recent completed run pinned to the active revision unless runId names another. Use this rather than GET /measurement-report when you want one Property \u2014 the report reconstructs every group and Target for a revision and does not paginate. Not available for a schema v1 revision, which records no question class to scope by. An empty page under measurement.state = not_measured means the Property has not been measured, which is not the same statement as a measured Property with no evidence.",
40074
+ description: "Returns the evidence rows for exactly one Property out of one revision-pinned run, optionally narrowed to a question class, provider, or location. shape chooses what a row is: sources (the default) is one row per cited URL, answers is one row per measured answer with its cited URLs nested inside, other-queries is one row per answer to a query not assigned to this Property that cited its pages (outside its rates). Prefer answers to explain a gap \u2014 an answer that mentioned the Property without linking it, or that named nobody, has no URL to hang a source row on and is invisible in the default shape. Run selection matches the overview: the most recent completed run pinned to the active revision unless runId names another. Use this rather than GET /measurement-report when you want one Property \u2014 the report reconstructs every group and Target for a revision and does not paginate. Not available for a schema v1 revision, which records no question class to scope by. An empty page under measurement.state = not_measured means the Property has not been measured, which is not the same statement as a measured Property with no evidence.",
39999
40075
  tags: ["measurement-plans"],
40000
40076
  parameters: [
40001
40077
  nameParameter,
@@ -142,7 +142,7 @@ import {
142
142
  trafficSeriesGranularitySchema,
143
143
  visibilityCompareSelectionSchema,
144
144
  visibilityReportRequestSchema
145
- } from "./chunk-X2S7D3MN.js";
145
+ } from "./chunk-CYHUZ2JQ.js";
146
146
 
147
147
  // src/cli-error.ts
148
148
  function isMachineFormat(format) {
@@ -446,7 +446,7 @@ var measurementPropertyEvidenceInputSchema = measurementPropertyEvidenceQuerySch
446
446
  targetKey: measurementPropertyEvidenceQuerySchema.shape.targetKey.describe("Property stable key. Required \u2014 this read is scoped to exactly one Property."),
447
447
  queryClass: propertyQueryClassInputSchema,
448
448
  shape: measurementPropertyEvidenceQuerySchema.shape.shape.describe(
449
- "What one row is. Omit for sources (one row per cited URL). answers gives one row per measured answer with its cited URLs nested, including the answers that cited nothing."
449
+ "What one row is. Omit for sources (one row per cited URL). answers gives one row per measured answer with its cited URLs nested, including the answers that cited nothing. other-queries gives one row per answer to a query NOT assigned to this Property that cited one of its own pages; those answers are outside its rates."
450
450
  )
451
451
  }).strict();
452
452
  var measurementPortfolioSummaryInputSchema = measurementPortfolioSummaryQuerySchema.extend({
@@ -2938,7 +2938,7 @@ var canonryMcpTools = [
2938
2938
  defineTool({
2939
2939
  name: "canonry_measurement_property_evidence",
2940
2940
  title: "Page one Property's measurement evidence",
2941
- description: "Return the stored evidence rows for exactly one Property out of one revision-pinned run, optionally narrowed to a provider or location. Defaults to non-brand queries; pass queryClass branded or all to change it, and state the returned class. shape chooses what a row is: sources (the default) returns one row per cited URL under evidence; answers returns one row per measured answer under answers, with the cited URLs nested inside and both signals on the row (mentioned, cited). Use shape=answers to explain a GAP: an answer that mentioned the Property without linking it, or that named nobody at all, has no URL to hang a source row on and does not appear in the default shape, so counting source rows understates what was measured. mentioned is null, never false, when the answer text was never captured: that is a missing signal, not a measured no. Exactly one of evidence or answers is returned and the other is absent rather than empty. A cursor is bound to the shape that issued it and is refused on the other. Prefer this over canonry_measurement_report when you want one Property: the report reconstructs every group and every Target for a revision and does not paginate. Run selection matches canonry_measurement_overview: the most recent completed run pinned to the active revision unless runId names another, and a run pinned to a different revision is refused rather than joined. The cursor also pins the active revision, displayed run, evidence snapshot, and filters; reuse it unchanged. Not available for a schema v1 revision, which records no question class to scope by. It never starts provider work; page size is at most 100. An empty page under measurement.state = not_measured means the Property has not been measured at all, which is NOT a measured result of zero.",
2941
+ description: "Return the stored evidence rows for exactly one Property out of one revision-pinned run, optionally narrowed to a provider or location. Defaults to non-brand queries; pass queryClass branded or all to change it, and state the returned class. shape chooses what a row is: sources (the default) returns one row per cited URL under evidence; answers returns one row per measured answer under answers, with the cited URLs nested inside and both signals on the row (mentioned, cited). Use shape=answers to explain a GAP: an answer that mentioned the Property without linking it, or that named nobody at all, has no URL to hang a source row on and does not appear in the default shape, so counting source rows understates what was measured. shape=other-queries returns, under otherQueries, answers to queries assigned only to other Properties that still cited this Property's pages, one row per answer and assignment class with assignedTargetKeys; never add them to its rates. mentioned is null, never false, when the answer text was never captured: that is a missing signal, not a measured no. Exactly one of evidence, answers or otherQueries is returned and the others are absent rather than empty. A cursor is bound to the shape that issued it and is refused on the other. Prefer this over canonry_measurement_report when you want one Property: the report reconstructs every group and every Target for a revision and does not paginate. Run selection matches canonry_measurement_overview: the most recent completed run pinned to the active revision unless runId names another, and a run pinned to a different revision is refused rather than joined. The cursor also pins the active revision, displayed run, evidence snapshot, and filters; reuse it unchanged. Not available for a schema v1 revision, which records no question class to scope by. It never starts provider work; page size is at most 100. An empty page under measurement.state = not_measured means the Property has not been measured at all, which is NOT a measured result of zero.",
2942
2942
  access: "read",
2943
2943
  tier: "setup",
2944
2944
  inputSchema: measurementPropertyEvidenceInputSchema,
package/dist/cli.js CHANGED
@@ -34,11 +34,11 @@ import {
34
34
  trackCliCommandFinished,
35
35
  trackEvent,
36
36
  waitForServerRuntimeStartup
37
- } from "./chunk-PRL6UQ7L.js";
37
+ } from "./chunk-OMP5OLBK.js";
38
38
  import {
39
39
  autoSyncSkills,
40
40
  formatAutoSyncNotice
41
- } from "./chunk-JUOFNJLV.js";
41
+ } from "./chunk-PBTTPZZE.js";
42
42
  import {
43
43
  configExists,
44
44
  createApiClient,
@@ -55,13 +55,13 @@ import {
55
55
  saveConfig,
56
56
  saveConfigPatch,
57
57
  shouldPrintServeSkillsNudge
58
- } from "./chunk-JMC7CEJB.js";
58
+ } from "./chunk-35IIFDIT.js";
59
59
  import {
60
60
  DEFAULT_DEMO_TRUSTED_PROXIES,
61
61
  trustedProxiesProblem,
62
62
  trustedProxyProblem
63
63
  } from "./chunk-RB2F4D3Z.js";
64
- import "./chunk-U7D7LNVJ.js";
64
+ import "./chunk-A75L7CUV.js";
65
65
  import {
66
66
  CliError,
67
67
  EXIT_SYSTEM_ERROR,
@@ -73,7 +73,7 @@ import {
73
73
  runMeasurementDraftAction,
74
74
  systemError,
75
75
  usageError
76
- } from "./chunk-EAAM47H6.js";
76
+ } from "./chunk-VSTKTQPS.js";
77
77
  import {
78
78
  CLOUDFLARE_WORKER_BINDINGS,
79
79
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -105,7 +105,7 @@ import {
105
105
  rawEventSamples,
106
106
  readMarketCompetitorNames,
107
107
  runs
108
- } from "./chunk-COQOQYRZ.js";
108
+ } from "./chunk-UUI4HOEL.js";
109
109
  import {
110
110
  AdsDeliverySnapshotStatuses,
111
111
  AdsHistoricalCampaignRollupStatuses,
@@ -238,7 +238,7 @@ import {
238
238
  visibilityCompareSelectionSchema,
239
239
  visibilityReportRequestSchema,
240
240
  winnabilityClassSchema
241
- } from "./chunk-X2S7D3MN.js";
241
+ } from "./chunk-CYHUZ2JQ.js";
242
242
 
243
243
  // src/cli.ts
244
244
  import { pathToFileURL } from "url";
@@ -788,7 +788,7 @@ function parseDemoTrustedProxies(values) {
788
788
  async function demoCommand(options) {
789
789
  const { host, port } = parseDemoListenOptions(options);
790
790
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
791
- const { createDemoServer } = await import("./demo-server-XWRIXME5.js");
791
+ const { createDemoServer } = await import("./demo-server-K5G54GOE.js");
792
792
  const app = await createDemoServer({ trustProxy });
793
793
  let stopping = false;
794
794
  const stop = () => {
@@ -1202,7 +1202,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
1202
1202
  }
1203
1203
  }
1204
1204
  async function backfillInsightsCommand(project2, opts) {
1205
- const { IntelligenceService } = await import("./intelligence-service-332S7D7Y.js");
1205
+ const { IntelligenceService } = await import("./intelligence-service-LV2TQMMR.js");
1206
1206
  const config = loadConfig();
1207
1207
  const db = createClient(config.database);
1208
1208
  migrate(db);
@@ -12326,7 +12326,8 @@ function printMeasurementProperty(response) {
12326
12326
  function shapedPage(response) {
12327
12327
  if (response.answers !== void 0) return { shape: MeasurementEvidenceShapes.answers, ...response.answers };
12328
12328
  if (response.evidence !== void 0) return { shape: MeasurementEvidenceShapes.sources, ...response.evidence };
12329
- throw systemError("The measurement property evidence response carried neither an evidence nor an answers page.");
12329
+ if (response.otherQueries !== void 0) return { shape: MeasurementEvidenceShapes["other-queries"], ...response.otherQueries };
12330
+ throw systemError("The measurement property evidence response carried no evidence, answers or otherQueries page.");
12330
12331
  }
12331
12332
  async function showMeasurementPropertyEvidence(project2, opts) {
12332
12333
  const response = await createApiClient().getMeasurementPropertyEvidence(project2, {
@@ -12373,15 +12374,24 @@ function printMeasurementPropertyEvidence(response) {
12373
12374
  }
12374
12375
  const page = shapedPage(response);
12375
12376
  const answerShape = page.shape === MeasurementEvidenceShapes.answers;
12377
+ const otherQueriesShape = page.shape === MeasurementEvidenceShapes["other-queries"];
12376
12378
  lines.push(`Measurement: ${response.measurement.state}${response.measurement.displayedRunId ? ` \xB7 run ${response.measurement.displayedRunId}` : ""}`);
12377
12379
  if (page.items.length === 0) {
12378
- lines.push(answerShape ? "No answers matched this Property in the displayed run." : "No source evidence matched this Property in the displayed run.");
12380
+ lines.push(answerShape ? "No answers matched this Property in the displayed run." : otherQueriesShape ? "No answer to another query cited this Property's pages in the displayed run." : "No source evidence matched this Property in the displayed run.");
12379
12381
  console.log(lines.join("\n"));
12380
12382
  return;
12381
12383
  }
12382
- lines.push(`${page.items.length} of ${page.totalEstimate ?? page.items.length} ${answerShape ? "answers" : "evidence rows"}`);
12384
+ lines.push(`${page.items.length} of ${page.totalEstimate ?? page.items.length} ${answerShape || otherQueriesShape ? "answers" : "evidence rows"}`);
12385
+ if (otherQueriesShape) lines.push("Cited on queries not assigned to this Property; not counted in its rates.");
12383
12386
  lines.push("");
12384
- if (page.shape === MeasurementEvidenceShapes.answers) {
12387
+ if (page.shape === MeasurementEvidenceShapes["other-queries"]) {
12388
+ lines.push(`${"Engine".padEnd(12)}${"Question".padEnd(40)}${"Class".padEnd(11)}${"Assigned to".padEnd(24)}Pages`);
12389
+ for (const item of page.items) {
12390
+ lines.push(
12391
+ `${item.provider.padEnd(12)}${item.queryText.slice(0, 39).padEnd(40)}${item.queryClass.padEnd(11)}${item.assignedTargetKeys.join(", ").slice(0, 23).padEnd(24)}${item.sources.map((source) => source.sourceUrl).join(" ")}`
12392
+ );
12393
+ }
12394
+ } else if (page.shape === MeasurementEvidenceShapes.answers) {
12385
12395
  lines.push(`${"Engine".padEnd(12)}${"Question".padEnd(40)}${"Mentioned".padEnd(15)}${"Cited".padEnd(8)}Sources`);
12386
12396
  for (const item of page.items) {
12387
12397
  lines.push(
@@ -21747,7 +21757,7 @@ Usage: ${usage}`, {
21747
21757
 
21748
21758
  // src/cli-commands/measurement-plan.ts
21749
21759
  var QUERY_CLASSES = ["all", "branded", "non-brand"];
21750
- var EVIDENCE_SHAPES = ["sources", "answers"];
21760
+ var EVIDENCE_SHAPES = ["sources", "answers", "other-queries"];
21751
21761
  function queryClassOption(input) {
21752
21762
  const value = getString(input.values, "query-class");
21753
21763
  if (value === void 0) return void 0;
@@ -21979,7 +21989,7 @@ Usage: ${usage}`);
21979
21989
  },
21980
21990
  {
21981
21991
  path: ["measurement-plan", "property-evidence"],
21982
- usage: "canonry measurement-plan property-evidence <project> --target-key <key> [--query-class all|branded|non-brand] [--provider <p>] [--location <l>] [--run-id <id>] [--shape sources|answers] [--cursor <c>] [--limit N] [--format json|jsonl]",
21992
+ usage: "canonry measurement-plan property-evidence <project> --target-key <key> [--query-class all|branded|non-brand] [--provider <p>] [--location <l>] [--run-id <id>] [--shape sources|answers|other-queries] [--cursor <c>] [--limit N] [--format json|jsonl]",
21983
21993
  options: { ...PROPERTY_SCOPE_OPTIONS, shape: stringOption(), cursor: stringOption(), limit: stringOption() },
21984
21994
  run: (input) => {
21985
21995
  const usage = "canonry measurement-plan property-evidence <project> --target-key <key>";
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-U7D7LNVJ.js";
8
+ } from "./chunk-A75L7CUV.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-EAAM47H6.js";
12
+ } from "./chunk-VSTKTQPS.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-COQOQYRZ.js";
88
+ } from "./chunk-UUI4HOEL.js";
89
89
  import {
90
90
  RunKinds,
91
91
  SiteCrawlFetchStates,
@@ -115,7 +115,7 @@ import {
115
115
  roundPreservingTotal,
116
116
  siteAuditPageFactorSchema,
117
117
  wilsonInterval
118
- } from "./chunk-X2S7D3MN.js";
118
+ } from "./chunk-CYHUZ2JQ.js";
119
119
 
120
120
  // src/demo-server.ts
121
121
  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-PRL6UQ7L.js";
6
+ } from "./chunk-OMP5OLBK.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-JMC7CEJB.js";
10
- import "./chunk-U7D7LNVJ.js";
11
- import "./chunk-EAAM47H6.js";
12
- import "./chunk-COQOQYRZ.js";
13
- import "./chunk-X2S7D3MN.js";
9
+ } from "./chunk-35IIFDIT.js";
10
+ import "./chunk-A75L7CUV.js";
11
+ import "./chunk-VSTKTQPS.js";
12
+ import "./chunk-UUI4HOEL.js";
13
+ import "./chunk-CYHUZ2JQ.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-COQOQYRZ.js";
5
- import "./chunk-X2S7D3MN.js";
4
+ } from "./chunk-UUI4HOEL.js";
5
+ import "./chunk-CYHUZ2JQ.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-JUOFNJLV.js";
3
+ } from "./chunk-PBTTPZZE.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-JMC7CEJB.js";
8
- import "./chunk-EAAM47H6.js";
7
+ } from "./chunk-35IIFDIT.js";
8
+ import "./chunk-VSTKTQPS.js";
9
9
  import {
10
10
  isReadOnlyKey,
11
11
  updateCheckEnvOptOut
12
- } from "./chunk-X2S7D3MN.js";
12
+ } from "./chunk-CYHUZ2JQ.js";
13
13
 
14
14
  // src/mcp/cli.ts
15
15
  import { randomUUID } from "crypto";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "7.18.1",
3
+ "version": "7.19.0",
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": [
@@ -86,33 +86,33 @@
86
86
  "tsup": "^8.5.1",
87
87
  "tsx": "^4.19.0",
88
88
  "@ainyc/canonry-web": "0.0.0",
89
- "@ainyc/canonry-api-routes": "0.0.0",
90
89
  "@ainyc/canonry-api-client": "0.0.0",
91
- "@ainyc/canonry-config": "0.0.0",
90
+ "@ainyc/canonry-api-routes": "0.0.0",
92
91
  "@ainyc/canonry-contracts": "0.0.0",
92
+ "@ainyc/canonry-config": "0.0.0",
93
93
  "@ainyc/canonry-db": "0.0.0",
94
94
  "@ainyc/canonry-integration-bing": "0.0.0",
95
- "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
96
95
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
97
96
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
98
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
- "@ainyc/canonry-integration-google": "0.0.0",
100
- "@ainyc/canonry-integration-google-ads": "0.0.0",
97
+ "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
101
98
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
102
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
99
+ "@ainyc/canonry-integration-google-ads": "0.0.0",
100
+ "@ainyc/canonry-integration-google": "0.0.0",
103
101
  "@ainyc/canonry-integration-google-places": "0.0.0",
102
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
104
103
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
105
104
  "@ainyc/canonry-integration-traffic": "0.0.0",
106
105
  "@ainyc/canonry-integration-wordpress": "0.0.0",
106
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
107
+ "@ainyc/canonry-intelligence": "0.0.0",
107
108
  "@ainyc/canonry-provider-cdp": "0.0.0",
108
109
  "@ainyc/canonry-provider-claude": "0.0.0",
109
- "@ainyc/canonry-intelligence": "0.0.0",
110
110
  "@ainyc/canonry-provider-gemini": "0.0.0",
111
- "@ainyc/canonry-provider-local": "0.0.0",
112
- "@ainyc/canonry-provider-openai": "0.0.0",
113
111
  "@ainyc/canonry-provider-muse": "0.0.0",
112
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
113
+ "@ainyc/canonry-provider-openai": "0.0.0",
114
114
  "@ainyc/canonry-integration-typesafe": "0.0.0",
115
- "@ainyc/canonry-provider-perplexity": "0.0.0"
115
+ "@ainyc/canonry-provider-local": "0.0.0"
116
116
  },
117
117
  "scripts": {
118
118
  "build": "pnpm run build:cli && pnpm run build:web",
@@ -1 +0,0 @@
1
- import{r as a,u as h,j as e}from"./vendor-tanstack-ZXXkndVj.js";import{dJ as L,dK as P,cH as E,x as u,B as x,v,T as M}from"./index-DIA9jvMi.js";const o=100;function k(s){if(s==="all")return;const n=Number.parseInt(s,10);return new Date(Date.now()-n*24*60*60*1e3).toISOString()}function O(s){const n=s.userAgent?.toLowerCase()??"";return n.includes("canonry-mcp")?"MCP":n.includes("canonry-cli")?"CLI":n.includes("mozilla/")?"Dashboard":s.actor==="scheduler"?"Scheduler":s.actor==="agent"?"Agent":s.actor==="mcp"?"MCP":s.actor==="cli"?"CLI":s.actor}function B(s){return/delete|remove|revoke|disconnect|failed|refused/.test(s)?"negative":/create|add|connect|completed/.test(s)?"positive":/update|replace|sync|apply/.test(s)?"caution":"neutral"}function D(s){return[s.action,s.actor,s.entityType,s.entityId,s.actorSession,s.userAgent,s.diff==null?"":JSON.stringify(s.diff)].filter(Boolean).join(" ").toLowerCase()}function F({projectName:s}){const[n,l]=a.useState(0),[c,y]=a.useState("90d"),[m,N]=a.useState(""),[p,w]=a.useState(""),S=a.useMemo(()=>k(c),[c]),g={limit:o,offset:n*o,since:S,actor:m||void 0},C=h({...L({client:u,path:{name:s??""},query:g}),enabled:!!s}),A=h({...P({client:u,query:g}),enabled:!s}),f=h({...E({client:u}),enabled:!s}),i=s?C:A,r=i.data??[],I=a.useMemo(()=>new Map((f.data??[]).map(t=>[t.id,t.displayName||t.name])),[f.data]),d=p.trim().toLowerCase(),j=a.useMemo(()=>d?r.filter(t=>D(t).includes(d)):r,[r,d]),b=t=>{l(0),t()};return i.isLoading?e.jsx("p",{className:"supporting-copy mt-6",role:"status",children:"Loading change history…"}):e.jsxs("section",{className:"mt-6","aria-labelledby":"change-history-heading",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Audit trail"}),e.jsx("h2",{id:"change-history-heading",children:"Change history"}),e.jsx("p",{className:"supporting-copy mt-1 max-w-2xl",children:"Configuration and operator actions. Execution outcomes remain in Runs."})]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",onClick:()=>{i.refetch()},disabled:i.isFetching,children:i.isFetching?"Refreshing…":"Refresh"})]}),e.jsxs("div",{className:"mt-4 grid gap-2 sm:grid-cols-3",children:[e.jsx("input",{type:"search","aria-label":"Search change history",value:p,onChange:t=>w(t.target.value),placeholder:"Search actions, entities, or details",className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong placeholder-mono-600 focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600"}),e.jsxs("select",{"aria-label":"Filter change history by actor",value:m,onChange:t=>b(()=>N(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"",children:"All actors"}),e.jsx("option",{value:"api",children:"API and dashboard"}),e.jsx("option",{value:"cli",children:"CLI"}),e.jsx("option",{value:"scheduler",children:"Scheduler"}),e.jsx("option",{value:"agent",children:"Agent"}),e.jsx("option",{value:"mcp",children:"MCP"})]}),e.jsxs("select",{"aria-label":"Filter change history by date range",value:c,onChange:t=>b(()=>y(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"30d",children:"Last 30 days"}),e.jsx("option",{value:"90d",children:"Last 90 days"}),e.jsx("option",{value:"365d",children:"Last year"}),e.jsx("option",{value:"all",children:"All retained history"})]})]}),i.isError?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"Change history unavailable"}),e.jsx("p",{children:i.error instanceof Error?i.error.message:"Could not load the audit trail."})]}):j.length===0?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"No changes match these filters"}),e.jsx("p",{children:"Widen the date range or clear the actor and search filters."})]}):e.jsx("div",{className:"evidence-table-wrap mt-4",children:e.jsxs("table",{className:"evidence-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"When"}),s?null:e.jsx("th",{children:"Scope"}),e.jsx("th",{children:"Action"}),e.jsx("th",{children:"Entity"}),e.jsx("th",{children:"Origin"}),e.jsx("th",{children:"Details"})]})}),e.jsx("tbody",{children:j.map(t=>e.jsxs("tr",{children:[e.jsx("td",{className:"whitespace-nowrap tabular-nums text-muted",children:new Date(t.createdAt).toLocaleString()}),s?null:e.jsx("td",{className:"text-secondary",children:t.projectId?I.get(t.projectId)??`Deleted project · ${t.projectId}`:"Instance settings"}),e.jsx("td",{children:e.jsx(M,{tone:B(t.action),children:t.action})}),e.jsxs("td",{children:[e.jsx("span",{className:"text-strong",children:t.entityType}),t.entityId?e.jsx("span",{className:"mt-0.5 block max-w-48 truncate font-mono text-[11px] text-faint",title:t.entityId,children:t.entityId}):null]}),e.jsxs("td",{children:[e.jsx("span",{className:"text-secondary",children:O(t)}),t.actorSession?e.jsxs("span",{className:"mt-0.5 block max-w-40 truncate font-mono text-[11px] text-faint",title:t.actorSession,children:["Session ",t.actorSession]}):null]}),e.jsx("td",{children:t.diff==null?e.jsx("span",{className:"text-faint",children:"No field diff"}):e.jsxs("details",{children:[e.jsx("summary",{className:"min-h-11 cursor-pointer py-3 text-sm text-secondary hover:text-strong",children:"View diff"}),e.jsx("pre",{className:"max-h-72 max-w-xl overflow-auto whitespace-pre-wrap break-words rounded-md bg-bg-elevated p-3 text-xs text-secondary",children:JSON.stringify(t.diff,null,2)})]})})]},t.id))})]})}),!i.isError&&(n>0||r.length===o)?e.jsxs("div",{className:"mt-4 flex items-center justify-between gap-3",children:[e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:n===0,onClick:()=>l(t=>Math.max(0,t-1)),children:"Previous"}),e.jsxs("span",{className:"text-xs tabular-nums text-muted",children:["Page ",n+1]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:r.length<o,onClick:()=>l(t=>t+1),children:"Next"})]}):null]})}export{F as A};