@canonry/canonry 4.134.0 → 4.135.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 (27) hide show
  1. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +3 -2
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +34 -6
  3. package/assets/assets/{AuditHistoryPanel-DBUWbfNk.js → AuditHistoryPanel-BS0dmlrQ.js} +1 -1
  4. package/assets/assets/{BacklinksPage-BFm0VdUO.js → BacklinksPage-CPpWLVfa.js} +1 -1
  5. package/assets/assets/{ChartPrimitives-oV_nduTH.js → ChartPrimitives-Dv--2i87.js} +1 -1
  6. package/assets/assets/{HistoryPage-IF2FMem3.js → HistoryPage-BO4Fv-9q.js} +1 -1
  7. package/assets/assets/ProjectPage-Di_nDqtH.js +8 -0
  8. package/assets/assets/{RunRow-De5zdch5.js → RunRow-BE8zdGJ8.js} +1 -1
  9. package/assets/assets/{RunsPage-xtRF78r8.js → RunsPage-CbGanhaz.js} +1 -1
  10. package/assets/assets/{SettingsPage-B_GAxaWu.js → SettingsPage-odOpsOuf.js} +1 -1
  11. package/assets/assets/{TrafficPage-E8I5ineJ.js → TrafficPage-DK7oc0DK.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-DGk2MqSp.js → TrafficSourceDetailPage-CEFjQVuF.js} +1 -1
  13. package/assets/assets/{arrow-left-D-4QMxi9.js → arrow-left-DgzLEzZB.js} +1 -1
  14. package/assets/assets/{extract-error-message-DuqIFU8Z.js → extract-error-message-vlMjpZb2.js} +1 -1
  15. package/assets/assets/{index-De1MKbc7.js → index-BMlTgq7D.js} +72 -72
  16. package/assets/assets/{trash-2-BVh__r6G.js → trash-2-DX33Ys-K.js} +1 -1
  17. package/assets/index.html +1 -1
  18. package/dist/{chunk-TKXWTDV6.js → chunk-3PFRVEWP.js} +99 -31
  19. package/dist/{chunk-ZUQPHGEN.js → chunk-PAR6P5H6.js} +35 -3
  20. package/dist/{chunk-PC7AYXND.js → chunk-PXEEWJRV.js} +74 -20
  21. package/dist/{chunk-LIXWDIDR.js → chunk-TPVMHYHD.js} +188 -97
  22. package/dist/cli.js +63 -26
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-4XOFUN5E.js → intelligence-service-BMEJ5GFZ.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +9 -9
  27. package/assets/assets/ProjectPage-ClwxX9Ga.js +0 -8
@@ -1 +1 @@
1
- import{c as a}from"./index-De1MKbc7.js";const c=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],d=a("circle-check",c);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],k=a("circle-question-mark",e);const t=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=a("download",t);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],r=a("external-link",o);const n=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],y=a("trash-2",n);export{d as C,s as D,r as E,y as T,k as a};
1
+ import{c as a}from"./index-BMlTgq7D.js";const c=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],d=a("circle-check",c);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],k=a("circle-question-mark",e);const t=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=a("download",t);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],r=a("external-link",o);const n=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],y=a("trash-2",n);export{d as C,s as D,r as E,y as T,k as a};
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-De1MKbc7.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-BMlTgq7D.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-DSxTZhaH.js">
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-ZUQPHGEN.js";
13
+ } from "./chunk-PAR6P5H6.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -119,7 +119,7 @@ import {
119
119
  siteAuditPages,
120
120
  siteAuditSnapshots,
121
121
  usageCounters
122
- } from "./chunk-LIXWDIDR.js";
122
+ } from "./chunk-TPVMHYHD.js";
123
123
  import {
124
124
  AGENT_MEMORY_VALUE_MAX_BYTES,
125
125
  AGENT_PROVIDER_IDS,
@@ -171,6 +171,7 @@ import {
171
171
  effectiveDomains,
172
172
  embedClientConfigForRequest,
173
173
  factorStatusFromScore,
174
+ formatIsoDateInTimeZone,
174
175
  frameAncestorsHeaderValue,
175
176
  hostOf,
176
177
  isAgentProviderId,
@@ -193,7 +194,7 @@ import {
193
194
  validationError,
194
195
  winnabilityClassLabel,
195
196
  withRetry
196
- } from "./chunk-PC7AYXND.js";
197
+ } from "./chunk-PXEEWJRV.js";
197
198
 
198
199
  // src/telemetry.ts
199
200
  import crypto from "crypto";
@@ -4658,10 +4659,10 @@ async function adsFetch(apiKey, path10, queryPairs = [], method = "GET", body) {
4658
4659
  throw new OpenAiAdsApiError("OpenAI Ads API returned invalid JSON", 502);
4659
4660
  }
4660
4661
  }
4661
- async function fetchAllPages(apiKey, path10, queryPairs) {
4662
+ async function fetchAllPages(apiKey, path10, queryPairs, maxPages = OPENAI_ADS_MAX_PAGES) {
4662
4663
  const items = [];
4663
4664
  let after = null;
4664
- for (let page = 0; page < OPENAI_ADS_MAX_PAGES; page++) {
4665
+ for (let page = 0; page < maxPages; page++) {
4665
4666
  const pairs = after ? [...queryPairs, `after=${encodeURIComponent(after)}`] : [...queryPairs];
4666
4667
  const response = await adsFetch(apiKey, path10, pairs);
4667
4668
  items.push(...response.data);
@@ -4670,6 +4671,7 @@ async function fetchAllPages(apiKey, path10, queryPairs) {
4670
4671
  }
4671
4672
  after = response.last_id;
4672
4673
  }
4674
+ if (maxPages < OPENAI_ADS_MAX_PAGES) return items;
4673
4675
  adsClientLog("error", "pagination.cap-reached", { path: path10, pages: OPENAI_ADS_MAX_PAGES, items: items.length });
4674
4676
  throw new OpenAiAdsApiError(
4675
4677
  `OpenAI Ads API collection remained incomplete after the ${OPENAI_ADS_MAX_PAGES}-page safety cap`,
@@ -4683,6 +4685,9 @@ function insightsPairs(opts) {
4683
4685
  ...(opts?.timeRanges ?? []).map((range) => `time_ranges[]=${encodeURIComponent(JSON.stringify(range))}`)
4684
4686
  ];
4685
4687
  }
4688
+ function insightsMaxPages(opts) {
4689
+ return opts?.firstPageOnly ? 1 : OPENAI_ADS_MAX_PAGES;
4690
+ }
4686
4691
  async function getAdAccount(apiKey) {
4687
4692
  validateApiKey(apiKey);
4688
4693
  return adsFetch(apiKey, "ad_account");
@@ -4813,7 +4818,8 @@ async function getCampaignInsights(apiKey, campaignId, opts) {
4813
4818
  return fetchAllPages(
4814
4819
  apiKey,
4815
4820
  `campaigns/${encodeURIComponent(campaignId)}/insights`,
4816
- insightsPairs(opts)
4821
+ insightsPairs(opts),
4822
+ insightsMaxPages(opts)
4817
4823
  );
4818
4824
  }
4819
4825
  async function getAdGroupInsights(apiKey, adGroupId, opts) {
@@ -4822,7 +4828,8 @@ async function getAdGroupInsights(apiKey, adGroupId, opts) {
4822
4828
  return fetchAllPages(
4823
4829
  apiKey,
4824
4830
  `ad_groups/${encodeURIComponent(adGroupId)}/insights`,
4825
- insightsPairs(opts)
4831
+ insightsPairs(opts),
4832
+ insightsMaxPages(opts)
4826
4833
  );
4827
4834
  }
4828
4835
 
@@ -4862,6 +4869,8 @@ function removeOpenAiAdsConnection(config, projectName) {
4862
4869
  var log4 = createLogger("AdsSync");
4863
4870
  var CAMPAIGN_INSIGHT_FIELDS = ["campaign.impressions", "campaign.clicks", "campaign.spend", "campaign.conversions", "metadata.readable_time"];
4864
4871
  var AD_GROUP_INSIGHT_FIELDS = ["ad_group.impressions", "ad_group.clicks", "ad_group.spend", "ad_group.conversions", "metadata.readable_time"];
4872
+ var CAMPAIGN_IN_PROGRESS_INSIGHT_FIELDS = CAMPAIGN_INSIGHT_FIELDS.filter((field) => field !== "campaign.conversions");
4873
+ var AD_GROUP_IN_PROGRESS_INSIGHT_FIELDS = AD_GROUP_INSIGHT_FIELDS.filter((field) => field !== "ad_group.conversions");
4865
4874
  var INSIGHTS_LOOKBACK_MS = 90 * 24 * 60 * 60 * 1e3;
4866
4875
  function accountHour(date, timezone) {
4867
4876
  const parts = new Intl.DateTimeFormat("en-CA", {
@@ -4879,11 +4888,17 @@ function accountHour(date, timezone) {
4879
4888
  };
4880
4889
  return `${value("year")}-${value("month")}-${value("day")}T${value("hour")}`;
4881
4890
  }
4891
+ function accountLocalDate(at, timezone) {
4892
+ return formatIsoDateInTimeZone(at.toISOString(), timezone);
4893
+ }
4894
+ function startOfAccountDay(at, timezone) {
4895
+ return startOfDayHourInTimeZone(accountLocalDate(at, timezone), timezone);
4896
+ }
4882
4897
  function trailingAdsInsightHourRange(now, timezone, lookbackMs = INSIGHTS_LOOKBACK_MS) {
4883
4898
  return {
4884
4899
  type: "hour_range",
4885
4900
  since: accountHour(new Date(now.getTime() - lookbackMs), timezone),
4886
- until: accountHour(now, timezone),
4901
+ until: startOfAccountDay(now, timezone),
4887
4902
  timezone
4888
4903
  };
4889
4904
  }
@@ -4891,11 +4906,21 @@ function liveAdsInsightHourRange(request) {
4891
4906
  return {
4892
4907
  type: "hour_range",
4893
4908
  since: startOfDayHourInTimeZone(request.startDate, request.timezone),
4894
- until: accountHour(new Date(request.fetchedAtMs), request.timezone),
4909
+ until: startOfAccountDay(new Date(request.fetchedAtMs), request.timezone),
4895
4910
  timezone: request.timezone
4896
4911
  };
4897
4912
  }
4898
- function toInsightUpserts(level, entityId, rows) {
4913
+ async function readInsightDays(input) {
4914
+ const [closedDays, unranged] = await Promise.all([
4915
+ input.read({ fields: [...input.rangedFields], timeRanges: [...input.timeRanges] }),
4916
+ input.read({ fields: [...input.inProgressFields], firstPageOnly: true })
4917
+ ]);
4918
+ return {
4919
+ closedDays,
4920
+ inProgressDay: unranged.find((row) => row.readable_time === input.inProgressDate) ?? null
4921
+ };
4922
+ }
4923
+ function toInsightUpserts(level, entityId, rows, opts) {
4899
4924
  const upserts = [];
4900
4925
  for (const row of rows) {
4901
4926
  if (!row.readable_time) {
@@ -4909,11 +4934,22 @@ function toInsightUpserts(level, entityId, rows) {
4909
4934
  impressions: row.impressions ?? 0,
4910
4935
  clicks: row.clicks ?? 0,
4911
4936
  spendMicros: dollarsToMicros(row.spend ?? 0),
4912
- conversions: Math.round(row.conversions ?? 0)
4937
+ conversions: opts.conversionsReported ? Math.round(row.conversions ?? 0) : null
4913
4938
  });
4914
4939
  }
4915
4940
  return upserts;
4916
4941
  }
4942
+ function toDailyUpserts(level, entityId, read) {
4943
+ return [
4944
+ ...toInsightUpserts(level, entityId, read.closedDays, { conversionsReported: true }),
4945
+ ...toInsightUpserts(
4946
+ level,
4947
+ entityId,
4948
+ read.inProgressDay ? [read.inProgressDay] : [],
4949
+ { conversionsReported: false }
4950
+ )
4951
+ ];
4952
+ }
4917
4953
  async function executeAdsSync(db, runId, projectId, opts) {
4918
4954
  const now = (/* @__PURE__ */ new Date()).toISOString();
4919
4955
  db.update(runs).set({ status: "running", startedAt: now }).where(eq5(runs.id, runId)).run();
@@ -4932,7 +4968,9 @@ async function executeAdsSync(db, runId, projectId, opts) {
4932
4968
  log4.info("sync.start", { runId, projectId, adAccountId: connRow.adAccountId });
4933
4969
  const account = await getAdAccount(apiKey);
4934
4970
  const campaigns = await listCampaigns(apiKey);
4935
- const insightTimeRanges = [trailingAdsInsightHourRange(/* @__PURE__ */ new Date(), account.timezone)];
4971
+ const insightAnchor = /* @__PURE__ */ new Date();
4972
+ const insightTimeRanges = [trailingAdsInsightHourRange(insightAnchor, account.timezone)];
4973
+ const inProgressDate = accountLocalDate(insightAnchor, account.timezone);
4936
4974
  const errors = /* @__PURE__ */ new Map();
4937
4975
  const adGroupsByCampaign = /* @__PURE__ */ new Map();
4938
4976
  const adsByGroup = /* @__PURE__ */ new Map();
@@ -4942,25 +4980,31 @@ async function executeAdsSync(db, runId, projectId, opts) {
4942
4980
  try {
4943
4981
  const [adGroups, campaignInsights] = await Promise.all([
4944
4982
  listAdGroups(apiKey, campaign.id),
4945
- getCampaignInsights(apiKey, campaign.id, {
4946
- fields: CAMPAIGN_INSIGHT_FIELDS,
4947
- timeRanges: insightTimeRanges
4983
+ readInsightDays({
4984
+ read: (options) => getCampaignInsights(apiKey, campaign.id, options),
4985
+ rangedFields: CAMPAIGN_INSIGHT_FIELDS,
4986
+ inProgressFields: CAMPAIGN_IN_PROGRESS_INSIGHT_FIELDS,
4987
+ timeRanges: insightTimeRanges,
4988
+ inProgressDate
4948
4989
  })
4949
4990
  ]);
4950
4991
  const groupResults = await Promise.all(adGroups.map(async (group) => ({
4951
4992
  group,
4952
4993
  ads: await listAds(apiKey, group.id),
4953
- insights: await getAdGroupInsights(apiKey, group.id, {
4954
- fields: AD_GROUP_INSIGHT_FIELDS,
4955
- timeRanges: insightTimeRanges
4994
+ insights: await readInsightDays({
4995
+ read: (options) => getAdGroupInsights(apiKey, group.id, options),
4996
+ rangedFields: AD_GROUP_INSIGHT_FIELDS,
4997
+ inProgressFields: AD_GROUP_IN_PROGRESS_INSIGHT_FIELDS,
4998
+ timeRanges: insightTimeRanges,
4999
+ inProgressDate
4956
5000
  })
4957
5001
  })));
4958
5002
  syncedCampaigns.push(campaign);
4959
5003
  adGroupsByCampaign.set(campaign.id, adGroups);
4960
- insightUpserts.push(...toInsightUpserts("campaign", campaign.id, campaignInsights));
5004
+ insightUpserts.push(...toDailyUpserts("campaign", campaign.id, campaignInsights));
4961
5005
  for (const { group, ads, insights: insights2 } of groupResults) {
4962
5006
  adsByGroup.set(group.id, ads);
4963
- insightUpserts.push(...toInsightUpserts("ad_group", group.id, insights2));
5007
+ insightUpserts.push(...toDailyUpserts("ad_group", group.id, insights2));
4964
5008
  }
4965
5009
  } catch (err) {
4966
5010
  errors.set(campaign.name, err instanceof Error ? err.message : String(err));
@@ -5029,7 +5073,16 @@ async function executeAdsSync(db, runId, projectId, opts) {
5029
5073
  tx.insert(adsInsightsDaily).values({
5030
5074
  id: crypto7.randomUUID(),
5031
5075
  projectId,
5032
- ...upsert,
5076
+ level: upsert.level,
5077
+ entityId: upsert.entityId,
5078
+ date: upsert.date,
5079
+ impressions: upsert.impressions,
5080
+ clicks: upsert.clicks,
5081
+ spendMicros: upsert.spendMicros,
5082
+ // A day whose conversions the provider will not report yet starts at
5083
+ // the column default. The first sync after that day closes reads it
5084
+ // from the ranged call and fills it in.
5085
+ conversions: upsert.conversions ?? 0,
5033
5086
  syncRunId: runId
5034
5087
  }).onConflictDoUpdate({
5035
5088
  target: [adsInsightsDaily.projectId, adsInsightsDaily.level, adsInsightsDaily.entityId, adsInsightsDaily.date],
@@ -5037,7 +5090,10 @@ async function executeAdsSync(db, runId, projectId, opts) {
5037
5090
  impressions: upsert.impressions,
5038
5091
  clicks: upsert.clicks,
5039
5092
  spendMicros: upsert.spendMicros,
5040
- conversions: upsert.conversions,
5093
+ // Only written when the provider actually reported a count.
5094
+ // Stamping a placeholder over a stored figure would turn a number
5095
+ // this read could not obtain into a number that is simply wrong.
5096
+ ...upsert.conversions === null ? {} : { conversions: upsert.conversions },
5041
5097
  syncRunId: runId
5042
5098
  }
5043
5099
  }).run();
@@ -7010,7 +7066,7 @@ function readStoredGroundingSources(rawResponse) {
7010
7066
  return result;
7011
7067
  }
7012
7068
  async function backfillInsightsCommand(project, opts) {
7013
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4XOFUN5E.js");
7069
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-BMEJ5GFZ.js");
7014
7070
  const config = loadConfig();
7015
7071
  const db = createClient(config.database);
7016
7072
  migrate(db);
@@ -12246,20 +12302,32 @@ async function createServer(opts) {
12246
12302
  cpc: row.cpc ?? null,
12247
12303
  cpm: row.cpm ?? null
12248
12304
  });
12305
+ const adsLiveInsightDays = (read, request, rangedFields, inProgressFields) => readInsightDays({
12306
+ read,
12307
+ rangedFields,
12308
+ inProgressFields,
12309
+ timeRanges: [liveAdsInsightHourRange(request)],
12310
+ inProgressDate: accountLocalDate(new Date(request.fetchedAtMs), request.timezone)
12311
+ });
12312
+ const adsLiveMetricRows = (read) => [...read.closedDays, ...read.inProgressDay ? [read.inProgressDay] : []].map(adsLiveMetricRow);
12249
12313
  const adsLiveDeliveryReader = {
12250
12314
  listCampaigns: async (apiKey) => (await listCampaigns(apiKey)).map((campaign) => adsLiveEntity(campaign, { mode: campaign.mode })),
12251
12315
  listAdGroups: async (apiKey, campaignId) => (await listAdGroups(apiKey, campaignId)).map((adGroup) => adsLiveEntity(adGroup)),
12252
12316
  listAds: async (apiKey, adGroupId) => (await listAds(apiKey, adGroupId)).map(
12253
12317
  (ad) => adsLiveEntity(ad, { reviewStatus: ad.review_status ?? ad.review?.status ?? null })
12254
12318
  ),
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)
12319
+ campaignInsights: async (apiKey, campaignId, request) => adsLiveMetricRows(await adsLiveInsightDays(
12320
+ (options) => getCampaignInsights(apiKey, campaignId, options),
12321
+ request,
12322
+ CAMPAIGN_INSIGHT_FIELDS,
12323
+ CAMPAIGN_IN_PROGRESS_INSIGHT_FIELDS
12324
+ )),
12325
+ adGroupInsights: async (apiKey, adGroupId, request) => adsLiveMetricRows(await adsLiveInsightDays(
12326
+ (options) => getAdGroupInsights(apiKey, adGroupId, options),
12327
+ request,
12328
+ AD_GROUP_INSIGHT_FIELDS,
12329
+ AD_GROUP_IN_PROGRESS_INSIGHT_FIELDS
12330
+ ))
12263
12331
  };
12264
12332
  const scheduler = new Scheduler(opts.db, {
12265
12333
  onRunCreated: (runId, projectId, providers2, location) => {
@@ -42,7 +42,7 @@ import {
42
42
  trafficConnectWordpressRequestSchema,
43
43
  trafficEventKindSchema,
44
44
  trafficSeriesGranularitySchema
45
- } from "./chunk-PC7AYXND.js";
45
+ } from "./chunk-PXEEWJRV.js";
46
46
 
47
47
  // src/config.ts
48
48
  import fs from "fs";
@@ -3283,6 +3283,18 @@ var getApiV1ProjectsByNameGaAiReferralHistory = (options) => {
3283
3283
  ...options
3284
3284
  });
3285
3285
  };
3286
+ var getApiV1ProjectsByNameGaAiReferralDaily = (options) => {
3287
+ return (options.client ?? client).get({
3288
+ security: [
3289
+ {
3290
+ scheme: "bearer",
3291
+ type: "http"
3292
+ }
3293
+ ],
3294
+ url: "/api/v1/projects/{name}/ga/ai-referral-daily",
3295
+ ...options
3296
+ });
3297
+ };
3286
3298
  var getApiV1ProjectsByNameGaSocialReferralHistory = (options) => {
3287
3299
  return (options.client ?? client).get({
3288
3300
  security: [
@@ -5352,6 +5364,15 @@ var ApiClient = class {
5352
5364
  })
5353
5365
  );
5354
5366
  }
5367
+ async gaAiReferralDaily(project, params) {
5368
+ return this.invoke(
5369
+ () => getApiV1ProjectsByNameGaAiReferralDaily({
5370
+ client: this.heyClient,
5371
+ path: { name: project },
5372
+ query: params
5373
+ })
5374
+ );
5375
+ }
5355
5376
  async gaSocialReferralHistory(project, params) {
5356
5377
  return this.invoke(
5357
5378
  () => getApiV1ProjectsByNameGaSocialReferralHistory({
@@ -7081,8 +7102,8 @@ var canonryMcpTools = [
7081
7102
  }),
7082
7103
  defineTool({
7083
7104
  name: "canonry_ga_ai_referral_history",
7084
- title: "Get GA AI referral history",
7085
- description: "Get AI referral sessions per day grouped by source.",
7105
+ title: "Get GA AI referral detail rows",
7106
+ description: "Get raw AI referral rows per day, landing page, and attribution dimension. Detail, not totals: use canonry_ga_ai_referral_daily for session counts.",
7086
7107
  access: "read",
7087
7108
  tier: "ga",
7088
7109
  inputSchema: gaWindowInputSchema,
@@ -7090,6 +7111,17 @@ var canonryMcpTools = [
7090
7111
  openApiOperations: ["GET /api/v1/projects/{name}/ga/ai-referral-history"],
7091
7112
  handler: (client2, input) => client2.gaAiReferralHistory(input.project, compactStringParams(input, ["window"]))
7092
7113
  }),
7114
+ defineTool({
7115
+ name: "canonry_ga_ai_referral_daily",
7116
+ title: "Get GA AI referral sessions per day",
7117
+ description: "Get AI referral sessions per day and per source, deduplicated across attribution dimensions. Totals match the AI sessions reported by canonry_ga_traffic.",
7118
+ access: "read",
7119
+ tier: "ga",
7120
+ inputSchema: gaWindowInputSchema,
7121
+ annotations: readAnnotations(),
7122
+ openApiOperations: ["GET /api/v1/projects/{name}/ga/ai-referral-daily"],
7123
+ handler: (client2, input) => client2.gaAiReferralDaily(input.project, compactStringParams(input, ["window"]))
7124
+ }),
7093
7125
  defineTool({
7094
7126
  name: "canonry_ga_social_referral_history",
7095
7127
  title: "Get GA social referral history",
@@ -3976,7 +3976,21 @@ var ga4AiReferralDtoSchema = z25.object({
3976
3976
  medium: z25.string(),
3977
3977
  trafficClass: aiReferralTrafficClassSchema,
3978
3978
  sessions: z25.number(),
3979
- users: z25.number(),
3979
+ /**
3980
+ * @deprecated Never emitted since 4.135.0. Removed in the next major.
3981
+ *
3982
+ * GA reports `totalUsers` as a COUNT DISTINCT at the grain requested, and
3983
+ * `ga_ai_referrals` is keyed by (date, source, medium, channelGroup,
3984
+ * landingPage, sourceDimension), so summing it re-counted the same visitor on
3985
+ * every extra day, page and channel they appear in. Unlike GA daily users, no
3986
+ * un-dimensioned AI-referral fetch exists to ask Google for the true figure,
3987
+ * so the number could not be corrected — only withdrawn. Optional rather than
3988
+ * deleted so existing API / `canonry ga traffic --format json` / MCP
3989
+ * consumers keep parsing; they now read `undefined` instead of an inflated
3990
+ * number. Sessions are unaffected: GA4 attributes exactly one landing page
3991
+ * per session, so the landing-page rows partition the day and do sum.
3992
+ */
3993
+ users: z25.number().optional(),
3980
3994
  /**
3981
3995
  * The winning attribution dimension for this (source, medium) tuple — the
3982
3996
  * one with the highest session count. GA4 emits one row per dimension
@@ -3997,7 +4011,8 @@ var ga4AiReferralLandingPageDtoSchema = z25.object({
3997
4011
  sourceDimension: ga4SourceDimensionSchema,
3998
4012
  landingPage: z25.string(),
3999
4013
  sessions: z25.number(),
4000
- users: z25.number()
4014
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4015
+ users: z25.number().optional()
4001
4016
  });
4002
4017
  var ga4SocialReferralDtoSchema = z25.object({
4003
4018
  source: z25.string(),
@@ -4037,28 +4052,28 @@ var ga4TrafficSummaryDtoSchema = z25.object({
4037
4052
  aiReferralLandingPages: z25.array(ga4AiReferralLandingPageDtoSchema),
4038
4053
  /** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. Cross-cutting: can overlap with Direct/Organic/Social via firstUserSource. */
4039
4054
  aiSessionsDeduped: z25.number(),
4040
- /** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
4041
- aiUsersDeduped: z25.number(),
4055
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4056
+ aiUsersDeduped: z25.number().optional(),
4042
4057
  /** Deduped AI sessions whose attribution carries paid intent. */
4043
4058
  paidAiSessionsDeduped: z25.number(),
4044
- /** Deduped users for paid AI sessions. */
4045
- paidAiUsersDeduped: z25.number(),
4059
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4060
+ paidAiUsersDeduped: z25.number().optional(),
4046
4061
  /** Deduped AI sessions without paid intent evidence. */
4047
4062
  organicAiSessionsDeduped: z25.number(),
4048
- /** Deduped users for organic/non-paid AI sessions. */
4049
- organicAiUsersDeduped: z25.number(),
4063
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4064
+ organicAiUsersDeduped: z25.number().optional(),
4050
4065
  /** AI sessions whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals; `channelBreakdown` removes those overlaps for display. */
4051
4066
  aiSessionsBySession: z25.number(),
4052
- /** AI users whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals. */
4053
- aiUsersBySession: z25.number(),
4067
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4068
+ aiUsersBySession: z25.number().optional(),
4054
4069
  /** Session-source-only paid AI sessions. */
4055
4070
  paidAiSessionsBySession: z25.number(),
4056
- /** Session-source-only paid AI users. */
4057
- paidAiUsersBySession: z25.number(),
4071
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4072
+ paidAiUsersBySession: z25.number().optional(),
4058
4073
  /** Session-source-only organic/non-paid AI sessions. */
4059
4074
  organicAiSessionsBySession: z25.number(),
4060
- /** Session-source-only organic/non-paid AI users. */
4061
- organicAiUsersBySession: z25.number(),
4075
+ /** @deprecated See `GA4AiReferralDto.users`. Never emitted since 4.135.0. */
4076
+ organicAiUsersBySession: z25.number().optional(),
4062
4077
  socialReferrals: z25.array(ga4SocialReferralDtoSchema),
4063
4078
  /** Total social sessions (session-scoped, no cross-dimension dedup needed). */
4064
4079
  socialSessions: z25.number(),
@@ -4148,6 +4163,29 @@ var ga4AiReferralHistoryEntrySchema = z25.object({
4148
4163
  /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
4149
4164
  sourceDimension: ga4SourceDimensionSchema
4150
4165
  });
4166
+ var ga4AiReferralDailySourceSchema = z25.object({
4167
+ source: z25.string(),
4168
+ sessions: z25.number(),
4169
+ paidSessions: z25.number(),
4170
+ organicSessions: z25.number()
4171
+ });
4172
+ var ga4AiReferralDailyEntrySchema = z25.object({
4173
+ date: z25.string(),
4174
+ sessions: z25.number(),
4175
+ paidSessions: z25.number(),
4176
+ organicSessions: z25.number(),
4177
+ /** Per-source split of the day. `sessions` is the sum of these. */
4178
+ bySource: z25.array(ga4AiReferralDailySourceSchema)
4179
+ });
4180
+ var ga4AiReferralDailyDtoSchema = z25.object({
4181
+ /** Ascending by date. Dates with no AI referrals are absent, not zero-filled. */
4182
+ days: z25.array(ga4AiReferralDailyEntrySchema),
4183
+ /** Sources present in the window, ordered by total sessions descending. */
4184
+ sources: z25.array(z25.string()),
4185
+ totalSessions: z25.number(),
4186
+ totalPaidSessions: z25.number(),
4187
+ totalOrganicSessions: z25.number()
4188
+ });
4151
4189
  var ga4SocialReferralHistoryEntrySchema = z25.object({
4152
4190
  date: z25.string(),
4153
4191
  source: z25.string(),
@@ -6818,13 +6856,31 @@ var adsInsightRowDtoSchema = z38.object({
6818
6856
  /** clicks / impressions; null when impressions is 0. */
6819
6857
  ctr: z38.number().nullable(),
6820
6858
  /** spendMicros / clicks, rounded to integer micros; null when clicks is 0. */
6821
- cpcMicros: z38.number().int().nullable()
6859
+ cpcMicros: z38.number().int().nullable(),
6860
+ /**
6861
+ * True when `date` is the ad account's CURRENT local calendar date, so this
6862
+ * row is a running figure rather than a closed day. The sync re-reads the
6863
+ * open day and overwrites the row every time it runs, so the numbers here
6864
+ * keep rising until the day ends. Do not compare it against a finished day
6865
+ * or treat it as final.
6866
+ *
6867
+ * `conversions` is the one field that does NOT fill in live on such a row.
6868
+ * The provider serves the open day only from a call that may not request
6869
+ * conversion metrics, so the count stays at 0 until the day closes and the
6870
+ * next sync reads the real figure. Treat it as not yet reported.
6871
+ */
6872
+ inProgress: z38.boolean()
6822
6873
  });
6823
6874
  var adsInsightsResponseSchema = z38.object({
6824
6875
  rows: z38.array(adsInsightRowDtoSchema),
6825
6876
  /** Account currency for rendering spend/cpc; null before the first sync. */
6826
6877
  currencyCode: z38.string().nullable().optional()
6827
6878
  });
6879
+ var adsRollupWindowDtoSchema = z38.object({
6880
+ from: z38.string().nullable(),
6881
+ to: z38.string().nullable(),
6882
+ inProgressDate: z38.string().nullable()
6883
+ });
6828
6884
  var adsTotalsDtoSchema = z38.object({
6829
6885
  impressions: z38.number().int(),
6830
6886
  clicks: z38.number().int(),
@@ -6842,10 +6898,7 @@ var adsSummaryDtoSchema = z38.object({
6842
6898
  adGroupCount: z38.number().int(),
6843
6899
  adCount: z38.number().int(),
6844
6900
  /** Date range the totals cover (oldest/newest rollup date), null when empty. */
6845
- window: z38.object({
6846
- from: z38.string().nullable(),
6847
- to: z38.string().nullable()
6848
- }),
6901
+ window: adsRollupWindowDtoSchema,
6849
6902
  /** Campaign-level rollup totals over the window (levels are not summed across). */
6850
6903
  totals: adsTotalsDtoSchema
6851
6904
  });
@@ -6918,7 +6971,7 @@ var adsDeliveryDiagnosticsDtoSchema = z38.object({
6918
6971
  }),
6919
6972
  historicalCampaignRollups: z38.object({
6920
6973
  status: adsHistoricalCampaignRollupStatusSchema,
6921
- window: z38.object({ from: z38.string().nullable(), to: z38.string().nullable() }),
6974
+ window: adsRollupWindowDtoSchema,
6922
6975
  totals: adsTotalsDtoSchema.nullable()
6923
6976
  }),
6924
6977
  storedConfiguration: z38.object({
@@ -7965,6 +8018,7 @@ export {
7965
8018
  ga4StatusDtoSchema,
7966
8019
  ga4SyncResponseDtoSchema,
7967
8020
  ga4AiReferralHistoryEntrySchema,
8021
+ ga4AiReferralDailyDtoSchema,
7968
8022
  ga4SocialReferralHistoryEntrySchema,
7969
8023
  ga4SessionHistoryEntrySchema,
7970
8024
  extractAnswerMentions,