@canonry/canonry 4.133.2 → 4.134.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 (26) hide show
  1. package/README.md +0 -13
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +25 -0
  3. package/assets/assets/{AuditHistoryPanel-CVDg3GXg.js → AuditHistoryPanel-DBUWbfNk.js} +1 -1
  4. package/assets/assets/{BacklinksPage-Dh4RIvS7.js → BacklinksPage-BFm0VdUO.js} +1 -1
  5. package/assets/assets/{ChartPrimitives-BK89G5H_.js → ChartPrimitives-oV_nduTH.js} +1 -1
  6. package/assets/assets/{HistoryPage-D8rMu_Js.js → HistoryPage-IF2FMem3.js} +1 -1
  7. package/assets/assets/{ProjectPage-CiTDveNK.js → ProjectPage-ClwxX9Ga.js} +2 -2
  8. package/assets/assets/{RunRow-DVOwbmTp.js → RunRow-De5zdch5.js} +1 -1
  9. package/assets/assets/{RunsPage-tz3Oil7N.js → RunsPage-xtRF78r8.js} +1 -1
  10. package/assets/assets/{SettingsPage-C698YidM.js → SettingsPage-B_GAxaWu.js} +1 -1
  11. package/assets/assets/{TrafficPage-XDXWMtey.js → TrafficPage-E8I5ineJ.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-Clz8kOR8.js → TrafficSourceDetailPage-DGk2MqSp.js} +1 -1
  13. package/assets/assets/{arrow-left-GWLbh0pE.js → arrow-left-D-4QMxi9.js} +1 -1
  14. package/assets/assets/{extract-error-message-Dk8w-sb1.js → extract-error-message-DuqIFU8Z.js} +1 -1
  15. package/assets/assets/{index-jTshRRh0.js → index-De1MKbc7.js} +90 -90
  16. package/assets/assets/{trash-2-DwnzsuH1.js → trash-2-BVh__r6G.js} +1 -1
  17. package/assets/index.html +1 -1
  18. package/dist/{chunk-IEZTETUZ.js → chunk-LIXWDIDR.js} +787 -42
  19. package/dist/{chunk-ZPQX5ZQ2.js → chunk-PC7AYXND.js} +325 -2
  20. package/dist/{chunk-V2YR3YK6.js → chunk-TKXWTDV6.js} +73 -7
  21. package/dist/{chunk-JTJQZ4UC.js → chunk-ZUQPHGEN.js} +73 -2
  22. package/dist/cli.js +109 -4
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-WZNE23TV.js → intelligence-service-4XOFUN5E.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +10 -10
@@ -47,8 +47,8 @@ function authInvalid() {
47
47
  function forbidden(message = "Forbidden", details) {
48
48
  return new AppError("FORBIDDEN", message, 403, details);
49
49
  }
50
- function quotaExceeded(metric) {
51
- return new AppError("QUOTA_EXCEEDED", `Quota exceeded for ${metric}`, 429);
50
+ function quotaExceeded(metric, details) {
51
+ return new AppError("QUOTA_EXCEEDED", `Quota exceeded for ${metric}`, 429, details);
52
52
  }
53
53
  function providerError(message, details) {
54
54
  return new AppError("PROVIDER_ERROR", message, 502, details);
@@ -960,6 +960,111 @@ function formatIsoDate(iso) {
960
960
  return iso;
961
961
  }
962
962
  }
963
+ function zonedClockFormat(timeZone) {
964
+ return new Intl.DateTimeFormat("en-US", {
965
+ timeZone,
966
+ year: "numeric",
967
+ month: "2-digit",
968
+ day: "2-digit",
969
+ hour: "2-digit",
970
+ minute: "2-digit",
971
+ second: "2-digit",
972
+ hourCycle: "h23"
973
+ });
974
+ }
975
+ function readZonedParts(format, date) {
976
+ const values = {};
977
+ for (const part of format.formatToParts(date)) values[part.type] = part.value;
978
+ return values;
979
+ }
980
+ function readZonedClock(format, date) {
981
+ const parts = readZonedParts(format, date);
982
+ const value = (raw) => {
983
+ if (raw === void 0) return null;
984
+ const parsed = Number(raw);
985
+ return Number.isFinite(parsed) ? parsed : null;
986
+ };
987
+ const year = value(parts.year);
988
+ const month = value(parts.month);
989
+ const day = value(parts.day);
990
+ const hour = value(parts.hour);
991
+ const minute = value(parts.minute);
992
+ const second = value(parts.second);
993
+ if (year === null || month === null || day === null) return null;
994
+ if (hour === null || minute === null || second === null) return null;
995
+ return { year, month, day, hour, minute, second };
996
+ }
997
+ function formatIsoDateInTimeZone(iso, timeZone) {
998
+ if (!iso) return formatIsoDate(iso);
999
+ const d = new Date(iso);
1000
+ if (Number.isNaN(d.getTime())) return iso;
1001
+ try {
1002
+ const parts = readZonedParts(zonedClockFormat(timeZone), d);
1003
+ const yyyy = parts.year;
1004
+ const mm = parts.month;
1005
+ const dd = parts.day;
1006
+ if (!yyyy || !mm || !dd) return formatIsoDate(iso);
1007
+ return `${yyyy}-${mm}-${dd}`;
1008
+ } catch {
1009
+ return formatIsoDate(iso);
1010
+ }
1011
+ }
1012
+ function shiftIsoCalendarDate(isoDate, days) {
1013
+ if (!Number.isFinite(days)) return isoDate;
1014
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
1015
+ if (!match?.[1] || !match[2] || !match[3]) return isoDate;
1016
+ const shifted = new Date(Date.UTC(
1017
+ Number(match[1]),
1018
+ Number(match[2]) - 1,
1019
+ Number(match[3]) + Math.trunc(days)
1020
+ ));
1021
+ if (Number.isNaN(shifted.getTime())) return isoDate;
1022
+ const yyyy = shifted.getUTCFullYear();
1023
+ const mm = String(shifted.getUTCMonth() + 1).padStart(2, "0");
1024
+ const dd = String(shifted.getUTCDate()).padStart(2, "0");
1025
+ return `${yyyy}-${mm}-${dd}`;
1026
+ }
1027
+ function isoDateDaysBeforeInTimeZone(iso, days, timeZone) {
1028
+ return shiftIsoCalendarDate(formatIsoDateInTimeZone(iso, timeZone), -days);
1029
+ }
1030
+ var HOURS_PER_DAY = 24;
1031
+ var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
1032
+ function localHourExists(format, year, month, day, hour) {
1033
+ const asIfUtcMs = Date.UTC(year, month - 1, day, hour);
1034
+ for (const probeMs of [asIfUtcMs - ONE_DAY_MS, asIfUtcMs + ONE_DAY_MS]) {
1035
+ const probeClock = readZonedClock(format, new Date(probeMs));
1036
+ if (!probeClock) continue;
1037
+ const offsetMs = Date.UTC(
1038
+ probeClock.year,
1039
+ probeClock.month - 1,
1040
+ probeClock.day,
1041
+ probeClock.hour,
1042
+ probeClock.minute,
1043
+ probeClock.second
1044
+ ) - probeMs;
1045
+ const candidate = readZonedClock(format, new Date(asIfUtcMs - offsetMs));
1046
+ if (!candidate) continue;
1047
+ if (candidate.year === year && candidate.month === month && candidate.day === day && candidate.hour === hour && candidate.minute === 0 && candidate.second === 0) return true;
1048
+ }
1049
+ return false;
1050
+ }
1051
+ function startOfDayHourInTimeZone(isoDate, timeZone) {
1052
+ const hourBoundary = (hour) => `${isoDate}T${String(hour).padStart(2, "0")}`;
1053
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
1054
+ if (!match?.[1] || !match[2] || !match[3]) return hourBoundary(0);
1055
+ const year = Number(match[1]);
1056
+ const month = Number(match[2]);
1057
+ const day = Number(match[3]);
1058
+ try {
1059
+ const format = zonedClockFormat(timeZone);
1060
+ for (let hour = 0; hour < HOURS_PER_DAY; hour += 1) {
1061
+ if (localHourExists(format, year, month, day, hour)) return hourBoundary(hour);
1062
+ }
1063
+ return hourBoundary(0);
1064
+ } catch {
1065
+ return hourBoundary(0);
1066
+ }
1067
+ }
963
1068
  function formatDateRange(start, end) {
964
1069
  if (!start && !end) return "";
965
1070
  if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
@@ -6518,6 +6623,8 @@ var researchRunQuerySchema = z37.object({
6518
6623
  groundingSources: z37.array(groundingSourceSchema),
6519
6624
  citedDomains: z37.array(z37.string()),
6520
6625
  searchQueries: z37.array(z37.string()),
6626
+ namedCompetitors: z37.array(z37.string()).default([]),
6627
+ citedCompetitorDomains: z37.array(z37.string()).default([]),
6521
6628
  answerMentioned: z37.boolean().nullable(),
6522
6629
  citationState: citationStateSchema.nullable(),
6523
6630
  error: z37.string().nullable(),
@@ -6742,6 +6849,91 @@ var adsSummaryDtoSchema = z38.object({
6742
6849
  /** Campaign-level rollup totals over the window (levels are not summed across). */
6743
6850
  totals: adsTotalsDtoSchema
6744
6851
  });
6852
+ var adsDeliverySnapshotStatusSchema = z38.enum(["unavailable", "partial", "complete"]);
6853
+ var AdsDeliverySnapshotStatuses = adsDeliverySnapshotStatusSchema.enum;
6854
+ var adsDeliverySnapshotIssueSchema = z38.enum([
6855
+ "no_ads_connection",
6856
+ "connection_not_synced",
6857
+ "no_ads_sync",
6858
+ "entity_rows_missing_sync_run_id",
6859
+ "entity_rows_span_multiple_sync_runs",
6860
+ "source_sync_missing",
6861
+ "source_sync_not_ads_sync",
6862
+ "source_sync_not_completed"
6863
+ ]);
6864
+ var AdsDeliverySnapshotIssues = adsDeliverySnapshotIssueSchema.enum;
6865
+ var adsHistoricalCampaignRollupStatusSchema = z38.enum(["unavailable", "reported"]);
6866
+ var AdsHistoricalCampaignRollupStatuses = adsHistoricalCampaignRollupStatusSchema.enum;
6867
+ var AdsDeliveryConfigurationBases = {
6868
+ storedSnapshot: "stored_ads_snapshot"
6869
+ };
6870
+ var adsActivityAssessmentStateSchema = z38.enum([
6871
+ "unavailable",
6872
+ "partial_snapshot",
6873
+ "metrics_unavailable",
6874
+ "observed_activity",
6875
+ "no_observed_activity"
6876
+ ]);
6877
+ var AdsActivityAssessmentStates = adsActivityAssessmentStateSchema.enum;
6878
+ var adsDeliveryDiagnosticsAdSchema = z38.object({
6879
+ id: z38.string(),
6880
+ name: z38.string(),
6881
+ status: z38.string(),
6882
+ reviewStatus: z38.string().nullable()
6883
+ });
6884
+ var adsDeliveryDiagnosticsAdGroupSchema = z38.object({
6885
+ id: z38.string(),
6886
+ name: z38.string(),
6887
+ status: z38.string(),
6888
+ billingEventType: z38.union([adsAdGroupBillingEventTypeSchema, z38.null()]),
6889
+ maxBidMicros: z38.number().int().nullable(),
6890
+ contextHints: z38.array(z38.string()),
6891
+ ads: z38.array(adsDeliveryDiagnosticsAdSchema)
6892
+ });
6893
+ var adsDeliveryDiagnosticsCampaignSchema = z38.object({
6894
+ id: z38.string(),
6895
+ name: z38.string(),
6896
+ status: z38.string(),
6897
+ biddingType: z38.union([adsCampaignBiddingTypeSchema, z38.null()]),
6898
+ dailySpendLimitMicros: z38.number().int().nullable(),
6899
+ lifetimeSpendLimitMicros: z38.number().int().nullable(),
6900
+ conversionEventSettingIds: z38.array(z38.string()),
6901
+ adGroups: z38.array(adsDeliveryDiagnosticsAdGroupSchema)
6902
+ });
6903
+ var adsDeliveryDiagnosticsDtoSchema = z38.object({
6904
+ snapshot: z38.object({
6905
+ status: adsDeliverySnapshotStatusSchema,
6906
+ // A union keeps the OpenAPI form explicit enough for the generated SDK to
6907
+ // retain the complete-snapshot `null` value (rather than narrowing it to
6908
+ // the issue enum alone).
6909
+ issue: z38.union([adsDeliverySnapshotIssueSchema, z38.null()]),
6910
+ lastSyncedAt: z38.string().nullable(),
6911
+ campaignCount: z38.number().int().nonnegative(),
6912
+ adGroupCount: z38.number().int().nonnegative(),
6913
+ adCount: z38.number().int().nonnegative(),
6914
+ sourceSync: z38.object({
6915
+ runId: z38.string(),
6916
+ status: runStatusSchema
6917
+ }).nullable()
6918
+ }),
6919
+ historicalCampaignRollups: z38.object({
6920
+ status: adsHistoricalCampaignRollupStatusSchema,
6921
+ window: z38.object({ from: z38.string().nullable(), to: z38.string().nullable() }),
6922
+ totals: adsTotalsDtoSchema.nullable()
6923
+ }),
6924
+ storedConfiguration: z38.object({
6925
+ basis: z38.literal(AdsDeliveryConfigurationBases.storedSnapshot),
6926
+ connection: z38.object({
6927
+ status: z38.string().nullable(),
6928
+ reviewStatus: z38.string().nullable(),
6929
+ integrityReviewStatus: z38.string().nullable(),
6930
+ integrityDecision: z38.string().nullable(),
6931
+ conversionTrackingConfigured: z38.boolean()
6932
+ }).nullable(),
6933
+ campaigns: z38.array(adsDeliveryDiagnosticsCampaignSchema)
6934
+ }),
6935
+ assessment: z38.object({ state: adsActivityAssessmentStateSchema })
6936
+ });
6745
6937
  var adsOperationKeySchema = z38.string().min(8).max(128).regex(/^[\w.:-]+$/, "operationKey may contain letters, numbers, dot, underscore, colon, and hyphen");
6746
6938
  var adsEntityIdSchema = z38.string().min(1).max(200);
6747
6939
  var adsNameSchema = z38.string().min(3).max(1e3).refine((value) => value.trim().length > 0);
@@ -7248,6 +7440,122 @@ var adsOperationReconcileResponseSchema = z38.object({
7248
7440
  operation: adsOperationDtoSchema,
7249
7441
  resolved: z38.boolean()
7250
7442
  });
7443
+ var adsLiveDeliveryQuerySchema = z38.object({
7444
+ campaignId: z38.string().min(1).max(200).optional(),
7445
+ lookbackDays: z38.coerce.number().int().min(1).max(30).default(7)
7446
+ });
7447
+ var adsLiveEntityTypeSchema = z38.enum(["campaign", "ad_group", "ad"]);
7448
+ var AdsLiveEntityTypes = adsLiveEntityTypeSchema.enum;
7449
+ var adsLivePresenceSchema = z38.enum(["both", "live-only", "stored-only"]);
7450
+ var AdsLivePresences = adsLivePresenceSchema.enum;
7451
+ var adsLiveDeliveryBasisSchema = z38.literal("live-provider-read");
7452
+ var AdsLiveDeliveryBases = { liveProviderRead: "live-provider-read" };
7453
+ var adsLiveMetricRowSchema = z38.object({
7454
+ date: z38.string().nullable(),
7455
+ startTime: z38.number().nullable(),
7456
+ endTime: z38.number().nullable(),
7457
+ impressions: z38.number().nullable(),
7458
+ clicks: z38.number().nullable(),
7459
+ spend: z38.number().nullable(),
7460
+ conversions: z38.number().nullable(),
7461
+ ctr: z38.number().nullable(),
7462
+ cpc: z38.number().nullable(),
7463
+ cpm: z38.number().nullable()
7464
+ });
7465
+ var adsLiveMetricValuesSchema = z38.object({
7466
+ impressions: z38.number().int(),
7467
+ clicks: z38.number().int(),
7468
+ spendMicros: z38.number().int(),
7469
+ conversions: z38.number().int()
7470
+ });
7471
+ var adsLiveMetricDeltaSchema = z38.object({
7472
+ date: z38.string(),
7473
+ live: adsLiveMetricValuesSchema.nullable(),
7474
+ stored: adsLiveMetricValuesSchema.nullable(),
7475
+ drifted: z38.boolean()
7476
+ });
7477
+ var adsLiveFieldDeltaSchema = z38.object({
7478
+ field: z38.string(),
7479
+ live: z38.string().nullable(),
7480
+ stored: z38.string().nullable()
7481
+ });
7482
+ var adsLiveEntityStateSchema = z38.object({
7483
+ name: z38.string().nullable(),
7484
+ status: z38.string(),
7485
+ reviewStatus: z38.string().nullable(),
7486
+ mode: z38.string().nullable(),
7487
+ updatedAt: z38.number().nullable()
7488
+ });
7489
+ var adsStoredEntityStateSchema = z38.object({
7490
+ name: z38.string(),
7491
+ status: z38.string(),
7492
+ reviewStatus: z38.string().nullable(),
7493
+ upstreamUpdatedAt: z38.number().nullable(),
7494
+ syncedAt: z38.string()
7495
+ });
7496
+ var adsLiveEntityComparisonSchema = z38.object({
7497
+ entityType: adsLiveEntityTypeSchema,
7498
+ id: z38.string(),
7499
+ parentId: z38.string().nullable(),
7500
+ presence: adsLivePresenceSchema,
7501
+ live: adsLiveEntityStateSchema.nullable(),
7502
+ stored: adsStoredEntityStateSchema.nullable(),
7503
+ fieldDeltas: z38.array(adsLiveFieldDeltaSchema),
7504
+ /** Provider rows, unaggregated. Null for ads (no per-ad insights surface). */
7505
+ liveMetrics: z38.array(adsLiveMetricRowSchema).nullable(),
7506
+ metricDeltas: z38.array(adsLiveMetricDeltaSchema).nullable(),
7507
+ drifted: z38.boolean()
7508
+ });
7509
+ var adsLiveReadFailureSchema = z38.object({
7510
+ surface: z38.string(),
7511
+ entityId: z38.string().nullable(),
7512
+ upstreamStatus: z38.number().int().nullable()
7513
+ });
7514
+ var adsLiveDeliveryDtoSchema = z38.object({
7515
+ basis: adsLiveDeliveryBasisSchema,
7516
+ /** Instant the live read was issued; the metrics window is measured back from it. */
7517
+ fetchedAt: z38.string(),
7518
+ adAccountId: z38.string(),
7519
+ storedSnapshotSyncedAt: z38.string().nullable(),
7520
+ metricsWindow: z38.object({ lookbackDays: z38.number().int().positive() }),
7521
+ /**
7522
+ * Two different units live here, and confusing them understates the cost of
7523
+ * this endpoint by two orders of magnitude.
7524
+ *
7525
+ * A READER CALL is one logical list or insight read. Every list/insight
7526
+ * reader call is an auto-paginating walk, so one reader call can issue up to
7527
+ * `maxPagesPerReaderCall` upstream HTTP requests before the client gives up.
7528
+ * `maxUpstreamHttpRequests` is the honest worst-case ceiling in HTTP
7529
+ * requests (`maxReaderCalls * maxPagesPerReaderCall`, about 4000 at the
7530
+ * shipped defaults, not 40).
7531
+ *
7532
+ * `readerCalls` is an observed count of reader calls. There is deliberately
7533
+ * no observed HTTP-request count: the pagination happens inside the provider
7534
+ * client, below the injected reader seam, so the route cannot see it.
7535
+ */
7536
+ bounds: z38.object({
7537
+ maxCampaigns: z38.number().int().positive(),
7538
+ maxAdGroupsPerCampaign: z38.number().int().positive(),
7539
+ maxAdsPerAdGroup: z38.number().int().positive(),
7540
+ /** Budget in logical reader calls, NOT in upstream HTTP requests. */
7541
+ maxReaderCalls: z38.number().int().positive(),
7542
+ /** Reader calls this read actually issued. Observed, not a bound. */
7543
+ readerCalls: z38.number().int().nonnegative(),
7544
+ /** Pages one paginated reader call may fetch before the client gives up. */
7545
+ maxPagesPerReaderCall: z38.number().int().positive(),
7546
+ /** Worst-case upstream HTTP requests. A documented bound, not an observation. */
7547
+ maxUpstreamHttpRequests: z38.number().int().positive(),
7548
+ truncated: z38.boolean()
7549
+ }),
7550
+ entities: z38.array(adsLiveEntityComparisonSchema),
7551
+ drift: z38.object({
7552
+ entitiesCompared: z38.number().int().nonnegative(),
7553
+ driftedEntities: z38.number().int().nonnegative(),
7554
+ statusDrifted: z38.number().int().nonnegative(),
7555
+ metricsDrifted: z38.number().int().nonnegative()
7556
+ }),
7557
+ errors: z38.array(adsLiveReadFailureSchema)
7558
+ });
7251
7559
  function adsCtr(clicks, impressions) {
7252
7560
  return impressions > 0 ? clicks / impressions : null;
7253
7561
  }
@@ -7498,6 +7806,9 @@ export {
7498
7806
  formatNumber,
7499
7807
  formatDate,
7500
7808
  formatIsoDate,
7809
+ formatIsoDateInTimeZone,
7810
+ isoDateDaysBeforeInTimeZone,
7811
+ startOfDayHourInTimeZone,
7501
7812
  formatDateRange,
7502
7813
  parseInclusiveEndMs,
7503
7814
  deltaPercent,
@@ -7776,8 +8087,15 @@ export {
7776
8087
  AdsAdGroupBillingEventTypes,
7777
8088
  adsCampaignListResponseSchema,
7778
8089
  adsInsightLevelSchema,
8090
+ AdsInsightLevels,
7779
8091
  adsInsightsResponseSchema,
7780
8092
  adsSummaryDtoSchema,
8093
+ AdsDeliverySnapshotStatuses,
8094
+ AdsDeliverySnapshotIssues,
8095
+ AdsHistoricalCampaignRollupStatuses,
8096
+ AdsDeliveryConfigurationBases,
8097
+ AdsActivityAssessmentStates,
8098
+ adsDeliveryDiagnosticsDtoSchema,
7781
8099
  AdsOperationKinds,
7782
8100
  AdsOperationStates,
7783
8101
  AdsReconcileStrategies,
@@ -7810,6 +8128,11 @@ export {
7810
8128
  adsUnresolvedOperationListResponseSchema,
7811
8129
  adsOperationReconcileRequestSchema,
7812
8130
  adsOperationReconcileResponseSchema,
8131
+ adsLiveDeliveryQuerySchema,
8132
+ AdsLiveEntityTypes,
8133
+ AdsLivePresences,
8134
+ AdsLiveDeliveryBases,
8135
+ adsLiveDeliveryDtoSchema,
7813
8136
  adsCtr,
7814
8137
  adsCpcMicros,
7815
8138
  dollarsToMicros,
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-JTJQZ4UC.js";
13
+ } from "./chunk-ZUQPHGEN.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -40,6 +40,7 @@ import {
40
40
  classifyAiUserFetch,
41
41
  classifyCrawler,
42
42
  competitors,
43
+ computeCitedCompetitorDomains,
43
44
  computeCompetitorOverlap,
44
45
  countAttributes,
45
46
  countPopulatedGroups,
@@ -118,7 +119,7 @@ import {
118
119
  siteAuditPages,
119
120
  siteAuditSnapshots,
120
121
  usageCounters
121
- } from "./chunk-IEZTETUZ.js";
122
+ } from "./chunk-LIXWDIDR.js";
122
123
  import {
123
124
  AGENT_MEMORY_VALUE_MAX_BYTES,
124
125
  AGENT_PROVIDER_IDS,
@@ -188,10 +189,11 @@ import {
188
189
  serializeRunError,
189
190
  skillsClientSchema,
190
191
  splitList,
192
+ startOfDayHourInTimeZone,
191
193
  validationError,
192
194
  winnabilityClassLabel,
193
195
  withRetry
194
- } from "./chunk-ZPQX5ZQ2.js";
196
+ } from "./chunk-PC7AYXND.js";
195
197
 
196
198
  // src/telemetry.ts
197
199
  import crypto from "crypto";
@@ -4877,14 +4879,22 @@ function accountHour(date, timezone) {
4877
4879
  };
4878
4880
  return `${value("year")}-${value("month")}-${value("day")}T${value("hour")}`;
4879
4881
  }
4880
- function trailingAdsInsightHourRange(now, timezone) {
4882
+ function trailingAdsInsightHourRange(now, timezone, lookbackMs = INSIGHTS_LOOKBACK_MS) {
4881
4883
  return {
4882
4884
  type: "hour_range",
4883
- since: accountHour(new Date(now.getTime() - INSIGHTS_LOOKBACK_MS), timezone),
4885
+ since: accountHour(new Date(now.getTime() - lookbackMs), timezone),
4884
4886
  until: accountHour(now, timezone),
4885
4887
  timezone
4886
4888
  };
4887
4889
  }
4890
+ function liveAdsInsightHourRange(request) {
4891
+ return {
4892
+ type: "hour_range",
4893
+ since: startOfDayHourInTimeZone(request.startDate, request.timezone),
4894
+ until: accountHour(new Date(request.fetchedAtMs), request.timezone),
4895
+ timezone: request.timezone
4896
+ };
4897
+ }
4888
4898
  function toInsightUpserts(level, entityId, rows) {
4889
4899
  const upserts = [];
4890
4900
  for (const row of rows) {
@@ -7000,7 +7010,7 @@ function readStoredGroundingSources(rawResponse) {
7000
7010
  return result;
7001
7011
  }
7002
7012
  async function backfillInsightsCommand(project, opts) {
7003
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-WZNE23TV.js");
7013
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4XOFUN5E.js");
7004
7014
  const config = loadConfig();
7005
7015
  const db = createClient(config.database);
7006
7016
  migrate(db);
@@ -9113,6 +9123,8 @@ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
9113
9123
  CanonryMcpToolNames.canonry_ads_campaigns,
9114
9124
  CanonryMcpToolNames.canonry_ads_insights,
9115
9125
  CanonryMcpToolNames.canonry_ads_summary,
9126
+ CanonryMcpToolNames.canonry_ads_delivery_diagnostics,
9127
+ CanonryMcpToolNames.canonry_ads_live_delivery,
9116
9128
  CanonryMcpToolNames.canonry_ads_operations_unresolved,
9117
9129
  CanonryMcpToolNames.canonry_ads_operation_get,
9118
9130
  CanonryMcpToolNames.canonry_ads_operation_reconcile,
@@ -9234,7 +9246,7 @@ function buildAdsOperatorContextTool(ctx) {
9234
9246
  return {
9235
9247
  name: AERO_ADS_OPERATOR_CONTEXT_TOOL_NAME,
9236
9248
  label: "Get ads operator context",
9237
- description: "One-call Aero context pack for ads operations. Reads the project overview, ads connection, paid summary, bounded campaign snapshots, recent paid rollups, ads doctor checks, and recent Aero memory. Use before diagnosing ChatGPT ads performance or planning the next operator action.",
9249
+ description: "One-call Aero context pack for ads operations. Reads the project overview, stored ads delivery diagnostics, ads connection, paid summary, bounded campaign snapshots, recent paid rollups, ads doctor checks, and recent Aero memory. Use before diagnosing ChatGPT ads performance or planning the next operator action.",
9238
9250
  parameters: Type3.Object({
9239
9251
  windowDays: Type3.Optional(Type3.Integer({
9240
9252
  minimum: 1,
@@ -9262,6 +9274,7 @@ function buildAdsOperatorContextTool(ctx) {
9262
9274
  const [
9263
9275
  overview,
9264
9276
  adsStatus,
9277
+ deliveryDiagnostics,
9265
9278
  adsSummary,
9266
9279
  campaigns,
9267
9280
  campaignInsights,
@@ -9271,6 +9284,7 @@ function buildAdsOperatorContextTool(ctx) {
9271
9284
  ] = await Promise.all([
9272
9285
  readSection(() => ctx.client.getProjectOverview(ctx.projectName)),
9273
9286
  readSection(() => ctx.client.getAdsStatus(ctx.projectName)),
9287
+ readSection(() => ctx.client.getAdsDeliveryDiagnostics(ctx.projectName)),
9274
9288
  readSection(() => ctx.client.getAdsSummary(ctx.projectName)),
9275
9289
  readSection(async () => compactCampaigns(await ctx.client.getAdsCampaigns(ctx.projectName), campaignLimit)),
9276
9290
  readSection(
@@ -9298,6 +9312,7 @@ function buildAdsOperatorContextTool(ctx) {
9298
9312
  overview: overview.status === "ok" && overview.data ? { status: "ok", data: compactOverview(overview.data) } : overview,
9299
9313
  ads: {
9300
9314
  status: adsStatus,
9315
+ deliveryDiagnostics,
9301
9316
  summary: adsSummary,
9302
9317
  campaigns,
9303
9318
  insights: {
@@ -11588,6 +11603,14 @@ async function executeResearchRun(db, registry, runId, projectId) {
11588
11603
  return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
11589
11604
  });
11590
11605
  const normalized = provider.adapter.normalizeResult(raw);
11606
+ const namedCompetitors = extractRecommendedCompetitors(
11607
+ normalized.answerText,
11608
+ domains,
11609
+ normalized.citedDomains,
11610
+ competitorDomains,
11611
+ brands
11612
+ );
11613
+ const citedCompetitorDomains = computeCitedCompetitorDomains(normalized.citedDomains, competitorDomains);
11591
11614
  const completed = db.update(researchRunQueries).set({
11592
11615
  status: ResearchQueryStatuses.completed,
11593
11616
  servedModel: raw.servedModel ?? null,
@@ -11595,6 +11618,8 @@ async function executeResearchRun(db, registry, runId, projectId) {
11595
11618
  groundingSources: normalized.groundingSources,
11596
11619
  citedDomains: normalized.citedDomains,
11597
11620
  searchQueries: normalized.searchQueries,
11621
+ namedCompetitors,
11622
+ citedCompetitorDomains,
11598
11623
  answerMentioned: determineAnswerMentioned(normalized.answerText, brands, domains),
11599
11624
  citationState: determineCitationState(normalized, domains),
11600
11625
  rawResponse: raw.rawResponse,
@@ -12200,6 +12225,42 @@ async function createServer(opts) {
12200
12225
  activateAd: async (apiKey, id) => adsAdEntityResult(await activateAd(apiKey, id)),
12201
12226
  pauseAd: async (apiKey, id) => adsAdEntityResult(await pauseAd(apiKey, id))
12202
12227
  };
12228
+ const adsLiveEntity = (entity, extra) => ({
12229
+ id: entity.id,
12230
+ name: entity.name,
12231
+ status: entity.status,
12232
+ reviewStatus: extra?.reviewStatus ?? null,
12233
+ mode: extra?.mode ?? null,
12234
+ updatedAt: entity.updated_at
12235
+ });
12236
+ const adsLiveMetricRow = (row) => ({
12237
+ date: row.readable_time ?? null,
12238
+ startTime: row.start_time ?? null,
12239
+ endTime: row.end_time ?? null,
12240
+ impressions: row.impressions ?? null,
12241
+ clicks: row.clicks ?? null,
12242
+ // Provider units: the insights API returns decimal currency for spend.
12243
+ spend: row.spend ?? null,
12244
+ conversions: row.conversions ?? null,
12245
+ ctr: row.ctr ?? null,
12246
+ cpc: row.cpc ?? null,
12247
+ cpm: row.cpm ?? null
12248
+ });
12249
+ const adsLiveDeliveryReader = {
12250
+ listCampaigns: async (apiKey) => (await listCampaigns(apiKey)).map((campaign) => adsLiveEntity(campaign, { mode: campaign.mode })),
12251
+ listAdGroups: async (apiKey, campaignId) => (await listAdGroups(apiKey, campaignId)).map((adGroup) => adsLiveEntity(adGroup)),
12252
+ listAds: async (apiKey, adGroupId) => (await listAds(apiKey, adGroupId)).map(
12253
+ (ad) => adsLiveEntity(ad, { reviewStatus: ad.review_status ?? ad.review?.status ?? null })
12254
+ ),
12255
+ campaignInsights: async (apiKey, campaignId, request) => (await getCampaignInsights(apiKey, campaignId, {
12256
+ fields: CAMPAIGN_INSIGHT_FIELDS,
12257
+ timeRanges: [liveAdsInsightHourRange(request)]
12258
+ })).map(adsLiveMetricRow),
12259
+ adGroupInsights: async (apiKey, adGroupId, request) => (await getAdGroupInsights(apiKey, adGroupId, {
12260
+ fields: AD_GROUP_INSIGHT_FIELDS,
12261
+ timeRanges: [liveAdsInsightHourRange(request)]
12262
+ })).map(adsLiveMetricRow)
12263
+ };
12203
12264
  const scheduler = new Scheduler(opts.db, {
12204
12265
  onRunCreated: (runId, projectId, providers2, location) => {
12205
12266
  jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
@@ -12799,6 +12860,11 @@ async function createServer(opts) {
12799
12860
  adsCredentialStore,
12800
12861
  verifyAdsAccount,
12801
12862
  adsReader,
12863
+ adsLiveDeliveryReader,
12864
+ // Reporting only: lets the route state the true upstream HTTP ceiling
12865
+ // (reader-call budget x pages per reader call) instead of implying that a
12866
+ // reader call is one request.
12867
+ adsLiveDeliveryMaxPagesPerReaderCall: OPENAI_ADS_MAX_PAGES,
12802
12868
  adsOperator,
12803
12869
  onAdsSyncRequested: (runId, projectId) => {
12804
12870
  runAdsSync(runId, projectId);
@@ -42,7 +42,7 @@ import {
42
42
  trafficConnectWordpressRequestSchema,
43
43
  trafficEventKindSchema,
44
44
  trafficSeriesGranularitySchema
45
- } from "./chunk-ZPQX5ZQ2.js";
45
+ } from "./chunk-PC7AYXND.js";
46
46
 
47
47
  // src/config.ts
48
48
  import fs from "fs";
@@ -2727,6 +2727,30 @@ var getApiV1ProjectsByNameAdsSummary = (options) => {
2727
2727
  ...options
2728
2728
  });
2729
2729
  };
2730
+ var getApiV1ProjectsByNameAdsDeliveryDiagnostics = (options) => {
2731
+ return (options.client ?? client).get({
2732
+ security: [
2733
+ {
2734
+ scheme: "bearer",
2735
+ type: "http"
2736
+ }
2737
+ ],
2738
+ url: "/api/v1/projects/{name}/ads/delivery-diagnostics",
2739
+ ...options
2740
+ });
2741
+ };
2742
+ var getApiV1ProjectsByNameAdsLiveDelivery = (options) => {
2743
+ return (options.client ?? client).get({
2744
+ security: [
2745
+ {
2746
+ scheme: "bearer",
2747
+ type: "http"
2748
+ }
2749
+ ],
2750
+ url: "/api/v1/projects/{name}/ads/live-delivery",
2751
+ ...options
2752
+ });
2753
+ };
2730
2754
  var postApiV1ProjectsByNameBingConnect = (options) => {
2731
2755
  return (options.client ?? client).post({
2732
2756
  security: [
@@ -4854,6 +4878,23 @@ var ApiClient = class {
4854
4878
  () => getApiV1ProjectsByNameAdsSummary({ client: this.heyClient, path: { name: project } })
4855
4879
  );
4856
4880
  }
4881
+ async getAdsDeliveryDiagnostics(project) {
4882
+ return this.invoke(
4883
+ () => getApiV1ProjectsByNameAdsDeliveryDiagnostics({ client: this.heyClient, path: { name: project } })
4884
+ );
4885
+ }
4886
+ async getAdsLiveDelivery(project, opts) {
4887
+ return this.invoke(
4888
+ () => getApiV1ProjectsByNameAdsLiveDelivery({
4889
+ client: this.heyClient,
4890
+ path: { name: project },
4891
+ query: {
4892
+ ...opts?.campaignId === void 0 ? {} : { campaignId: opts.campaignId },
4893
+ ...opts?.lookbackDays === void 0 ? {} : { lookbackDays: opts.lookbackDays }
4894
+ }
4895
+ })
4896
+ );
4897
+ }
4857
4898
  async getAdsOperation(project, operationKey) {
4858
4899
  return this.invoke(
4859
4900
  () => getApiV1ProjectsByNameAdsOperationsByOperationKey({
@@ -6152,6 +6193,11 @@ var adsInsightsInputSchema = z2.object({
6152
6193
  var adsGeoSearchInputSchema = adsGeoSearchQuerySchema.extend({
6153
6194
  project: projectNameSchema
6154
6195
  });
6196
+ var adsLiveDeliveryInputSchema = z2.object({
6197
+ project: projectNameSchema,
6198
+ campaignId: z2.string().min(1).max(200).optional(),
6199
+ lookbackDays: z2.number().int().min(1).max(30).optional()
6200
+ });
6155
6201
  var adsOperationInputSchema = z2.object({
6156
6202
  project: projectNameSchema,
6157
6203
  operationKey: z2.string().min(8).max(128)
@@ -7693,7 +7739,7 @@ var canonryMcpTools = [
7693
7739
  defineTool({
7694
7740
  name: "canonry_research_run_get",
7695
7741
  title: "Get research query run",
7696
- description: "Get the saved per-query answers, sources, cited domains, and independent mention/citation results for one research run. It is read-only and does not promote or track any query.",
7742
+ description: "Get saved per-query answers, source links, cited domains, answer-text named competitors, cited competitor domains, and independent mention/citation results for one research run. It is read-only and does not promote or track any query.",
7697
7743
  access: "read",
7698
7744
  tier: "discovery",
7699
7745
  inputSchema: researchRunIdInputSchema,
@@ -7912,6 +7958,31 @@ var canonryMcpTools = [
7912
7958
  openApiOperations: ["GET /api/v1/projects/{name}/ads/summary"],
7913
7959
  handler: (client2, input) => client2.getAdsSummary(input.project)
7914
7960
  }),
7961
+ defineTool({
7962
+ name: "canonry_ads_delivery_diagnostics",
7963
+ title: "Stored ads delivery diagnostics",
7964
+ description: "Read stored ads snapshot provenance, account/campaign/ad-group/ad configuration facts, and historical campaign activity in one call. This is not a live OpenAI eligibility or serving verdict.",
7965
+ access: "read",
7966
+ tier: "ads",
7967
+ inputSchema: projectInputSchema,
7968
+ annotations: readAnnotations(),
7969
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/delivery-diagnostics"],
7970
+ handler: (client2, input) => client2.getAdsDeliveryDiagnostics(input.project)
7971
+ }),
7972
+ defineTool({
7973
+ name: "canonry_ads_live_delivery",
7974
+ title: "Live ads read with stored-snapshot delta",
7975
+ description: "Call the OpenAI Ads API right now and return the provider's current status and metrics per campaign / ad group / ad, unaggregated, alongside the stored snapshot values and an explicit per-entity delta. Use this when stored data is suspected of being stale or contradicted by the advertiser UI. Read-only: it mutates nothing and does not wait for a sync. The walk is bounded and one project may issue at most one live read per minute, so prefer canonry_ads_delivery_diagnostics for routine checks.",
7976
+ access: "read",
7977
+ tier: "ads",
7978
+ inputSchema: adsLiveDeliveryInputSchema,
7979
+ annotations: readAnnotations(),
7980
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/live-delivery"],
7981
+ handler: (client2, input) => client2.getAdsLiveDelivery(input.project, {
7982
+ campaignId: input.campaignId,
7983
+ lookbackDays: input.lookbackDays
7984
+ })
7985
+ }),
7915
7986
  defineTool({
7916
7987
  name: "canonry_ads_operations_unresolved",
7917
7988
  title: "List unresolved ads mutation receipts",