@canonry/canonry 4.134.0 → 4.134.1

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 +14 -3
  3. package/assets/assets/{AuditHistoryPanel-DBUWbfNk.js → AuditHistoryPanel-BW7LVMSZ.js} +1 -1
  4. package/assets/assets/{BacklinksPage-BFm0VdUO.js → BacklinksPage-DU65axM1.js} +1 -1
  5. package/assets/assets/{ChartPrimitives-oV_nduTH.js → ChartPrimitives-CUZJ7nUb.js} +1 -1
  6. package/assets/assets/{HistoryPage-IF2FMem3.js → HistoryPage-BswdEXC5.js} +1 -1
  7. package/assets/assets/ProjectPage-CttTuKKo.js +8 -0
  8. package/assets/assets/{RunRow-De5zdch5.js → RunRow-CBxbfubI.js} +1 -1
  9. package/assets/assets/{RunsPage-xtRF78r8.js → RunsPage-BQxA05ua.js} +1 -1
  10. package/assets/assets/{SettingsPage-B_GAxaWu.js → SettingsPage-krEusjc9.js} +1 -1
  11. package/assets/assets/{TrafficPage-E8I5ineJ.js → TrafficPage-DMhcA9lg.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-DGk2MqSp.js → TrafficSourceDetailPage-Dm0Gj0HS.js} +1 -1
  13. package/assets/assets/{arrow-left-D-4QMxi9.js → arrow-left-_1YaUPbl.js} +1 -1
  14. package/assets/assets/{extract-error-message-DuqIFU8Z.js → extract-error-message-D4XKG37U.js} +1 -1
  15. package/assets/assets/{index-De1MKbc7.js → index-V1C3c7nO.js} +68 -68
  16. package/assets/assets/{trash-2-BVh__r6G.js → trash-2-D3LWIGDJ.js} +1 -1
  17. package/assets/index.html +1 -1
  18. package/dist/{chunk-ZUQPHGEN.js → chunk-BSU2MI3Y.js} +35 -3
  19. package/dist/{chunk-PC7AYXND.js → chunk-CF5WTWXI.js} +24 -0
  20. package/dist/{chunk-LIXWDIDR.js → chunk-DVJGFY3J.js} +189 -72
  21. package/dist/{chunk-TKXWTDV6.js → chunk-NWVPRV7A.js} +4 -4
  22. package/dist/cli.js +49 -5
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-4XOFUN5E.js → intelligence-service-NO4FJUYD.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +7 -7
  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-V1C3c7nO.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-V1C3c7nO.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">
@@ -42,7 +42,7 @@ import {
42
42
  trafficConnectWordpressRequestSchema,
43
43
  trafficEventKindSchema,
44
44
  trafficSeriesGranularitySchema
45
- } from "./chunk-PC7AYXND.js";
45
+ } from "./chunk-CF5WTWXI.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",
@@ -4148,6 +4148,29 @@ var ga4AiReferralHistoryEntrySchema = z25.object({
4148
4148
  /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
4149
4149
  sourceDimension: ga4SourceDimensionSchema
4150
4150
  });
4151
+ var ga4AiReferralDailySourceSchema = z25.object({
4152
+ source: z25.string(),
4153
+ sessions: z25.number(),
4154
+ paidSessions: z25.number(),
4155
+ organicSessions: z25.number()
4156
+ });
4157
+ var ga4AiReferralDailyEntrySchema = z25.object({
4158
+ date: z25.string(),
4159
+ sessions: z25.number(),
4160
+ paidSessions: z25.number(),
4161
+ organicSessions: z25.number(),
4162
+ /** Per-source split of the day. `sessions` is the sum of these. */
4163
+ bySource: z25.array(ga4AiReferralDailySourceSchema)
4164
+ });
4165
+ var ga4AiReferralDailyDtoSchema = z25.object({
4166
+ /** Ascending by date. Dates with no AI referrals are absent, not zero-filled. */
4167
+ days: z25.array(ga4AiReferralDailyEntrySchema),
4168
+ /** Sources present in the window, ordered by total sessions descending. */
4169
+ sources: z25.array(z25.string()),
4170
+ totalSessions: z25.number(),
4171
+ totalPaidSessions: z25.number(),
4172
+ totalOrganicSessions: z25.number()
4173
+ });
4151
4174
  var ga4SocialReferralHistoryEntrySchema = z25.object({
4152
4175
  date: z25.string(),
4153
4176
  source: z25.string(),
@@ -7965,6 +7988,7 @@ export {
7965
7988
  ga4StatusDtoSchema,
7966
7989
  ga4SyncResponseDtoSchema,
7967
7990
  ga4AiReferralHistoryEntrySchema,
7991
+ ga4AiReferralDailyDtoSchema,
7968
7992
  ga4SocialReferralHistoryEntrySchema,
7969
7993
  ga4SessionHistoryEntrySchema,
7970
7994
  extractAnswerMentions,
@@ -209,6 +209,7 @@ import {
209
209
  formatNumber,
210
210
  formatRatio,
211
211
  formatWindowCountDelta,
212
+ ga4AiReferralDailyDtoSchema,
212
213
  ga4AiReferralHistoryEntrySchema,
213
214
  ga4SessionHistoryEntrySchema,
214
215
  ga4SocialReferralHistoryEntrySchema,
@@ -364,7 +365,7 @@ import {
364
365
  wordpressSchemaDeployResultDtoSchema,
365
366
  wordpressSchemaStatusResultDtoSchema,
366
367
  wordpressStatusDtoSchema
367
- } from "./chunk-PC7AYXND.js";
368
+ } from "./chunk-CF5WTWXI.js";
368
369
 
369
370
  // src/intelligence-service.ts
370
371
  import { eq as eq44, desc as desc21, asc as asc9, and as and35, ne as ne6, or as or10, inArray as inArray15, gte as gte12, lte as lte8 } from "drizzle-orm";
@@ -17462,6 +17463,7 @@ var SCHEMA_TABLE = {
17462
17463
  ResearchRunDetailDto: researchRunDetailSchema,
17463
17464
  ResearchRunListDto: researchRunListSchema,
17464
17465
  DoctorReportDto: doctorReportSchema,
17466
+ GA4AiReferralDailyDto: ga4AiReferralDailyDtoSchema,
17465
17467
  GA4AiReferralHistoryEntry: ga4AiReferralHistoryEntrySchema,
17466
17468
  GA4MeasurementAnalysisDto: gaMeasurementAnalysisDtoSchema,
17467
17469
  GA4SessionHistoryEntry: ga4SessionHistoryEntrySchema,
@@ -21205,7 +21207,8 @@ var routeCatalog = [
21205
21207
  {
21206
21208
  method: "get",
21207
21209
  path: "/api/v1/projects/{name}/ga/ai-referral-history",
21208
- summary: "Get AI referral sessions per day grouped by source and landing page",
21210
+ summary: "Get raw AI referral detail rows per day, landing page, and attribution dimension",
21211
+ description: "Detail rows, not totals. One row per landing page per attribution dimension, so a single day of one source is many rows and each is commonly worth one session. Use /ga/ai-referral-daily for per-date or per-source session counts.",
21209
21212
  tags: ["ga4"],
21210
21213
  parameters: [nameParameter, analyticsWindowParameter],
21211
21214
  responses: {
@@ -21214,6 +21217,19 @@ var routeCatalog = [
21214
21217
  404: errorResponse("Project not found.")
21215
21218
  }
21216
21219
  },
21220
+ {
21221
+ method: "get",
21222
+ path: "/api/v1/projects/{name}/ga/ai-referral-daily",
21223
+ summary: "Get AI referral sessions per day and per source",
21224
+ description: "Sums landing pages within one attribution dimension and never across dimensions, so totalSessions equals the aiSessionsDeduped reported by /ga/traffic for the same window. Sessions only: GA counts users as a distinct count at the grain requested, so an AI-referral user total cannot be summed from these rows and no un-dimensioned AI-referral fetch exists to supply one.",
21225
+ tags: ["ga4"],
21226
+ parameters: [nameParameter, analyticsWindowParameter],
21227
+ responses: {
21228
+ 200: jsonResponse("AI referral daily series returned.", "GA4AiReferralDailyDto"),
21229
+ 400: errorResponse("GA4 is not connected."),
21230
+ 404: errorResponse("Project not found.")
21231
+ }
21232
+ },
21217
21233
  {
21218
21234
  method: "get",
21219
21235
  path: "/api/v1/projects/{name}/ga/social-referral-history",
@@ -31802,6 +31818,160 @@ function buildSessionHistory(landingPageDays, dailyTotals) {
31802
31818
  });
31803
31819
  }
31804
31820
 
31821
+ // ../api-routes/src/ga-ai-referral-aggregation.ts
31822
+ function normalizeAiTrafficClass(value) {
31823
+ return value === AiReferralTrafficClasses.paid ? AiReferralTrafficClasses.paid : AiReferralTrafficClasses.organic;
31824
+ }
31825
+ function emptyAiCounts() {
31826
+ return { sessions: 0, users: 0 };
31827
+ }
31828
+ function resolveWinningDimensions(rows) {
31829
+ const groups = /* @__PURE__ */ new Map();
31830
+ for (const row of rows) {
31831
+ const key = `${row.date}\0${row.source}\0${row.medium}`;
31832
+ let group = groups.get(key);
31833
+ if (!group) {
31834
+ group = { date: row.date, source: row.source, medium: row.medium, byDimension: /* @__PURE__ */ new Map() };
31835
+ groups.set(key, group);
31836
+ }
31837
+ let dim = group.byDimension.get(row.sourceDimension);
31838
+ if (!dim) {
31839
+ dim = { paidSessions: 0, organicSessions: 0 };
31840
+ group.byDimension.set(row.sourceDimension, dim);
31841
+ }
31842
+ const sessions = row.sessions ?? 0;
31843
+ if (normalizeAiTrafficClass(row.trafficClass) === AiReferralTrafficClasses.paid) {
31844
+ dim.paidSessions += sessions;
31845
+ } else {
31846
+ dim.organicSessions += sessions;
31847
+ }
31848
+ }
31849
+ const winners = [];
31850
+ for (const group of groups.values()) {
31851
+ const dims = [...group.byDimension.values()];
31852
+ const bestSessions = dims.reduce((best, d) => d.paidSessions + d.organicSessions > best.paidSessions + best.organicSessions ? d : best);
31853
+ winners.push({
31854
+ date: group.date,
31855
+ source: group.source,
31856
+ medium: group.medium,
31857
+ paidSessions: bestSessions.paidSessions,
31858
+ organicSessions: bestSessions.organicSessions
31859
+ });
31860
+ }
31861
+ return winners;
31862
+ }
31863
+ function resolveLegacyWinningUsers(rows) {
31864
+ const groups = /* @__PURE__ */ new Map();
31865
+ for (const row of rows) {
31866
+ const key = `${row.date}\0${row.source}\0${row.medium}`;
31867
+ let byDimension = groups.get(key);
31868
+ if (!byDimension) {
31869
+ byDimension = /* @__PURE__ */ new Map();
31870
+ groups.set(key, byDimension);
31871
+ }
31872
+ let dim = byDimension.get(row.sourceDimension);
31873
+ if (!dim) {
31874
+ dim = { paidUsers: 0, organicUsers: 0 };
31875
+ byDimension.set(row.sourceDimension, dim);
31876
+ }
31877
+ const users = row.users ?? 0;
31878
+ if (normalizeAiTrafficClass(row.trafficClass) === AiReferralTrafficClasses.paid) {
31879
+ dim.paidUsers += users;
31880
+ } else {
31881
+ dim.organicUsers += users;
31882
+ }
31883
+ }
31884
+ let paidUsers = 0;
31885
+ let organicUsers = 0;
31886
+ for (const byDimension of groups.values()) {
31887
+ const dims = [...byDimension.values()];
31888
+ const best = dims.reduce((winner, d) => d.paidUsers + d.organicUsers > winner.paidUsers + winner.organicUsers ? d : winner);
31889
+ paidUsers += best.paidUsers;
31890
+ organicUsers += best.organicUsers;
31891
+ }
31892
+ return { paidUsers, organicUsers };
31893
+ }
31894
+ function summarizeAiReferralCounts(rows) {
31895
+ const paidDeduped = emptyAiCounts();
31896
+ const organicDeduped = emptyAiCounts();
31897
+ const paidBySession = emptyAiCounts();
31898
+ const organicBySession = emptyAiCounts();
31899
+ const bySessionChannelGroup = /* @__PURE__ */ new Map();
31900
+ for (const winner of resolveWinningDimensions(rows)) {
31901
+ paidDeduped.sessions += winner.paidSessions;
31902
+ organicDeduped.sessions += winner.organicSessions;
31903
+ }
31904
+ const legacyUsers = resolveLegacyWinningUsers(rows);
31905
+ paidDeduped.users = legacyUsers.paidUsers;
31906
+ organicDeduped.users = legacyUsers.organicUsers;
31907
+ for (const row of rows) {
31908
+ if (row.sourceDimension !== "session") continue;
31909
+ const sessions = row.sessions ?? 0;
31910
+ const users = row.users ?? 0;
31911
+ bySessionChannelGroup.set(
31912
+ row.channelGroup,
31913
+ (bySessionChannelGroup.get(row.channelGroup) ?? 0) + sessions
31914
+ );
31915
+ const bucket = normalizeAiTrafficClass(row.trafficClass) === AiReferralTrafficClasses.paid ? paidBySession : organicBySession;
31916
+ bucket.sessions += sessions;
31917
+ bucket.users += users;
31918
+ }
31919
+ return {
31920
+ paidDeduped,
31921
+ organicDeduped,
31922
+ paidBySession,
31923
+ organicBySession,
31924
+ bySessionChannelGroup,
31925
+ deduped: {
31926
+ sessions: paidDeduped.sessions + organicDeduped.sessions,
31927
+ users: paidDeduped.users + organicDeduped.users
31928
+ },
31929
+ bySession: {
31930
+ sessions: paidBySession.sessions + organicBySession.sessions,
31931
+ users: paidBySession.users + organicBySession.users
31932
+ }
31933
+ };
31934
+ }
31935
+ function buildAiReferralDailySeries(rows) {
31936
+ const byDate = /* @__PURE__ */ new Map();
31937
+ const sourceTotals = /* @__PURE__ */ new Map();
31938
+ let totalSessions = 0;
31939
+ let totalPaidSessions = 0;
31940
+ let totalOrganicSessions = 0;
31941
+ for (const winner of resolveWinningDimensions(rows)) {
31942
+ const sessions = winner.paidSessions + winner.organicSessions;
31943
+ let bySource = byDate.get(winner.date);
31944
+ if (!bySource) {
31945
+ bySource = /* @__PURE__ */ new Map();
31946
+ byDate.set(winner.date, bySource);
31947
+ }
31948
+ let bucket = bySource.get(winner.source);
31949
+ if (!bucket) {
31950
+ bucket = { source: winner.source, sessions: 0, paidSessions: 0, organicSessions: 0 };
31951
+ bySource.set(winner.source, bucket);
31952
+ }
31953
+ bucket.sessions += sessions;
31954
+ bucket.paidSessions += winner.paidSessions;
31955
+ bucket.organicSessions += winner.organicSessions;
31956
+ sourceTotals.set(winner.source, (sourceTotals.get(winner.source) ?? 0) + sessions);
31957
+ totalSessions += sessions;
31958
+ totalPaidSessions += winner.paidSessions;
31959
+ totalOrganicSessions += winner.organicSessions;
31960
+ }
31961
+ const days = [...byDate.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([date, bySource]) => {
31962
+ const sources2 = [...bySource.values()].sort((a, b) => b.sessions - a.sessions || a.source.localeCompare(b.source));
31963
+ return {
31964
+ date,
31965
+ sessions: sources2.reduce((sum, s) => sum + s.sessions, 0),
31966
+ paidSessions: sources2.reduce((sum, s) => sum + s.paidSessions, 0),
31967
+ organicSessions: sources2.reduce((sum, s) => sum + s.organicSessions, 0),
31968
+ bySource: sources2
31969
+ };
31970
+ });
31971
+ const sources = [...sourceTotals.entries()].sort(([aSource, aSessions], [bSource, bSessions]) => bSessions - aSessions || aSource.localeCompare(bSource)).map(([source]) => source);
31972
+ return { days, sources, totalSessions, totalPaidSessions, totalOrganicSessions };
31973
+ }
31974
+
31805
31975
  // ../api-routes/src/ga.ts
31806
31976
  function gaLog(level, action, ctx) {
31807
31977
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
@@ -31858,12 +32028,6 @@ function pickWinningDimension(rows, tupleKey) {
31858
32028
  }
31859
32029
  return [...winners.values()].sort((a, b) => (b.sessions ?? 0) - (a.sessions ?? 0));
31860
32030
  }
31861
- function normalizeAiTrafficClass(value) {
31862
- return value === AiReferralTrafficClasses.paid ? AiReferralTrafficClasses.paid : AiReferralTrafficClasses.organic;
31863
- }
31864
- function emptyAiCounts() {
31865
- return { sessions: 0, users: 0 };
31866
- }
31867
32031
  function persistAcquisitionMeasurement(db, input) {
31868
32032
  const { startDate, endDate } = input.report;
31869
32033
  db.transaction((tx) => {
@@ -31992,70 +32156,6 @@ function persistMeasurementError(db, projectId, component, message, updatedAt) {
31992
32156
  }
31993
32157
  }).run();
31994
32158
  }
31995
- function summarizeAiReferralCounts(rows) {
31996
- const dedupeGroups = /* @__PURE__ */ new Map();
31997
- const bySessionChannelGroup = /* @__PURE__ */ new Map();
31998
- const paidDeduped = emptyAiCounts();
31999
- const organicDeduped = emptyAiCounts();
32000
- const paidBySession = emptyAiCounts();
32001
- const organicBySession = emptyAiCounts();
32002
- for (const row of rows) {
32003
- const isPaid = normalizeAiTrafficClass(row.trafficClass) === AiReferralTrafficClasses.paid;
32004
- const sessions = row.sessions ?? 0;
32005
- const users = row.users ?? 0;
32006
- const key = `${row.date}\0${row.source}\0${row.medium}`;
32007
- let byDimension = dedupeGroups.get(key);
32008
- if (!byDimension) {
32009
- byDimension = /* @__PURE__ */ new Map();
32010
- dedupeGroups.set(key, byDimension);
32011
- }
32012
- let dim = byDimension.get(row.sourceDimension);
32013
- if (!dim) {
32014
- dim = { paidSessions: 0, organicSessions: 0, paidUsers: 0, organicUsers: 0 };
32015
- byDimension.set(row.sourceDimension, dim);
32016
- }
32017
- if (isPaid) {
32018
- dim.paidSessions += sessions;
32019
- dim.paidUsers += users;
32020
- } else {
32021
- dim.organicSessions += sessions;
32022
- dim.organicUsers += users;
32023
- }
32024
- if (row.sourceDimension === "session") {
32025
- bySessionChannelGroup.set(
32026
- row.channelGroup,
32027
- (bySessionChannelGroup.get(row.channelGroup) ?? 0) + sessions
32028
- );
32029
- const sessionBucket = isPaid ? paidBySession : organicBySession;
32030
- sessionBucket.sessions += sessions;
32031
- sessionBucket.users += users;
32032
- }
32033
- }
32034
- for (const byDimension of dedupeGroups.values()) {
32035
- const dims = [...byDimension.values()];
32036
- const bestSessions = dims.reduce((best, d) => d.paidSessions + d.organicSessions > best.paidSessions + best.organicSessions ? d : best);
32037
- const bestUsers = dims.reduce((best, d) => d.paidUsers + d.organicUsers > best.paidUsers + best.organicUsers ? d : best);
32038
- paidDeduped.sessions += bestSessions.paidSessions;
32039
- organicDeduped.sessions += bestSessions.organicSessions;
32040
- paidDeduped.users += bestUsers.paidUsers;
32041
- organicDeduped.users += bestUsers.organicUsers;
32042
- }
32043
- return {
32044
- paidDeduped,
32045
- organicDeduped,
32046
- paidBySession,
32047
- organicBySession,
32048
- bySessionChannelGroup,
32049
- deduped: {
32050
- sessions: paidDeduped.sessions + organicDeduped.sessions,
32051
- users: paidDeduped.users + organicDeduped.users
32052
- },
32053
- bySession: {
32054
- sessions: paidBySession.sessions + organicBySession.sessions,
32055
- users: paidBySession.users + organicBySession.users
32056
- }
32057
- };
32058
- }
32059
32159
  async function refreshOAuthTokenIfNeeded(googleStore, authConfig, canonicalDomain, oauthConn) {
32060
32160
  const expiresAt = oauthConn.tokenExpiresAt ? new Date(oauthConn.tokenExpiresAt).getTime() : 0;
32061
32161
  const fiveMinutes = 5 * 60 * 1e3;
@@ -32757,6 +32857,23 @@ async function ga4Routes(app, opts) {
32757
32857
  ).orderBy(gaAiReferrals.date).all();
32758
32858
  return rows;
32759
32859
  });
32860
+ app.get("/projects/:name/ga/ai-referral-daily", async (request, _reply) => {
32861
+ const project = resolveProject(app.db, request.params.name);
32862
+ requireGa4Connection(opts, project.name, project.canonicalDomain);
32863
+ const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
32864
+ const conditions = [eq31(gaAiReferrals.projectId, project.id)];
32865
+ if (cutoffDate) conditions.push(sql15`${gaAiReferrals.date} >= ${cutoffDate}`);
32866
+ const rows = app.db.select({
32867
+ date: gaAiReferrals.date,
32868
+ source: gaAiReferrals.source,
32869
+ medium: gaAiReferrals.medium,
32870
+ trafficClass: gaAiReferrals.trafficClass,
32871
+ sourceDimension: gaAiReferrals.sourceDimension,
32872
+ channelGroup: gaAiReferrals.channelGroup,
32873
+ sessions: gaAiReferrals.sessions
32874
+ }).from(gaAiReferrals).where(and25(...conditions)).all();
32875
+ return buildAiReferralDailySeries(rows);
32876
+ });
32760
32877
  app.get("/projects/:name/ga/social-referral-history", async (request, _reply) => {
32761
32878
  const project = resolveProject(app.db, request.params.name);
32762
32879
  requireGa4Connection(opts, project.name, project.canonicalDomain);
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-ZUQPHGEN.js";
13
+ } from "./chunk-BSU2MI3Y.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-DVJGFY3J.js";
123
123
  import {
124
124
  AGENT_MEMORY_VALUE_MAX_BYTES,
125
125
  AGENT_PROVIDER_IDS,
@@ -193,7 +193,7 @@ import {
193
193
  validationError,
194
194
  winnabilityClassLabel,
195
195
  withRetry
196
- } from "./chunk-PC7AYXND.js";
196
+ } from "./chunk-CF5WTWXI.js";
197
197
 
198
198
  // src/telemetry.ts
199
199
  import crypto from "crypto";
@@ -7010,7 +7010,7 @@ function readStoredGroundingSources(rawResponse) {
7010
7010
  return result;
7011
7011
  }
7012
7012
  async function backfillInsightsCommand(project, opts) {
7013
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4XOFUN5E.js");
7013
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-NO4FJUYD.js");
7014
7014
  const config = loadConfig();
7015
7015
  const db = createClient(config.database);
7016
7016
  migrate(db);
package/dist/cli.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  setTelemetrySource,
30
30
  showFirstRunNotice,
31
31
  trackEvent
32
- } from "./chunk-TKXWTDV6.js";
32
+ } from "./chunk-NWVPRV7A.js";
33
33
  import {
34
34
  CliError,
35
35
  EXIT_SYSTEM_ERROR,
@@ -46,7 +46,7 @@ import {
46
46
  saveConfig,
47
47
  saveConfigPatch,
48
48
  usageError
49
- } from "./chunk-ZUQPHGEN.js";
49
+ } from "./chunk-BSU2MI3Y.js";
50
50
  import {
51
51
  apiKeys,
52
52
  createClient,
@@ -54,7 +54,7 @@ import {
54
54
  projects,
55
55
  queries,
56
56
  renderReportHtml
57
- } from "./chunk-LIXWDIDR.js";
57
+ } from "./chunk-DVJGFY3J.js";
58
58
  import {
59
59
  AdsDeliverySnapshotStatuses,
60
60
  AdsHistoricalCampaignRollupStatuses,
@@ -100,7 +100,7 @@ import {
100
100
  providerQuotaPolicySchema,
101
101
  resolveProviderInput,
102
102
  winnabilityClassSchema
103
- } from "./chunk-PC7AYXND.js";
103
+ } from "./chunk-CF5WTWXI.js";
104
104
 
105
105
  // src/cli.ts
106
106
  import { pathToFileURL } from "url";
@@ -4111,6 +4111,36 @@ async function gaAiReferralHistory(project, opts) {
4111
4111
  );
4112
4112
  }
4113
4113
  }
4114
+ async function gaAiReferralDaily(project, opts) {
4115
+ const client = getClient7();
4116
+ const result = await client.gaAiReferralDaily(project, opts?.window ? { window: opts.window } : void 0);
4117
+ if (opts?.format === "json") {
4118
+ console.log(JSON.stringify(result, null, 2));
4119
+ return;
4120
+ } else if (opts?.format === "jsonl") {
4121
+ emitJsonl(result.days.map((day) => ({ project, window: opts?.window, ...day })));
4122
+ return;
4123
+ }
4124
+ if (result.days.length === 0) {
4125
+ console.log(`No AI referral sessions.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
4126
+ return;
4127
+ }
4128
+ const dateWidth = 12;
4129
+ const sourceWidth = Math.min(30, Math.max(10, ...result.sources.map((s) => s.length)));
4130
+ console.log(`GA4 AI Referral Sessions per Day for "${project}":
4131
+ `);
4132
+ console.log(` ${"DATE".padEnd(dateWidth)} ${"SOURCE".padEnd(sourceWidth)} ${"SESSIONS".padEnd(10)}${"PAID".padEnd(8)}${"ORGANIC".padEnd(9)}`);
4133
+ console.log(` ${"\u2500".repeat(dateWidth)} ${"\u2500".repeat(sourceWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(8)}${"\u2500".repeat(9)}`);
4134
+ for (const day of result.days) {
4135
+ for (const entry of day.bySource) {
4136
+ console.log(
4137
+ ` ${day.date.padEnd(dateWidth)} ${entry.source.padEnd(sourceWidth)} ${String(entry.sessions).padEnd(10)}${String(entry.paidSessions).padEnd(8)}${String(entry.organicSessions).padEnd(9)}`
4138
+ );
4139
+ }
4140
+ }
4141
+ console.log(`
4142
+ Total: ${result.totalSessions} sessions (${result.totalPaidSessions} paid, ${result.totalOrganicSessions} organic)`);
4143
+ }
4114
4144
  async function gaSocialReferralHistory(project, opts) {
4115
4145
  const client = getClient7();
4116
4146
  const result = await client.gaSocialReferralHistory(project, opts?.window ? { window: opts.window } : void 0);
@@ -4575,6 +4605,20 @@ var GA_CLI_COMMANDS = [
4575
4605
  });
4576
4606
  }
4577
4607
  },
4608
+ {
4609
+ path: ["ga", "ai-referral-daily"],
4610
+ usage: "canonry ga ai-referral-daily <project> [--window 30d] [--format json]",
4611
+ options: {
4612
+ window: stringOption()
4613
+ },
4614
+ run: async (input) => {
4615
+ const project = requireProject(input, "ga.ai-referral-daily", "canonry ga ai-referral-daily <project> [--window 30d] [--format json]");
4616
+ await gaAiReferralDaily(project, {
4617
+ window: getString(input.values, "window"),
4618
+ format: input.format
4619
+ });
4620
+ }
4621
+ },
4578
4622
  {
4579
4623
  path: ["ga", "social-referral-history"],
4580
4624
  usage: "canonry ga social-referral-history <project> [--window 30d] [--format json]",
@@ -4638,7 +4682,7 @@ var GA_CLI_COMMANDS = [
4638
4682
  unknownSubcommand(input.positionals[0], {
4639
4683
  command: "ga",
4640
4684
  usage: "canonry ga <subcommand> <project> [args]",
4641
- available: ["connect", "disconnect", "status", "sync", "measurement-analysis", "traffic", "coverage", "ai-referral-history", "social-referral-history", "session-history", "social-referral-summary", "attribution"]
4685
+ available: ["connect", "disconnect", "status", "sync", "measurement-analysis", "traffic", "coverage", "ai-referral-history", "ai-referral-daily", "social-referral-history", "session-history", "social-referral-summary", "attribution"]
4642
4686
  });
4643
4687
  }
4644
4688
  }
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-TKXWTDV6.js";
3
+ } from "./chunk-NWVPRV7A.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-ZUQPHGEN.js";
7
- import "./chunk-LIXWDIDR.js";
8
- import "./chunk-PC7AYXND.js";
6
+ } from "./chunk-BSU2MI3Y.js";
7
+ import "./chunk-DVJGFY3J.js";
8
+ import "./chunk-CF5WTWXI.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-LIXWDIDR.js";
4
- import "./chunk-PC7AYXND.js";
3
+ } from "./chunk-DVJGFY3J.js";
4
+ import "./chunk-CF5WTWXI.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-ZUQPHGEN.js";
6
+ } from "./chunk-BSU2MI3Y.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-PC7AYXND.js";
9
+ } from "./chunk-CF5WTWXI.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.134.0",
3
+ "version": "4.134.1",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -71,20 +71,20 @@
71
71
  "@ainyc/canonry-db": "0.0.0",
72
72
  "@ainyc/canonry-contracts": "0.0.0",
73
73
  "@ainyc/canonry-integration-bing": "0.0.0",
74
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
74
75
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
76
+ "@ainyc/canonry-integration-google-places": "0.0.0",
75
77
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
76
78
  "@ainyc/canonry-integration-google": "0.0.0",
77
- "@ainyc/canonry-integration-google-places": "0.0.0",
78
79
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
79
80
  "@ainyc/canonry-integration-traffic": "0.0.0",
80
- "@ainyc/canonry-integration-wordpress": "0.0.0",
81
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
81
+ "@ainyc/canonry-intelligence": "0.0.0",
82
82
  "@ainyc/canonry-provider-cdp": "0.0.0",
83
- "@ainyc/canonry-provider-claude": "0.0.0",
84
- "@ainyc/canonry-provider-local": "0.0.0",
85
83
  "@ainyc/canonry-provider-gemini": "0.0.0",
86
- "@ainyc/canonry-intelligence": "0.0.0",
84
+ "@ainyc/canonry-provider-claude": "0.0.0",
85
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
87
86
  "@ainyc/canonry-provider-openai": "0.0.0",
87
+ "@ainyc/canonry-provider-local": "0.0.0",
88
88
  "@ainyc/canonry-provider-perplexity": "0.0.0"
89
89
  },
90
90
  "scripts": {