@ainyc/canonry 4.115.1 → 4.116.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 (23) hide show
  1. package/assets/assets/{BacklinksPage-yUb8oJuq.js → BacklinksPage-Dy6Xc-LT.js} +1 -1
  2. package/assets/assets/{ChartPrimitives-CdQM3YDG.js → ChartPrimitives-CHJh5M8x.js} +1 -1
  3. package/assets/assets/ProjectPage-Bjfa4CI5.js +7 -0
  4. package/assets/assets/{RunRow-BU_x7EcP.js → RunRow-DmvQnWPf.js} +1 -1
  5. package/assets/assets/{RunsPage-CFIBWaO7.js → RunsPage-BxkxQEKE.js} +1 -1
  6. package/assets/assets/{SettingsPage-PyMryUad.js → SettingsPage-DCyqHqDh.js} +1 -1
  7. package/assets/assets/{TrafficPage-471ypAdQ.js → TrafficPage-BT_ae5aO.js} +1 -1
  8. package/assets/assets/{TrafficSourceDetailPage-CI7TEQge.js → TrafficSourceDetailPage-Cps-tSHn.js} +1 -1
  9. package/assets/assets/{arrow-left-D2N9u-0i.js → arrow-left-D8vdqhx4.js} +1 -1
  10. package/assets/assets/{extract-error-message-CAdsqKp3.js → extract-error-message-BOY5txUA.js} +1 -1
  11. package/assets/assets/{index-CoL5HENE.js → index-DxjY63VC.js} +63 -63
  12. package/assets/assets/{trash-2-UgQrN5Np.js → trash-2-ap_Fa4zA.js} +1 -1
  13. package/assets/index.html +1 -1
  14. package/dist/{chunk-B2SNXXAJ.js → chunk-GV665WOU.js} +4 -4
  15. package/dist/{chunk-6LOKMEL3.js → chunk-IQPMK36Y.js} +1285 -1233
  16. package/dist/{chunk-CW4JISBL.js → chunk-RVH6QPTA.js} +1 -1
  17. package/dist/{chunk-Z3OGDQHB.js → chunk-WEAXJ25Y.js} +162 -34
  18. package/dist/cli.js +9 -5
  19. package/dist/index.js +4 -4
  20. package/dist/{intelligence-service-4AWB2V7U.js → intelligence-service-HEN5HLUM.js} +2 -2
  21. package/dist/mcp.js +2 -2
  22. package/package.json +12 -12
  23. package/assets/assets/ProjectPage-CHj9fvTs.js +0 -7
@@ -2907,9 +2907,98 @@ function calendarMonthBounds(month) {
2907
2907
  }
2908
2908
 
2909
2909
  // ../contracts/src/ga.ts
2910
+ import { z as z22 } from "zod";
2911
+
2912
+ // ../contracts/src/traffic-class.ts
2910
2913
  import { z as z21 } from "zod";
2911
- var ga4AiReferralTrafficClassSchema = z21.enum(["organic", "paid"]);
2912
- var GA4AiReferralTrafficClasses = ga4AiReferralTrafficClassSchema.enum;
2914
+
2915
+ // ../contracts/src/formatting.ts
2916
+ function formatRatio(value) {
2917
+ if (!Number.isFinite(value) || value === 0) return "0%";
2918
+ return `${(value * 100).toFixed(1)}%`;
2919
+ }
2920
+ function formatNumber(value) {
2921
+ if (!Number.isFinite(value)) return "\u2014";
2922
+ if (Math.abs(value) >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
2923
+ if (Math.abs(value) >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
2924
+ return value.toLocaleString("en-US");
2925
+ }
2926
+ function formatDate(iso) {
2927
+ if (!iso) return "\u2014";
2928
+ try {
2929
+ const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
2930
+ const options = { month: "short", day: "numeric", year: "numeric" };
2931
+ const d = dateOnly && dateOnly[1] && dateOnly[2] && dateOnly[3] ? new Date(Date.UTC(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))) : new Date(iso);
2932
+ if (Number.isNaN(d.getTime())) return iso;
2933
+ return d.toLocaleDateString("en-US", dateOnly ? { ...options, timeZone: "UTC" } : options);
2934
+ } catch {
2935
+ return iso;
2936
+ }
2937
+ }
2938
+ function formatIsoDate(iso) {
2939
+ if (!iso) return "\u2014";
2940
+ try {
2941
+ const d = new Date(iso);
2942
+ if (Number.isNaN(d.getTime())) return iso;
2943
+ const yyyy = d.getUTCFullYear();
2944
+ const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
2945
+ const dd = String(d.getUTCDate()).padStart(2, "0");
2946
+ return `${yyyy}-${mm}-${dd}`;
2947
+ } catch {
2948
+ return iso;
2949
+ }
2950
+ }
2951
+ function formatDateRange(start, end) {
2952
+ if (!start && !end) return "";
2953
+ if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
2954
+ return formatDate(start || end);
2955
+ }
2956
+ var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
2957
+ function parseInclusiveEndMs(iso) {
2958
+ const ms = Date.parse(iso);
2959
+ if (Number.isNaN(ms)) return null;
2960
+ return DATE_ONLY_PATTERN.test(iso) ? ms + 864e5 - 1 : ms;
2961
+ }
2962
+ function deltaPercent(current, prior) {
2963
+ if (prior <= 0) return null;
2964
+ return Math.round((current - prior) / prior * 100);
2965
+ }
2966
+ function deltaTone(deltaPct) {
2967
+ if (deltaPct === null || deltaPct === 0) return "neutral";
2968
+ return deltaPct > 0 ? "positive" : "negative";
2969
+ }
2970
+ function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
2971
+ if (d.deltaPct === null) {
2972
+ return d.prior === 0 ? "First baseline week" : "";
2973
+ }
2974
+ if (d.deltaPct > 0) return `Up ${d.deltaPct}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
2975
+ if (d.deltaPct < 0) return `Down ${Math.abs(d.deltaPct)}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
2976
+ return `Flat ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
2977
+ }
2978
+ var MIN_PCT_BASE = 30;
2979
+ function round1(value) {
2980
+ return Math.round(value * 10) / 10;
2981
+ }
2982
+ function formatAverageDelta(d) {
2983
+ if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
2984
+ const sign2 = d.deltaPct > 0 ? "+" : "";
2985
+ return `${sign2}${d.deltaPct}% vs prior`;
2986
+ }
2987
+ const sign = d.deltaAbs > 0 ? "+" : "";
2988
+ return `${sign}${round1(d.deltaAbs)} vs ${round1(d.prior)}`;
2989
+ }
2990
+ function formatWindowCountDelta(d, countLabel, windowLabel) {
2991
+ if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
2992
+ const sign2 = d.deltaPct > 0 ? "+" : "";
2993
+ return `${sign2}${d.deltaPct}% ${windowLabel}`;
2994
+ }
2995
+ const sign = d.deltaAbs > 0 ? "+" : "";
2996
+ return `${sign}${formatNumber(Math.round(d.deltaAbs))} ${countLabel} ${windowLabel}`;
2997
+ }
2998
+
2999
+ // ../contracts/src/traffic-class.ts
3000
+ var aiReferralTrafficClassSchema = z21.enum(["organic", "paid"]);
3001
+ var AiReferralTrafficClasses = aiReferralTrafficClassSchema.enum;
2913
3002
  var PAID_CHANNEL_GROUPS = /* @__PURE__ */ new Set([
2914
3003
  "paid search",
2915
3004
  "paid social",
@@ -2956,39 +3045,51 @@ function hasPaidLandingPageParam(landingPage) {
2956
3045
  return false;
2957
3046
  }
2958
3047
  }
2959
- function classifyGa4AiReferralTrafficClass(input) {
3048
+ function classifyAiReferralTrafficClass(input) {
2960
3049
  const channelGroup = (input.channelGroup ?? "").trim().toLowerCase();
2961
3050
  if (PAID_CHANNEL_GROUPS.has(channelGroup) || channelGroup.startsWith("paid ")) {
2962
- return GA4AiReferralTrafficClasses.paid;
3051
+ return AiReferralTrafficClasses.paid;
2963
3052
  }
2964
3053
  if (hasPaidToken(input.medium) || hasPaidToken(input.source) || hasPaidLandingPageParam(input.landingPage)) {
2965
- return GA4AiReferralTrafficClasses.paid;
3054
+ return AiReferralTrafficClasses.paid;
2966
3055
  }
2967
- return GA4AiReferralTrafficClasses.organic;
2968
- }
2969
- var ga4ConnectionDtoSchema = z21.object({
2970
- id: z21.string(),
2971
- projectId: z21.string(),
2972
- propertyId: z21.string(),
2973
- clientEmail: z21.string(),
2974
- connected: z21.boolean(),
2975
- createdAt: z21.string(),
2976
- updatedAt: z21.string()
2977
- });
2978
- var ga4TrafficSnapshotDtoSchema = z21.object({
2979
- date: z21.string(),
2980
- landingPage: z21.string(),
2981
- sessions: z21.number(),
2982
- organicSessions: z21.number(),
2983
- users: z21.number()
2984
- });
2985
- var ga4SourceDimensionSchema = z21.enum(["session", "first_user", "manual_utm"]);
2986
- var ga4AiReferralDtoSchema = z21.object({
2987
- source: z21.string(),
2988
- medium: z21.string(),
2989
- trafficClass: ga4AiReferralTrafficClassSchema,
2990
- sessions: z21.number(),
2991
- users: z21.number(),
3056
+ return AiReferralTrafficClasses.organic;
3057
+ }
3058
+ function aiReferralClassCounts(total, paid, organic) {
3059
+ return { total, paid, organic, unknown: Math.max(0, total - paid - organic) };
3060
+ }
3061
+ function formatAiReferralClassSummary(counts) {
3062
+ const parts = [];
3063
+ if (counts.paid > 0) parts.push(`Paid ${formatNumber(counts.paid)}`);
3064
+ if (counts.organic > 0) parts.push(`Organic ${formatNumber(counts.organic)}`);
3065
+ if (counts.unknown > 0) parts.push(`Unclassified ${formatNumber(counts.unknown)}`);
3066
+ return parts.join(" \xB7 ");
3067
+ }
3068
+
3069
+ // ../contracts/src/ga.ts
3070
+ var ga4ConnectionDtoSchema = z22.object({
3071
+ id: z22.string(),
3072
+ projectId: z22.string(),
3073
+ propertyId: z22.string(),
3074
+ clientEmail: z22.string(),
3075
+ connected: z22.boolean(),
3076
+ createdAt: z22.string(),
3077
+ updatedAt: z22.string()
3078
+ });
3079
+ var ga4TrafficSnapshotDtoSchema = z22.object({
3080
+ date: z22.string(),
3081
+ landingPage: z22.string(),
3082
+ sessions: z22.number(),
3083
+ organicSessions: z22.number(),
3084
+ users: z22.number()
3085
+ });
3086
+ var ga4SourceDimensionSchema = z22.enum(["session", "first_user", "manual_utm"]);
3087
+ var ga4AiReferralDtoSchema = z22.object({
3088
+ source: z22.string(),
3089
+ medium: z22.string(),
3090
+ trafficClass: aiReferralTrafficClassSchema,
3091
+ sessions: z22.number(),
3092
+ users: z22.number(),
2992
3093
  /**
2993
3094
  * The winning attribution dimension for this (source, medium) tuple — the
2994
3095
  * one with the highest session count. GA4 emits one row per dimension
@@ -2998,178 +3099,178 @@ var ga4AiReferralDtoSchema = z21.object({
2998
3099
  */
2999
3100
  sourceDimension: ga4SourceDimensionSchema
3000
3101
  });
3001
- var ga4AiReferralLandingPageDtoSchema = z21.object({
3002
- source: z21.string(),
3003
- medium: z21.string(),
3004
- trafficClass: ga4AiReferralTrafficClassSchema,
3102
+ var ga4AiReferralLandingPageDtoSchema = z22.object({
3103
+ source: z22.string(),
3104
+ medium: z22.string(),
3105
+ trafficClass: aiReferralTrafficClassSchema,
3005
3106
  /**
3006
3107
  * The winning attribution dimension for this (source, medium, landingPage)
3007
3108
  * tuple — the one with the highest session count.
3008
3109
  */
3009
3110
  sourceDimension: ga4SourceDimensionSchema,
3010
- landingPage: z21.string(),
3011
- sessions: z21.number(),
3012
- users: z21.number()
3013
- });
3014
- var ga4SocialReferralDtoSchema = z21.object({
3015
- source: z21.string(),
3016
- medium: z21.string(),
3017
- sessions: z21.number(),
3018
- users: z21.number(),
3111
+ landingPage: z22.string(),
3112
+ sessions: z22.number(),
3113
+ users: z22.number()
3114
+ });
3115
+ var ga4SocialReferralDtoSchema = z22.object({
3116
+ source: z22.string(),
3117
+ medium: z22.string(),
3118
+ sessions: z22.number(),
3119
+ users: z22.number(),
3019
3120
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
3020
- channelGroup: z21.string()
3121
+ channelGroup: z22.string()
3021
3122
  });
3022
- var ga4ChannelBucketDtoSchema = z21.object({
3023
- sessions: z21.number(),
3024
- sharePct: z21.number(),
3025
- sharePctDisplay: z21.string()
3123
+ var ga4ChannelBucketDtoSchema = z22.object({
3124
+ sessions: z22.number(),
3125
+ sharePct: z22.number(),
3126
+ sharePctDisplay: z22.string()
3026
3127
  });
3027
- var ga4ChannelBreakdownDtoSchema = z21.object({
3128
+ var ga4ChannelBreakdownDtoSchema = z22.object({
3028
3129
  organic: ga4ChannelBucketDtoSchema,
3029
3130
  social: ga4ChannelBucketDtoSchema,
3030
3131
  direct: ga4ChannelBucketDtoSchema,
3031
3132
  ai: ga4ChannelBucketDtoSchema,
3032
3133
  other: ga4ChannelBucketDtoSchema
3033
3134
  });
3034
- var ga4TrafficSummaryDtoSchema = z21.object({
3035
- totalSessions: z21.number(),
3036
- totalOrganicSessions: z21.number(),
3135
+ var ga4TrafficSummaryDtoSchema = z22.object({
3136
+ totalSessions: z22.number(),
3137
+ totalOrganicSessions: z22.number(),
3037
3138
  /** Direct-channel sessions (sessions with no source — bookmarks, typed URLs, AI-driven traffic with stripped referrer). 0 for legacy rows from before the column was added. */
3038
- totalDirectSessions: z21.number(),
3039
- totalUsers: z21.number(),
3040
- topPages: z21.array(z21.object({
3041
- landingPage: z21.string(),
3042
- sessions: z21.number(),
3043
- organicSessions: z21.number(),
3139
+ totalDirectSessions: z22.number(),
3140
+ totalUsers: z22.number(),
3141
+ topPages: z22.array(z22.object({
3142
+ landingPage: z22.string(),
3143
+ sessions: z22.number(),
3144
+ organicSessions: z22.number(),
3044
3145
  /** Per-page Direct-channel sessions. 0 for legacy rows. */
3045
- directSessions: z21.number(),
3046
- users: z21.number()
3146
+ directSessions: z22.number(),
3147
+ users: z22.number()
3047
3148
  })),
3048
- aiReferrals: z21.array(ga4AiReferralDtoSchema),
3049
- aiReferralLandingPages: z21.array(ga4AiReferralLandingPageDtoSchema),
3149
+ aiReferrals: z22.array(ga4AiReferralDtoSchema),
3150
+ aiReferralLandingPages: z22.array(ga4AiReferralLandingPageDtoSchema),
3050
3151
  /** 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. */
3051
- aiSessionsDeduped: z21.number(),
3152
+ aiSessionsDeduped: z22.number(),
3052
3153
  /** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
3053
- aiUsersDeduped: z21.number(),
3154
+ aiUsersDeduped: z22.number(),
3054
3155
  /** Deduped AI sessions whose attribution carries paid intent. */
3055
- paidAiSessionsDeduped: z21.number(),
3156
+ paidAiSessionsDeduped: z22.number(),
3056
3157
  /** Deduped users for paid AI sessions. */
3057
- paidAiUsersDeduped: z21.number(),
3158
+ paidAiUsersDeduped: z22.number(),
3058
3159
  /** Deduped AI sessions without paid intent evidence. */
3059
- organicAiSessionsDeduped: z21.number(),
3160
+ organicAiSessionsDeduped: z22.number(),
3060
3161
  /** Deduped users for organic/non-paid AI sessions. */
3061
- organicAiUsersDeduped: z21.number(),
3162
+ organicAiUsersDeduped: z22.number(),
3062
3163
  /** AI sessions whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals; `channelBreakdown` removes those overlaps for display. */
3063
- aiSessionsBySession: z21.number(),
3164
+ aiSessionsBySession: z22.number(),
3064
3165
  /** AI users whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals. */
3065
- aiUsersBySession: z21.number(),
3166
+ aiUsersBySession: z22.number(),
3066
3167
  /** Session-source-only paid AI sessions. */
3067
- paidAiSessionsBySession: z21.number(),
3168
+ paidAiSessionsBySession: z22.number(),
3068
3169
  /** Session-source-only paid AI users. */
3069
- paidAiUsersBySession: z21.number(),
3170
+ paidAiUsersBySession: z22.number(),
3070
3171
  /** Session-source-only organic/non-paid AI sessions. */
3071
- organicAiSessionsBySession: z21.number(),
3172
+ organicAiSessionsBySession: z22.number(),
3072
3173
  /** Session-source-only organic/non-paid AI users. */
3073
- organicAiUsersBySession: z21.number(),
3074
- socialReferrals: z21.array(ga4SocialReferralDtoSchema),
3174
+ organicAiUsersBySession: z22.number(),
3175
+ socialReferrals: z22.array(ga4SocialReferralDtoSchema),
3075
3176
  /** Total social sessions (session-scoped, no cross-dimension dedup needed). */
3076
- socialSessions: z21.number(),
3177
+ socialSessions: z22.number(),
3077
3178
  /** Total social users (session-scoped, no cross-dimension dedup needed). */
3078
- socialUsers: z21.number(),
3179
+ socialUsers: z22.number(),
3079
3180
  /** Five disjoint buckets used for the channel breakdown. Known AI session-source matches are removed from their native GA4 bucket before shares are computed. */
3080
3181
  channelBreakdown: ga4ChannelBreakdownDtoSchema,
3081
3182
  /** Organic sessions as a percentage of total sessions (0–100, rounded). */
3082
- organicSharePct: z21.number(),
3183
+ organicSharePct: z22.number(),
3083
3184
  /** Deduped AI sessions as a percentage of total sessions (0–100, rounded). Cross-cutting: can overlap with Direct/Organic/Social. */
3084
- aiSharePct: z21.number(),
3185
+ aiSharePct: z22.number(),
3085
3186
  /** Session-source-only AI sessions as a percentage of total sessions (0–100, rounded). Can overlap with raw Organic/Social/Direct totals. */
3086
- aiSharePctBySession: z21.number(),
3187
+ aiSharePctBySession: z22.number(),
3087
3188
  /** Paid AI sessions as a percentage of total sessions (0–100, rounded). */
3088
- paidAiSharePct: z21.number(),
3189
+ paidAiSharePct: z22.number(),
3089
3190
  /** Session-source paid AI sessions as a percentage of total sessions (0–100, rounded). */
3090
- paidAiSharePctBySession: z21.number(),
3191
+ paidAiSharePctBySession: z22.number(),
3091
3192
  /** Organic/non-paid AI sessions as a percentage of total sessions (0–100, rounded). */
3092
- organicAiSharePct: z21.number(),
3193
+ organicAiSharePct: z22.number(),
3093
3194
  /** Session-source organic/non-paid AI sessions as a percentage of total sessions (0–100, rounded). */
3094
- organicAiSharePctBySession: z21.number(),
3195
+ organicAiSharePctBySession: z22.number(),
3095
3196
  /** Direct-channel sessions as a percentage of total sessions (0–100, rounded). */
3096
- directSharePct: z21.number(),
3197
+ directSharePct: z22.number(),
3097
3198
  /** Social sessions as a percentage of total sessions (0–100, rounded). */
3098
- socialSharePct: z21.number(),
3199
+ socialSharePct: z22.number(),
3099
3200
  /** Display string for organicSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3100
- organicSharePctDisplay: z21.string(),
3201
+ organicSharePctDisplay: z22.string(),
3101
3202
  /** Display string for aiSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3102
- aiSharePctDisplay: z21.string(),
3203
+ aiSharePctDisplay: z22.string(),
3103
3204
  /** Display string for aiSharePctBySession: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3104
- aiSharePctBySessionDisplay: z21.string(),
3205
+ aiSharePctBySessionDisplay: z22.string(),
3105
3206
  /** Display string for paidAiSharePct. */
3106
- paidAiSharePctDisplay: z21.string(),
3207
+ paidAiSharePctDisplay: z22.string(),
3107
3208
  /** Display string for paidAiSharePctBySession. */
3108
- paidAiSharePctBySessionDisplay: z21.string(),
3209
+ paidAiSharePctBySessionDisplay: z22.string(),
3109
3210
  /** Display string for organicAiSharePct. */
3110
- organicAiSharePctDisplay: z21.string(),
3211
+ organicAiSharePctDisplay: z22.string(),
3111
3212
  /** Display string for organicAiSharePctBySession. */
3112
- organicAiSharePctBySessionDisplay: z21.string(),
3213
+ organicAiSharePctBySessionDisplay: z22.string(),
3113
3214
  /** Display string for directSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3114
- directSharePctDisplay: z21.string(),
3215
+ directSharePctDisplay: z22.string(),
3115
3216
  /** Display string for socialSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3116
- socialSharePctDisplay: z21.string(),
3217
+ socialSharePctDisplay: z22.string(),
3117
3218
  /** Sessions not covered by Organic, Social, Direct, or AI (session) channels — e.g. Referral, Email, Paid Search, Display. Always non-negative; clamped to 0 when the four disjoint channels sum above total (rounding edge). */
3118
- otherSessions: z21.number(),
3219
+ otherSessions: z22.number(),
3119
3220
  /** Other sessions as a percentage of total sessions (0–100, rounded). */
3120
- otherSharePct: z21.number(),
3221
+ otherSharePct: z22.number(),
3121
3222
  /** Display string for otherSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
3122
- otherSharePctDisplay: z21.string(),
3123
- lastSyncedAt: z21.string().nullable()
3124
- });
3125
- var ga4StatusDtoSchema = z21.object({
3126
- connected: z21.boolean(),
3127
- propertyId: z21.string().nullable(),
3128
- clientEmail: z21.string().nullable(),
3129
- authMethod: z21.enum(["service-account", "oauth"]).nullable(),
3130
- lastSyncedAt: z21.string().nullable(),
3131
- createdAt: z21.string().nullable().optional(),
3132
- updatedAt: z21.string().nullable().optional()
3133
- });
3134
- var ga4SyncResponseDtoSchema = z21.object({
3135
- synced: z21.boolean(),
3136
- rowCount: z21.number().int().nonnegative(),
3137
- aiReferralCount: z21.number().int().nonnegative(),
3138
- socialReferralCount: z21.number().int().nonnegative(),
3139
- days: z21.number().int().nonnegative(),
3140
- syncedAt: z21.string(),
3223
+ otherSharePctDisplay: z22.string(),
3224
+ lastSyncedAt: z22.string().nullable()
3225
+ });
3226
+ var ga4StatusDtoSchema = z22.object({
3227
+ connected: z22.boolean(),
3228
+ propertyId: z22.string().nullable(),
3229
+ clientEmail: z22.string().nullable(),
3230
+ authMethod: z22.enum(["service-account", "oauth"]).nullable(),
3231
+ lastSyncedAt: z22.string().nullable(),
3232
+ createdAt: z22.string().nullable().optional(),
3233
+ updatedAt: z22.string().nullable().optional()
3234
+ });
3235
+ var ga4SyncResponseDtoSchema = z22.object({
3236
+ synced: z22.boolean(),
3237
+ rowCount: z22.number().int().nonnegative(),
3238
+ aiReferralCount: z22.number().int().nonnegative(),
3239
+ socialReferralCount: z22.number().int().nonnegative(),
3240
+ days: z22.number().int().nonnegative(),
3241
+ syncedAt: z22.string(),
3141
3242
  /**
3142
3243
  * Components that were written this run. Present when `only` is set.
3143
3244
  * Always includes `traffic` and `summary` (the share denominator) plus
3144
3245
  * the requested channel breakdown — `ai` and/or `social`.
3145
3246
  */
3146
- syncedComponents: z21.array(z21.string()).optional()
3147
- });
3148
- var ga4AiReferralHistoryEntrySchema = z21.object({
3149
- date: z21.string(),
3150
- source: z21.string(),
3151
- medium: z21.string(),
3152
- trafficClass: ga4AiReferralTrafficClassSchema,
3153
- landingPage: z21.string(),
3154
- sessions: z21.number(),
3155
- users: z21.number(),
3247
+ syncedComponents: z22.array(z22.string()).optional()
3248
+ });
3249
+ var ga4AiReferralHistoryEntrySchema = z22.object({
3250
+ date: z22.string(),
3251
+ source: z22.string(),
3252
+ medium: z22.string(),
3253
+ trafficClass: aiReferralTrafficClassSchema,
3254
+ landingPage: z22.string(),
3255
+ sessions: z22.number(),
3256
+ users: z22.number(),
3156
3257
  /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
3157
3258
  sourceDimension: ga4SourceDimensionSchema
3158
3259
  });
3159
- var ga4SocialReferralHistoryEntrySchema = z21.object({
3160
- date: z21.string(),
3161
- source: z21.string(),
3162
- medium: z21.string(),
3163
- sessions: z21.number(),
3164
- users: z21.number(),
3260
+ var ga4SocialReferralHistoryEntrySchema = z22.object({
3261
+ date: z22.string(),
3262
+ source: z22.string(),
3263
+ medium: z22.string(),
3264
+ sessions: z22.number(),
3265
+ users: z22.number(),
3165
3266
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
3166
- channelGroup: z21.string()
3267
+ channelGroup: z22.string()
3167
3268
  });
3168
- var ga4SessionHistoryEntrySchema = z21.object({
3169
- date: z21.string(),
3170
- sessions: z21.number(),
3171
- organicSessions: z21.number(),
3172
- users: z21.number()
3269
+ var ga4SessionHistoryEntrySchema = z22.object({
3270
+ date: z22.string(),
3271
+ sessions: z22.number(),
3272
+ organicSessions: z22.number(),
3273
+ users: z22.number()
3173
3274
  });
3174
3275
 
3175
3276
  // ../contracts/src/answer-visibility.ts
@@ -3337,50 +3438,50 @@ function escapeRegExp(value) {
3337
3438
  }
3338
3439
 
3339
3440
  // ../contracts/src/agent.ts
3340
- import { z as z22 } from "zod";
3341
- var agentProviderIdSchema = z22.enum(["claude", "openai", "gemini", "zai", "deepinfra"]);
3342
- var agentProviderOptionDtoSchema = z22.object({
3441
+ import { z as z23 } from "zod";
3442
+ var agentProviderIdSchema = z23.enum(["claude", "openai", "gemini", "zai", "deepinfra"]);
3443
+ var agentProviderOptionDtoSchema = z23.object({
3343
3444
  /** Stable identifier — what clients pass back as `provider` on the prompt endpoint. */
3344
3445
  id: agentProviderIdSchema,
3345
3446
  /** Human-readable label for UI pickers, e.g. "Anthropic (Claude)". */
3346
- label: z22.string(),
3447
+ label: z23.string(),
3347
3448
  /** Default model if the caller doesn't pick one. */
3348
- defaultModel: z22.string(),
3449
+ defaultModel: z23.string(),
3349
3450
  /** Whether a usable API key was found (config.yaml or provider env var). */
3350
- configured: z22.boolean(),
3451
+ configured: z23.boolean(),
3351
3452
  /**
3352
3453
  * Where the key resolved from, if any. `null` when `configured === false`.
3353
3454
  * Surfaced so the UI can nudge users toward their preferred source of truth.
3354
3455
  */
3355
- keySource: z22.enum(["config", "env"]).nullable()
3456
+ keySource: z23.enum(["config", "env"]).nullable()
3356
3457
  });
3357
- var agentProvidersResponseDtoSchema = z22.object({
3458
+ var agentProvidersResponseDtoSchema = z23.object({
3358
3459
  /**
3359
3460
  * Every provider Aero knows about. `configured === false` entries are
3360
3461
  * included so the UI can render them disabled with an onboarding hint.
3361
3462
  */
3362
- providers: z22.array(agentProviderOptionDtoSchema).default([]),
3463
+ providers: z23.array(agentProviderOptionDtoSchema).default([]),
3363
3464
  /**
3364
3465
  * Provider Aero auto-picks when no explicit override is passed. Null if
3365
3466
  * nothing is configured (install never exchanged a key).
3366
3467
  */
3367
3468
  defaultProvider: agentProviderIdSchema.nullable()
3368
3469
  });
3369
- var memorySourceSchema = z22.enum(["aero", "user", "compaction"]);
3470
+ var memorySourceSchema = z23.enum(["aero", "user", "compaction"]);
3370
3471
  var MemorySources = memorySourceSchema.enum;
3371
3472
  var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
3372
3473
  var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
3373
- var agentMemoryUpsertRequestSchema = z22.object({
3374
- key: z22.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
3375
- value: z22.string().min(1)
3474
+ var agentMemoryUpsertRequestSchema = z23.object({
3475
+ key: z23.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
3476
+ value: z23.string().min(1)
3376
3477
  });
3377
- var agentMemoryDeleteRequestSchema = z22.object({
3378
- key: z22.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
3478
+ var agentMemoryDeleteRequestSchema = z23.object({
3479
+ key: z23.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
3379
3480
  });
3380
3481
 
3381
3482
  // ../contracts/src/backlinks.ts
3382
- import { z as z23 } from "zod";
3383
- var backlinkSourceSchema = z23.enum(["commoncrawl", "bing-webmaster"]);
3483
+ import { z as z24 } from "zod";
3484
+ var backlinkSourceSchema = z24.enum(["commoncrawl", "bing-webmaster"]);
3384
3485
  var BacklinkSources = backlinkSourceSchema.enum;
3385
3486
  function computeBacklinkSummaryMetrics(rows) {
3386
3487
  if (rows.length === 0) {
@@ -3396,181 +3497,181 @@ function computeBacklinkSummaryMetrics(rows) {
3396
3497
  top10HostsShare: share.toFixed(6)
3397
3498
  };
3398
3499
  }
3399
- var ccReleaseSyncStatusSchema = z23.enum(["queued", "downloading", "querying", "ready", "failed"]);
3500
+ var ccReleaseSyncStatusSchema = z24.enum(["queued", "downloading", "querying", "ready", "failed"]);
3400
3501
  var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
3401
- var ccReleaseSyncDtoSchema = z23.object({
3402
- id: z23.string(),
3403
- release: z23.string(),
3502
+ var ccReleaseSyncDtoSchema = z24.object({
3503
+ id: z24.string(),
3504
+ release: z24.string(),
3404
3505
  status: ccReleaseSyncStatusSchema,
3405
- phaseDetail: z23.string().nullable().optional(),
3406
- vertexPath: z23.string().nullable().optional(),
3407
- edgesPath: z23.string().nullable().optional(),
3408
- vertexSha256: z23.string().nullable().optional(),
3409
- edgesSha256: z23.string().nullable().optional(),
3410
- vertexBytes: z23.number().int().nullable().optional(),
3411
- edgesBytes: z23.number().int().nullable().optional(),
3412
- projectsProcessed: z23.number().int().nullable().optional(),
3413
- domainsDiscovered: z23.number().int().nullable().optional(),
3414
- downloadStartedAt: z23.string().nullable().optional(),
3415
- downloadFinishedAt: z23.string().nullable().optional(),
3416
- queryStartedAt: z23.string().nullable().optional(),
3417
- queryFinishedAt: z23.string().nullable().optional(),
3418
- error: z23.string().nullable().optional(),
3419
- createdAt: z23.string(),
3420
- updatedAt: z23.string()
3421
- });
3422
- var backlinkDomainDtoSchema = z23.object({
3423
- linkingDomain: z23.string(),
3506
+ phaseDetail: z24.string().nullable().optional(),
3507
+ vertexPath: z24.string().nullable().optional(),
3508
+ edgesPath: z24.string().nullable().optional(),
3509
+ vertexSha256: z24.string().nullable().optional(),
3510
+ edgesSha256: z24.string().nullable().optional(),
3511
+ vertexBytes: z24.number().int().nullable().optional(),
3512
+ edgesBytes: z24.number().int().nullable().optional(),
3513
+ projectsProcessed: z24.number().int().nullable().optional(),
3514
+ domainsDiscovered: z24.number().int().nullable().optional(),
3515
+ downloadStartedAt: z24.string().nullable().optional(),
3516
+ downloadFinishedAt: z24.string().nullable().optional(),
3517
+ queryStartedAt: z24.string().nullable().optional(),
3518
+ queryFinishedAt: z24.string().nullable().optional(),
3519
+ error: z24.string().nullable().optional(),
3520
+ createdAt: z24.string(),
3521
+ updatedAt: z24.string()
3522
+ });
3523
+ var backlinkDomainDtoSchema = z24.object({
3524
+ linkingDomain: z24.string(),
3424
3525
  // For Common Crawl this is the count of distinct hosts within the linking
3425
3526
  // domain; for Bing Webmaster it is the count of distinct linking pages (URLs)
3426
3527
  // from that linking host. Read alongside `source` — the unit differs per source.
3427
- numHosts: z23.number().int(),
3528
+ numHosts: z24.number().int(),
3428
3529
  source: backlinkSourceSchema
3429
3530
  });
3430
- var backlinkSummaryDtoSchema = z23.object({
3431
- projectId: z23.string(),
3531
+ var backlinkSummaryDtoSchema = z24.object({
3532
+ projectId: z24.string(),
3432
3533
  // Window identifier. Common Crawl uses the release slug
3433
3534
  // (`cc-main-YYYY-<mon>-<mon>-<mon>`); Bing Webmaster uses a synthetic
3434
3535
  // per-sync-day window (`bing-YYYY-MM-DD`).
3435
- release: z23.string(),
3436
- targetDomain: z23.string(),
3437
- totalLinkingDomains: z23.number().int(),
3438
- totalHosts: z23.number().int(),
3439
- top10HostsShare: z23.string(),
3440
- queriedAt: z23.string(),
3536
+ release: z24.string(),
3537
+ targetDomain: z24.string(),
3538
+ totalLinkingDomains: z24.number().int(),
3539
+ totalHosts: z24.number().int(),
3540
+ top10HostsShare: z24.string(),
3541
+ queriedAt: z24.string(),
3441
3542
  source: backlinkSourceSchema,
3442
3543
  // Populated when the response is filtered (e.g. ?excludeCrawlers=1).
3443
3544
  // Counts the rows omitted from totalLinkingDomains/totalHosts so callers
3444
3545
  // can show "N hidden" hints without re-deriving them.
3445
- excludedLinkingDomains: z23.number().int().optional(),
3446
- excludedHosts: z23.number().int().optional()
3546
+ excludedLinkingDomains: z24.number().int().optional(),
3547
+ excludedHosts: z24.number().int().optional()
3447
3548
  });
3448
- var backlinkListResponseSchema = z23.object({
3549
+ var backlinkListResponseSchema = z24.object({
3449
3550
  // The source this response was filtered to (defaults to commoncrawl when the
3450
3551
  // caller omits `?source`).
3451
3552
  source: backlinkSourceSchema,
3452
3553
  summary: backlinkSummaryDtoSchema.nullable(),
3453
- total: z23.number().int(),
3454
- rows: z23.array(backlinkDomainDtoSchema)
3455
- });
3456
- var backlinkHistoryEntrySchema = z23.object({
3457
- release: z23.string(),
3458
- totalLinkingDomains: z23.number().int(),
3459
- totalHosts: z23.number().int(),
3460
- top10HostsShare: z23.string(),
3461
- queriedAt: z23.string(),
3554
+ total: z24.number().int(),
3555
+ rows: z24.array(backlinkDomainDtoSchema)
3556
+ });
3557
+ var backlinkHistoryEntrySchema = z24.object({
3558
+ release: z24.string(),
3559
+ totalLinkingDomains: z24.number().int(),
3560
+ totalHosts: z24.number().int(),
3561
+ top10HostsShare: z24.string(),
3562
+ queriedAt: z24.string(),
3462
3563
  source: backlinkSourceSchema
3463
3564
  });
3464
- var backlinkSourceAvailabilityDtoSchema = z23.object({
3565
+ var backlinkSourceAvailabilityDtoSchema = z24.object({
3465
3566
  source: backlinkSourceSchema,
3466
3567
  /**
3467
3568
  * The source is set up for this project:
3468
3569
  * - commoncrawl: `autoExtractBacklinks` enabled AND a `ready` release sync exists.
3469
3570
  * - bing-webmaster: a Bing Webmaster connection exists for the project domain.
3470
3571
  */
3471
- connected: z23.boolean(),
3572
+ connected: z24.boolean(),
3472
3573
  /** Backlink rows exist for this project + source. */
3473
- hasData: z23.boolean(),
3574
+ hasData: z24.boolean(),
3474
3575
  /** Latest window id with data for this source, null when none. */
3475
- latestRelease: z23.string().nullable(),
3576
+ latestRelease: z24.string().nullable(),
3476
3577
  /**
3477
3578
  * Linking-domain count in the latest window. Excludes crawler/proxy hosts only
3478
3579
  * when the request sets `?excludeCrawlers=1` (default off, matching the summary
3479
3580
  * and domains endpoints); the dashboard passes it so the switcher pill matches
3480
3581
  * the metric card.
3481
3582
  */
3482
- totalLinkingDomains: z23.number().int(),
3583
+ totalLinkingDomains: z24.number().int(),
3483
3584
  /** Freshness: `queriedAt` of the latest summary for this source, null when none. */
3484
- lastSyncedAt: z23.string().nullable()
3585
+ lastSyncedAt: z24.string().nullable()
3485
3586
  });
3486
- var backlinkSourcesResponseSchema = z23.object({
3487
- projectId: z23.string(),
3488
- targetDomain: z23.string(),
3587
+ var backlinkSourcesResponseSchema = z24.object({
3588
+ projectId: z24.string(),
3589
+ targetDomain: z24.string(),
3489
3590
  /** Availability for every known source, in a stable order. */
3490
- sources: z23.array(backlinkSourceAvailabilityDtoSchema),
3491
- anyConnected: z23.boolean(),
3492
- anyData: z23.boolean()
3493
- });
3494
- var backlinksInstallStatusDtoSchema = z23.object({
3495
- duckdbInstalled: z23.boolean(),
3496
- duckdbVersion: z23.string().nullable().optional(),
3497
- duckdbSpec: z23.string(),
3498
- pluginDir: z23.string()
3499
- });
3500
- var backlinksInstallResultDtoSchema = z23.object({
3501
- installed: z23.boolean(),
3502
- version: z23.string(),
3503
- path: z23.string(),
3504
- alreadyPresent: z23.boolean()
3505
- });
3506
- var ccAvailableReleaseSchema = z23.object({
3507
- release: z23.string(),
3508
- vertexUrl: z23.string(),
3509
- edgesUrl: z23.string(),
3510
- vertexBytes: z23.number().int().nullable(),
3511
- edgesBytes: z23.number().int().nullable(),
3512
- lastModified: z23.string().nullable()
3513
- });
3514
- var ccCachedReleaseSchema = z23.object({
3515
- release: z23.string(),
3591
+ sources: z24.array(backlinkSourceAvailabilityDtoSchema),
3592
+ anyConnected: z24.boolean(),
3593
+ anyData: z24.boolean()
3594
+ });
3595
+ var backlinksInstallStatusDtoSchema = z24.object({
3596
+ duckdbInstalled: z24.boolean(),
3597
+ duckdbVersion: z24.string().nullable().optional(),
3598
+ duckdbSpec: z24.string(),
3599
+ pluginDir: z24.string()
3600
+ });
3601
+ var backlinksInstallResultDtoSchema = z24.object({
3602
+ installed: z24.boolean(),
3603
+ version: z24.string(),
3604
+ path: z24.string(),
3605
+ alreadyPresent: z24.boolean()
3606
+ });
3607
+ var ccAvailableReleaseSchema = z24.object({
3608
+ release: z24.string(),
3609
+ vertexUrl: z24.string(),
3610
+ edgesUrl: z24.string(),
3611
+ vertexBytes: z24.number().int().nullable(),
3612
+ edgesBytes: z24.number().int().nullable(),
3613
+ lastModified: z24.string().nullable()
3614
+ });
3615
+ var ccCachedReleaseSchema = z24.object({
3616
+ release: z24.string(),
3516
3617
  syncStatus: ccReleaseSyncStatusSchema.nullable(),
3517
- bytes: z23.number().int(),
3518
- lastUsedAt: z23.string().nullable()
3618
+ bytes: z24.number().int(),
3619
+ lastUsedAt: z24.string().nullable()
3519
3620
  });
3520
3621
 
3521
3622
  // ../contracts/src/composites.ts
3522
- import { z as z24 } from "zod";
3523
- var metricToneSchema = z24.enum(["positive", "caution", "negative", "neutral"]);
3524
- var scoreSummarySchema = z24.object({
3525
- label: z24.string(),
3526
- value: z24.string(),
3527
- delta: z24.string(),
3623
+ import { z as z25 } from "zod";
3624
+ var metricToneSchema = z25.enum(["positive", "caution", "negative", "neutral"]);
3625
+ var scoreSummarySchema = z25.object({
3626
+ label: z25.string(),
3627
+ value: z25.string(),
3628
+ delta: z25.string(),
3528
3629
  tone: metricToneSchema,
3529
- description: z24.string(),
3530
- tooltip: z24.string().optional(),
3531
- trend: z24.array(z24.number()),
3532
- progress: z24.number().optional(),
3533
- providerCoverage: z24.string().optional()
3630
+ description: z25.string(),
3631
+ tooltip: z25.string().optional(),
3632
+ trend: z25.array(z25.number()),
3633
+ progress: z25.number().optional(),
3634
+ providerCoverage: z25.string().optional()
3534
3635
  });
3535
3636
  var mentionShareSchema = scoreSummarySchema.extend({
3536
- breakdown: z24.object({
3537
- projectMentionSnapshots: z24.number().int().nonnegative(),
3538
- competitorMentionSnapshots: z24.number().int().nonnegative(),
3539
- perCompetitor: z24.array(z24.object({
3540
- domain: z24.string(),
3541
- mentionSnapshots: z24.number().int().nonnegative(),
3542
- shareOfCompetitiveTotal: z24.number()
3637
+ breakdown: z25.object({
3638
+ projectMentionSnapshots: z25.number().int().nonnegative(),
3639
+ competitorMentionSnapshots: z25.number().int().nonnegative(),
3640
+ perCompetitor: z25.array(z25.object({
3641
+ domain: z25.string(),
3642
+ mentionSnapshots: z25.number().int().nonnegative(),
3643
+ shareOfCompetitiveTotal: z25.number()
3543
3644
  })),
3544
- snapshotsWithAnswerText: z24.number().int().nonnegative(),
3545
- snapshotsTotal: z24.number().int().nonnegative()
3645
+ snapshotsWithAnswerText: z25.number().int().nonnegative(),
3646
+ snapshotsTotal: z25.number().int().nonnegative()
3546
3647
  })
3547
3648
  });
3548
- var movementSummarySchema = z24.object({
3549
- gained: z24.number().int().nonnegative(),
3550
- lost: z24.number().int().nonnegative(),
3649
+ var movementSummarySchema = z25.object({
3650
+ gained: z25.number().int().nonnegative(),
3651
+ lost: z25.number().int().nonnegative(),
3551
3652
  tone: metricToneSchema,
3552
- hasPreviousRun: z24.boolean(),
3553
- gainedQueries: z24.array(z24.string()).optional(),
3554
- lostQueries: z24.array(z24.string()).optional()
3555
- });
3556
- var movementComparisonSchema = z24.object({
3557
- hasPreviousRun: z24.boolean(),
3558
- comparable: z24.boolean(),
3559
- querySetChanged: z24.boolean(),
3560
- previousRunAt: z24.string().nullable(),
3561
- currentQueryCount: z24.number().int().nonnegative(),
3562
- previousQueryCount: z24.number().int().nonnegative(),
3563
- comparableQueryCount: z24.number().int().nonnegative(),
3564
- addedQueryCount: z24.number().int().nonnegative(),
3565
- removedQueryCount: z24.number().int().nonnegative(),
3566
- addedQueries: z24.array(z24.string()),
3567
- removedQueries: z24.array(z24.string())
3568
- });
3569
- var projectOverviewInsightSchema = z24.object({
3570
- id: z24.string(),
3571
- projectId: z24.string(),
3572
- runId: z24.string().nullable(),
3573
- type: z24.enum([
3653
+ hasPreviousRun: z25.boolean(),
3654
+ gainedQueries: z25.array(z25.string()).optional(),
3655
+ lostQueries: z25.array(z25.string()).optional()
3656
+ });
3657
+ var movementComparisonSchema = z25.object({
3658
+ hasPreviousRun: z25.boolean(),
3659
+ comparable: z25.boolean(),
3660
+ querySetChanged: z25.boolean(),
3661
+ previousRunAt: z25.string().nullable(),
3662
+ currentQueryCount: z25.number().int().nonnegative(),
3663
+ previousQueryCount: z25.number().int().nonnegative(),
3664
+ comparableQueryCount: z25.number().int().nonnegative(),
3665
+ addedQueryCount: z25.number().int().nonnegative(),
3666
+ removedQueryCount: z25.number().int().nonnegative(),
3667
+ addedQueries: z25.array(z25.string()),
3668
+ removedQueries: z25.array(z25.string())
3669
+ });
3670
+ var projectOverviewInsightSchema = z25.object({
3671
+ id: z25.string(),
3672
+ projectId: z25.string(),
3673
+ runId: z25.string().nullable(),
3674
+ type: z25.enum([
3574
3675
  "regression",
3575
3676
  "gain",
3576
3677
  "opportunity",
@@ -3586,70 +3687,70 @@ var projectOverviewInsightSchema = z24.object({
3586
3687
  "gbp-metric-drop",
3587
3688
  "gbp-keyword-drop"
3588
3689
  ]),
3589
- severity: z24.enum(["critical", "high", "medium", "low"]),
3590
- title: z24.string(),
3591
- query: z24.string(),
3592
- provider: z24.string(),
3593
- recommendation: z24.object({
3594
- action: z24.string(),
3595
- target: z24.string().optional(),
3596
- reason: z24.string()
3690
+ severity: z25.enum(["critical", "high", "medium", "low"]),
3691
+ title: z25.string(),
3692
+ query: z25.string(),
3693
+ provider: z25.string(),
3694
+ recommendation: z25.object({
3695
+ action: z25.string(),
3696
+ target: z25.string().optional(),
3697
+ reason: z25.string()
3597
3698
  }).optional(),
3598
- cause: z24.object({
3599
- cause: z24.string(),
3600
- competitorDomain: z24.string().optional(),
3601
- details: z24.string().optional()
3699
+ cause: z25.object({
3700
+ cause: z25.string(),
3701
+ competitorDomain: z25.string().optional(),
3702
+ details: z25.string().optional()
3602
3703
  }).optional(),
3603
- dismissed: z24.boolean(),
3604
- createdAt: z24.string()
3605
- });
3606
- var projectOverviewHealthSchema = z24.object({
3607
- id: z24.string(),
3608
- projectId: z24.string(),
3609
- runId: z24.string().nullable(),
3610
- overallCitedRate: z24.number(),
3611
- overallMentionRate: z24.number(),
3612
- totalPairs: z24.number().int().nonnegative(),
3613
- citedPairs: z24.number().int().nonnegative(),
3614
- mentionedPairs: z24.number().int().nonnegative(),
3615
- providerBreakdown: z24.record(z24.string(), z24.object({
3616
- citedRate: z24.number(),
3617
- mentionRate: z24.number(),
3618
- cited: z24.number().int().nonnegative(),
3619
- mentioned: z24.number().int().nonnegative(),
3620
- total: z24.number().int().nonnegative()
3704
+ dismissed: z25.boolean(),
3705
+ createdAt: z25.string()
3706
+ });
3707
+ var projectOverviewHealthSchema = z25.object({
3708
+ id: z25.string(),
3709
+ projectId: z25.string(),
3710
+ runId: z25.string().nullable(),
3711
+ overallCitedRate: z25.number(),
3712
+ overallMentionRate: z25.number(),
3713
+ totalPairs: z25.number().int().nonnegative(),
3714
+ citedPairs: z25.number().int().nonnegative(),
3715
+ mentionedPairs: z25.number().int().nonnegative(),
3716
+ providerBreakdown: z25.record(z25.string(), z25.object({
3717
+ citedRate: z25.number(),
3718
+ mentionRate: z25.number(),
3719
+ cited: z25.number().int().nonnegative(),
3720
+ mentioned: z25.number().int().nonnegative(),
3721
+ total: z25.number().int().nonnegative()
3621
3722
  })),
3622
- createdAt: z24.string(),
3623
- status: z24.enum(["ready", "no-data"]),
3624
- reason: z24.literal("no-runs-yet").optional()
3723
+ createdAt: z25.string(),
3724
+ status: z25.enum(["ready", "no-data"]),
3725
+ reason: z25.literal("no-runs-yet").optional()
3625
3726
  });
3626
- var projectOverviewDtoSchema = z24.object({
3727
+ var projectOverviewDtoSchema = z25.object({
3627
3728
  project: projectDtoSchema,
3628
3729
  latestRun: latestProjectRunDtoSchema,
3629
3730
  health: projectOverviewHealthSchema.nullable(),
3630
- topInsights: z24.array(projectOverviewInsightSchema),
3631
- queryCounts: z24.object({
3632
- totalQueries: z24.number().int().nonnegative(),
3633
- citedQueries: z24.number().int().nonnegative(),
3634
- notCitedQueries: z24.number().int().nonnegative(),
3635
- citedRate: z24.number(),
3636
- mentionedQueries: z24.number().int().nonnegative(),
3637
- notMentionedQueries: z24.number().int().nonnegative(),
3638
- mentionRate: z24.number()
3731
+ topInsights: z25.array(projectOverviewInsightSchema),
3732
+ queryCounts: z25.object({
3733
+ totalQueries: z25.number().int().nonnegative(),
3734
+ citedQueries: z25.number().int().nonnegative(),
3735
+ notCitedQueries: z25.number().int().nonnegative(),
3736
+ citedRate: z25.number(),
3737
+ mentionedQueries: z25.number().int().nonnegative(),
3738
+ notMentionedQueries: z25.number().int().nonnegative(),
3739
+ mentionRate: z25.number()
3639
3740
  }),
3640
- providers: z24.array(z24.object({
3641
- provider: z24.string(),
3642
- citedRate: z24.number(),
3643
- cited: z24.number().int().nonnegative(),
3644
- total: z24.number().int().nonnegative()
3741
+ providers: z25.array(z25.object({
3742
+ provider: z25.string(),
3743
+ citedRate: z25.number(),
3744
+ cited: z25.number().int().nonnegative(),
3745
+ total: z25.number().int().nonnegative()
3645
3746
  })),
3646
- transitions: z24.object({
3647
- since: z24.string().nullable(),
3648
- gained: z24.number().int().nonnegative(),
3649
- lost: z24.number().int().nonnegative(),
3650
- emerging: z24.number().int().nonnegative()
3747
+ transitions: z25.object({
3748
+ since: z25.string().nullable(),
3749
+ gained: z25.number().int().nonnegative(),
3750
+ lost: z25.number().int().nonnegative(),
3751
+ emerging: z25.number().int().nonnegative()
3651
3752
  }),
3652
- scores: z24.object({
3753
+ scores: z25.object({
3653
3754
  mention: scoreSummarySchema,
3654
3755
  visibility: scoreSummarySchema,
3655
3756
  mentionShare: mentionShareSchema,
@@ -3663,72 +3764,72 @@ var projectOverviewDtoSchema = z24.object({
3663
3764
  citationMovement: movementSummarySchema,
3664
3765
  mentionMovement: movementSummarySchema,
3665
3766
  movementComparison: movementComparisonSchema,
3666
- competitors: z24.array(z24.object({
3667
- id: z24.string(),
3668
- domain: z24.string(),
3669
- citationCount: z24.number().int().nonnegative(),
3670
- totalQueries: z24.number().int().nonnegative(),
3671
- pressureLabel: z24.enum(["None", "Low", "Moderate", "High"]),
3672
- citedQueries: z24.array(z24.string())
3767
+ competitors: z25.array(z25.object({
3768
+ id: z25.string(),
3769
+ domain: z25.string(),
3770
+ citationCount: z25.number().int().nonnegative(),
3771
+ totalQueries: z25.number().int().nonnegative(),
3772
+ pressureLabel: z25.enum(["None", "Low", "Moderate", "High"]),
3773
+ citedQueries: z25.array(z25.string())
3673
3774
  })),
3674
- providerScores: z24.array(z24.object({
3675
- provider: z24.string(),
3676
- model: z24.string().nullable(),
3677
- score: z24.number(),
3678
- cited: z24.number().int().nonnegative(),
3679
- total: z24.number().int().nonnegative(),
3680
- trend: z24.array(z24.number()).optional()
3775
+ providerScores: z25.array(z25.object({
3776
+ provider: z25.string(),
3777
+ model: z25.string().nullable(),
3778
+ score: z25.number(),
3779
+ cited: z25.number().int().nonnegative(),
3780
+ total: z25.number().int().nonnegative(),
3781
+ trend: z25.array(z25.number()).optional()
3681
3782
  })),
3682
- attentionItems: z24.array(z24.object({
3683
- id: z24.string(),
3783
+ attentionItems: z25.array(z25.object({
3784
+ id: z25.string(),
3684
3785
  tone: metricToneSchema,
3685
- title: z24.string(),
3686
- detail: z24.string(),
3687
- actionLabel: z24.string(),
3688
- href: z24.string()
3786
+ title: z25.string(),
3787
+ detail: z25.string(),
3788
+ actionLabel: z25.string(),
3789
+ href: z25.string()
3689
3790
  })),
3690
- runHistory: z24.array(z24.object({
3691
- runId: z24.string(),
3692
- createdAt: z24.string(),
3693
- citedCount: z24.number().int().nonnegative(),
3694
- totalCount: z24.number().int().nonnegative(),
3695
- citationRate: z24.number(),
3696
- mentionedCount: z24.number().int().nonnegative(),
3697
- mentionRate: z24.number(),
3698
- status: z24.string()
3791
+ runHistory: z25.array(z25.object({
3792
+ runId: z25.string(),
3793
+ createdAt: z25.string(),
3794
+ citedCount: z25.number().int().nonnegative(),
3795
+ totalCount: z25.number().int().nonnegative(),
3796
+ citationRate: z25.number(),
3797
+ mentionedCount: z25.number().int().nonnegative(),
3798
+ mentionRate: z25.number(),
3799
+ status: z25.string()
3699
3800
  })),
3700
- suggestedQueries: z24.object({
3701
- rows: z24.array(z24.object({
3702
- query: z24.string(),
3703
- impressions: z24.number(),
3704
- clicks: z24.number(),
3705
- avgPosition: z24.number(),
3706
- reason: z24.string()
3801
+ suggestedQueries: z25.object({
3802
+ rows: z25.array(z25.object({
3803
+ query: z25.string(),
3804
+ impressions: z25.number(),
3805
+ clicks: z25.number(),
3806
+ avgPosition: z25.number(),
3807
+ reason: z25.string()
3707
3808
  })),
3708
- totalCandidates: z24.number().int().nonnegative(),
3709
- skippedAlreadyTracked: z24.number().int().nonnegative()
3809
+ totalCandidates: z25.number().int().nonnegative(),
3810
+ skippedAlreadyTracked: z25.number().int().nonnegative()
3710
3811
  }),
3711
- dateRangeLabel: z24.string(),
3712
- contextLabel: z24.string()
3713
- });
3714
- var searchHitKindSchema = z24.enum(["snapshot", "insight"]);
3715
- var projectSearchSnapshotHitSchema = z24.object({
3716
- kind: z24.literal("snapshot"),
3717
- id: z24.string(),
3718
- runId: z24.string(),
3719
- query: z24.string(),
3720
- provider: z24.string(),
3721
- model: z24.string().nullable(),
3812
+ dateRangeLabel: z25.string(),
3813
+ contextLabel: z25.string()
3814
+ });
3815
+ var searchHitKindSchema = z25.enum(["snapshot", "insight"]);
3816
+ var projectSearchSnapshotHitSchema = z25.object({
3817
+ kind: z25.literal("snapshot"),
3818
+ id: z25.string(),
3819
+ runId: z25.string(),
3820
+ query: z25.string(),
3821
+ provider: z25.string(),
3822
+ model: z25.string().nullable(),
3722
3823
  citationState: citationStateSchema,
3723
- matchedField: z24.enum(["answerText", "citedDomains", "searchQueries", "query"]),
3724
- snippet: z24.string(),
3725
- createdAt: z24.string()
3726
- });
3727
- var projectSearchInsightHitSchema = z24.object({
3728
- kind: z24.literal("insight"),
3729
- id: z24.string(),
3730
- runId: z24.string().nullable(),
3731
- type: z24.enum([
3824
+ matchedField: z25.enum(["answerText", "citedDomains", "searchQueries", "query"]),
3825
+ snippet: z25.string(),
3826
+ createdAt: z25.string()
3827
+ });
3828
+ var projectSearchInsightHitSchema = z25.object({
3829
+ kind: z25.literal("insight"),
3830
+ id: z25.string(),
3831
+ runId: z25.string().nullable(),
3832
+ type: z25.enum([
3732
3833
  "regression",
3733
3834
  "gain",
3734
3835
  "opportunity",
@@ -3745,29 +3846,29 @@ var projectSearchInsightHitSchema = z24.object({
3745
3846
  "gbp-metric-drop",
3746
3847
  "gbp-keyword-drop"
3747
3848
  ]),
3748
- severity: z24.enum(["critical", "high", "medium", "low"]),
3749
- title: z24.string(),
3750
- query: z24.string(),
3751
- provider: z24.string(),
3752
- matchedField: z24.enum(["title", "query", "recommendation", "cause"]),
3753
- snippet: z24.string(),
3754
- dismissed: z24.boolean(),
3755
- createdAt: z24.string()
3756
- });
3757
- var projectSearchHitSchema = z24.discriminatedUnion("kind", [
3849
+ severity: z25.enum(["critical", "high", "medium", "low"]),
3850
+ title: z25.string(),
3851
+ query: z25.string(),
3852
+ provider: z25.string(),
3853
+ matchedField: z25.enum(["title", "query", "recommendation", "cause"]),
3854
+ snippet: z25.string(),
3855
+ dismissed: z25.boolean(),
3856
+ createdAt: z25.string()
3857
+ });
3858
+ var projectSearchHitSchema = z25.discriminatedUnion("kind", [
3758
3859
  projectSearchSnapshotHitSchema,
3759
3860
  projectSearchInsightHitSchema
3760
3861
  ]);
3761
- var projectSearchResponseSchema = z24.object({
3762
- query: z24.string(),
3763
- totalHits: z24.number().int().nonnegative(),
3764
- truncated: z24.boolean(),
3765
- hits: z24.array(projectSearchHitSchema)
3862
+ var projectSearchResponseSchema = z25.object({
3863
+ query: z25.string(),
3864
+ totalHits: z25.number().int().nonnegative(),
3865
+ truncated: z25.boolean(),
3866
+ hits: z25.array(projectSearchHitSchema)
3766
3867
  });
3767
3868
 
3768
3869
  // ../contracts/src/content.ts
3769
- import { z as z25 } from "zod";
3770
- var contentActionSchema = z25.enum(["create", "expand", "refresh", "add-schema"]);
3870
+ import { z as z26 } from "zod";
3871
+ var contentActionSchema = z26.enum(["create", "expand", "refresh", "add-schema"]);
3771
3872
  var ContentActions = contentActionSchema.enum;
3772
3873
  function contentActionLabel(action) {
3773
3874
  switch (action) {
@@ -3781,9 +3882,9 @@ function contentActionLabel(action) {
3781
3882
  return "Add schema";
3782
3883
  }
3783
3884
  }
3784
- var demandSourceSchema = z25.enum(["gsc", "competitor-evidence", "both"]);
3885
+ var demandSourceSchema = z26.enum(["gsc", "competitor-evidence", "both"]);
3785
3886
  var DemandSources = demandSourceSchema.enum;
3786
- var actionConfidenceSchema = z25.enum(["high", "medium", "low"]);
3887
+ var actionConfidenceSchema = z26.enum(["high", "medium", "low"]);
3787
3888
  var ActionConfidences = actionConfidenceSchema.enum;
3788
3889
  function actionConfidenceLabel(confidence) {
3789
3890
  switch (confidence) {
@@ -3795,7 +3896,7 @@ function actionConfidenceLabel(confidence) {
3795
3896
  return "Low";
3796
3897
  }
3797
3898
  }
3798
- var pageTypeSchema = z25.enum([
3899
+ var pageTypeSchema = z26.enum([
3799
3900
  "blog-post",
3800
3901
  "comparison",
3801
3902
  "listicle",
@@ -3804,7 +3905,7 @@ var pageTypeSchema = z25.enum([
3804
3905
  "glossary"
3805
3906
  ]);
3806
3907
  var PageTypes = pageTypeSchema.enum;
3807
- var contentActionStateSchema = z25.enum([
3908
+ var contentActionStateSchema = z26.enum([
3808
3909
  "proposed",
3809
3910
  "briefed",
3810
3911
  "payload-generated",
@@ -3814,7 +3915,7 @@ var contentActionStateSchema = z25.enum([
3814
3915
  "dismissed"
3815
3916
  ]);
3816
3917
  var ContentActionStates = contentActionStateSchema.enum;
3817
- var winnabilityClassSchema = z25.enum(["ownable", "ceded"]);
3918
+ var winnabilityClassSchema = z26.enum(["ownable", "ceded"]);
3818
3919
  var WinnabilityClasses = winnabilityClassSchema.enum;
3819
3920
  function winnabilityClassLabel(winnabilityClass) {
3820
3921
  switch (winnabilityClass) {
@@ -3849,40 +3950,40 @@ function deriveWinnabilityClass(citedSurfaceDomains, surfaceClasses, threshold =
3849
3950
  winnability
3850
3951
  };
3851
3952
  }
3852
- var ourBestPageSchema = z25.object({
3853
- url: z25.string(),
3854
- gscImpressions: z25.number().nonnegative(),
3855
- gscClicks: z25.number().nonnegative(),
3953
+ var ourBestPageSchema = z26.object({
3954
+ url: z26.string(),
3955
+ gscImpressions: z26.number().nonnegative(),
3956
+ gscClicks: z26.number().nonnegative(),
3856
3957
  // Null when the page came from the inventory fallback (no GSC ranking data).
3857
- gscAvgPosition: z25.number().nonnegative().nullable(),
3858
- organicSessions: z25.number().nonnegative()
3958
+ gscAvgPosition: z26.number().nonnegative().nullable(),
3959
+ organicSessions: z26.number().nonnegative()
3859
3960
  });
3860
- var winningCompetitorSchema = z25.object({
3861
- domain: z25.string(),
3862
- url: z25.string(),
3863
- title: z25.string(),
3864
- citationCount: z25.number().int().nonnegative()
3961
+ var winningCompetitorSchema = z26.object({
3962
+ domain: z26.string(),
3963
+ url: z26.string(),
3964
+ title: z26.string(),
3965
+ citationCount: z26.number().int().nonnegative()
3865
3966
  });
3866
- var scoreBreakdownSchema = z25.object({
3867
- demand: z25.number(),
3868
- competitor: z25.number(),
3869
- absence: z25.number(),
3870
- gapSeverity: z25.number()
3967
+ var scoreBreakdownSchema = z26.object({
3968
+ demand: z26.number(),
3969
+ competitor: z26.number(),
3970
+ absence: z26.number(),
3971
+ gapSeverity: z26.number()
3871
3972
  });
3872
- var existingActionRefSchema = z25.object({
3873
- actionId: z25.string(),
3973
+ var existingActionRefSchema = z26.object({
3974
+ actionId: z26.string(),
3874
3975
  state: contentActionStateSchema,
3875
- lastUpdated: z25.string()
3976
+ lastUpdated: z26.string()
3876
3977
  });
3877
- var contentTargetRowDtoSchema = z25.object({
3878
- targetRef: z25.string(),
3879
- query: z25.string(),
3978
+ var contentTargetRowDtoSchema = z26.object({
3979
+ targetRef: z26.string(),
3980
+ query: z26.string(),
3880
3981
  action: contentActionSchema,
3881
3982
  ourBestPage: ourBestPageSchema.nullable(),
3882
3983
  winningCompetitor: winningCompetitorSchema.nullable(),
3883
- score: z25.number(),
3984
+ score: z26.number(),
3884
3985
  scoreBreakdown: scoreBreakdownSchema,
3885
- drivers: z25.array(z25.string()),
3986
+ drivers: z26.array(z26.string()),
3886
3987
  demandSource: demandSourceSchema,
3887
3988
  actionConfidence: actionConfidenceSchema,
3888
3989
  existingAction: existingActionRefSchema.nullable(),
@@ -3897,134 +3998,134 @@ var contentTargetRowDtoSchema = z25.object({
3897
3998
  * `[0, 1]`. `null` when the gate failed open (no classification coverage for
3898
3999
  * the cited surface) — distinct from a computed `1.0`.
3899
4000
  */
3900
- winnability: z25.number().min(0).max(1).nullable()
3901
- });
3902
- var contentTargetsResponseDtoSchema = z25.object({
3903
- targets: z25.array(contentTargetRowDtoSchema),
3904
- contextMetrics: z25.object({
3905
- totalAiReferralSessions: z25.number().int().nonnegative(),
3906
- latestRunId: z25.string(),
3907
- runTimestamp: z25.string()
4001
+ winnability: z26.number().min(0).max(1).nullable()
4002
+ });
4003
+ var contentTargetsResponseDtoSchema = z26.object({
4004
+ targets: z26.array(contentTargetRowDtoSchema),
4005
+ contextMetrics: z26.object({
4006
+ totalAiReferralSessions: z26.number().int().nonnegative(),
4007
+ latestRunId: z26.string(),
4008
+ runTimestamp: z26.string()
3908
4009
  })
3909
4010
  });
3910
- var contentTargetDismissalDtoSchema = z25.object({
3911
- targetRef: z25.string(),
3912
- addressedUrl: z25.string().nullable(),
3913
- note: z25.string().nullable(),
3914
- dismissedAt: z25.string()
4011
+ var contentTargetDismissalDtoSchema = z26.object({
4012
+ targetRef: z26.string(),
4013
+ addressedUrl: z26.string().nullable(),
4014
+ note: z26.string().nullable(),
4015
+ dismissedAt: z26.string()
3915
4016
  });
3916
- var contentTargetDismissalsResponseDtoSchema = z25.object({
3917
- dismissals: z25.array(contentTargetDismissalDtoSchema)
4017
+ var contentTargetDismissalsResponseDtoSchema = z26.object({
4018
+ dismissals: z26.array(contentTargetDismissalDtoSchema)
3918
4019
  });
3919
- var contentTargetDismissRequestSchema = z25.object({
3920
- targetRef: z25.string().min(1),
4020
+ var contentTargetDismissRequestSchema = z26.object({
4021
+ targetRef: z26.string().min(1),
3921
4022
  /** URL of the page the user wrote that addresses this recommendation. Stored verbatim for the audit trail; not currently used to suppress the slug-token matcher. */
3922
- addressedUrl: z25.string().url().optional(),
4023
+ addressedUrl: z26.string().url().optional(),
3923
4024
  /** Free-form note (e.g. "covered in our Q1 content sprint"). 500 char cap is the API surface limit; the DB column is unbounded. */
3924
- note: z25.string().max(500).optional()
4025
+ note: z26.string().max(500).optional()
3925
4026
  });
3926
- var recommendationExplanationDtoSchema = z25.object({
3927
- targetRef: z25.string(),
4027
+ var recommendationExplanationDtoSchema = z26.object({
4028
+ targetRef: z26.string(),
3928
4029
  /** Version of the prompt template used. Bumping the version invalidates the cache forward without touching the table. */
3929
- promptVersion: z25.string(),
4030
+ promptVersion: z26.string(),
3930
4031
  /** Provider that produced the explanation (e.g. "claude", "gemini"). */
3931
- provider: z25.string(),
4032
+ provider: z26.string(),
3932
4033
  /** Model id within that provider (e.g. "claude-sonnet-4-6"). */
3933
- model: z25.string(),
4034
+ model: z26.string(),
3934
4035
  /** Markdown-formatted rationale + recommended next steps. */
3935
- responseText: z25.string(),
4036
+ responseText: z26.string(),
3936
4037
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
3937
- costMillicents: z25.number().int().nonnegative(),
3938
- generatedAt: z25.string()
4038
+ costMillicents: z26.number().int().nonnegative(),
4039
+ generatedAt: z26.string()
3939
4040
  });
3940
- var recommendationExplainRequestSchema = z25.object({
4041
+ var recommendationExplainRequestSchema = z26.object({
3941
4042
  /**
3942
4043
  * Optional provider override (e.g. "claude" to force Claude even if
3943
4044
  * the project's default is Gemini). Falls through to project default
3944
4045
  * → auto-detect when omitted.
3945
4046
  */
3946
- provider: z25.string().optional(),
4047
+ provider: z26.string().optional(),
3947
4048
  /**
3948
4049
  * Optional model override within the chosen provider. Falls through to
3949
4050
  * the `analyze`-tier default model when omitted.
3950
4051
  */
3951
- model: z25.string().optional(),
4052
+ model: z26.string().optional(),
3952
4053
  /**
3953
4054
  * Force a fresh LLM call even if a cached explanation exists for the
3954
4055
  * current prompt version. Use sparingly — defeats the cache.
3955
4056
  */
3956
- forceRefresh: z25.boolean().optional()
4057
+ forceRefresh: z26.boolean().optional()
3957
4058
  });
3958
- var contentBriefDtoSchema = z25.object({
4059
+ var contentBriefDtoSchema = z26.object({
3959
4060
  /** The query the brief is for (echoed from the recommendation). */
3960
- targetQuery: z25.string().trim().min(1),
4061
+ targetQuery: z26.string().trim().min(1),
3961
4062
  /** Always `ownable` in practice — the gate rejects `ceded` before synthesis. */
3962
4063
  winnabilityClass: winnabilityClassSchema,
3963
4064
  /** The differentiated content angle to take. */
3964
- angle: z25.string().trim().min(1),
4065
+ angle: z26.string().trim().min(1),
3965
4066
  /** Why this query is winnable, citing the cited-surface signal. */
3966
- whyWinnable: z25.string().trim().min(1),
4067
+ whyWinnable: z26.string().trim().min(1),
3967
4068
  /** The schema.org type or markup to add or extend. */
3968
- schemaHookup: z25.string().trim().min(1),
4069
+ schemaHookup: z26.string().trim().min(1),
3969
4070
  /** Why the cited surface is controllable (the ownable-vs-ceded reasoning). */
3970
- controllableSurfaceRationale: z25.string().trim().min(1)
4071
+ controllableSurfaceRationale: z26.string().trim().min(1)
3971
4072
  });
3972
- var recommendationBriefDtoSchema = z25.object({
3973
- targetRef: z25.string(),
4073
+ var recommendationBriefDtoSchema = z26.object({
4074
+ targetRef: z26.string(),
3974
4075
  /** Version of the brief prompt template; bumping invalidates the cache forward. */
3975
- promptVersion: z25.string(),
3976
- provider: z25.string(),
3977
- model: z25.string(),
4076
+ promptVersion: z26.string(),
4077
+ provider: z26.string(),
4078
+ model: z26.string(),
3978
4079
  brief: contentBriefDtoSchema,
3979
4080
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
3980
- costMillicents: z25.number().int().nonnegative(),
3981
- generatedAt: z25.string()
4081
+ costMillicents: z26.number().int().nonnegative(),
4082
+ generatedAt: z26.string()
3982
4083
  });
3983
- var domainClassificationDtoSchema = z25.object({
3984
- domain: z25.string(),
4084
+ var domainClassificationDtoSchema = z26.object({
4085
+ domain: z26.string(),
3985
4086
  competitorType: discoveryCompetitorTypeSchema,
3986
- hits: z25.number().int().nonnegative(),
3987
- updatedAt: z25.string()
3988
- });
3989
- var domainClassificationsResponseDtoSchema = z25.object({
3990
- classifications: z25.array(domainClassificationDtoSchema)
3991
- });
3992
- var contentGroundingSourceSchema = z25.object({
3993
- uri: z25.string(),
3994
- title: z25.string(),
3995
- domain: z25.string(),
3996
- isOurDomain: z25.boolean(),
3997
- isCompetitor: z25.boolean(),
3998
- citationCount: z25.number().int().nonnegative(),
3999
- providers: z25.array(providerNameSchema)
4087
+ hits: z26.number().int().nonnegative(),
4088
+ updatedAt: z26.string()
4000
4089
  });
4001
- var contentSourceRowDtoSchema = z25.object({
4002
- query: z25.string(),
4003
- groundingSources: z25.array(contentGroundingSourceSchema)
4004
- });
4005
- var contentSourcesResponseDtoSchema = z25.object({
4006
- sources: z25.array(contentSourceRowDtoSchema),
4007
- latestRunId: z25.string()
4008
- });
4009
- var contentGapRowDtoSchema = z25.object({
4010
- query: z25.string(),
4011
- competitorDomains: z25.array(z25.string()),
4012
- competitorCount: z25.number().int().nonnegative(),
4013
- missRate: z25.number().min(0).max(1),
4014
- lastSeenInRunId: z25.string()
4090
+ var domainClassificationsResponseDtoSchema = z26.object({
4091
+ classifications: z26.array(domainClassificationDtoSchema)
4015
4092
  });
4016
- var contentGapsResponseDtoSchema = z25.object({
4017
- gaps: z25.array(contentGapRowDtoSchema),
4018
- latestRunId: z25.string()
4093
+ var contentGroundingSourceSchema = z26.object({
4094
+ uri: z26.string(),
4095
+ title: z26.string(),
4096
+ domain: z26.string(),
4097
+ isOurDomain: z26.boolean(),
4098
+ isCompetitor: z26.boolean(),
4099
+ citationCount: z26.number().int().nonnegative(),
4100
+ providers: z26.array(providerNameSchema)
4101
+ });
4102
+ var contentSourceRowDtoSchema = z26.object({
4103
+ query: z26.string(),
4104
+ groundingSources: z26.array(contentGroundingSourceSchema)
4105
+ });
4106
+ var contentSourcesResponseDtoSchema = z26.object({
4107
+ sources: z26.array(contentSourceRowDtoSchema),
4108
+ latestRunId: z26.string()
4109
+ });
4110
+ var contentGapRowDtoSchema = z26.object({
4111
+ query: z26.string(),
4112
+ competitorDomains: z26.array(z26.string()),
4113
+ competitorCount: z26.number().int().nonnegative(),
4114
+ missRate: z26.number().min(0).max(1),
4115
+ lastSeenInRunId: z26.string()
4116
+ });
4117
+ var contentGapsResponseDtoSchema = z26.object({
4118
+ gaps: z26.array(contentGapRowDtoSchema),
4119
+ latestRunId: z26.string()
4019
4120
  });
4020
4121
 
4021
4122
  // ../contracts/src/doctor.ts
4022
- import { z as z26 } from "zod";
4023
- var checkStatusSchema = z26.enum(["ok", "warn", "fail", "skipped"]);
4123
+ import { z as z27 } from "zod";
4124
+ var checkStatusSchema = z27.enum(["ok", "warn", "fail", "skipped"]);
4024
4125
  var CheckStatuses = checkStatusSchema.enum;
4025
- var checkScopeSchema = z26.enum(["global", "project"]);
4126
+ var checkScopeSchema = z27.enum(["global", "project"]);
4026
4127
  var CheckScopes = checkScopeSchema.enum;
4027
- var checkCategorySchema = z26.enum([
4128
+ var checkCategorySchema = z27.enum([
4028
4129
  "auth",
4029
4130
  "config",
4030
4131
  "providers",
@@ -4035,31 +4136,31 @@ var checkCategorySchema = z26.enum([
4035
4136
  "agent"
4036
4137
  ]);
4037
4138
  var CheckCategories = checkCategorySchema.enum;
4038
- var checkResultSchema = z26.object({
4039
- id: z26.string(),
4139
+ var checkResultSchema = z27.object({
4140
+ id: z27.string(),
4040
4141
  category: checkCategorySchema,
4041
4142
  scope: checkScopeSchema,
4042
- title: z26.string(),
4143
+ title: z27.string(),
4043
4144
  status: checkStatusSchema,
4044
- code: z26.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
4045
- summary: z26.string(),
4046
- remediation: z26.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
4047
- details: z26.record(z26.string(), z26.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
4048
- durationMs: z26.number().int().nonnegative().describe("How long the check took to execute.")
4145
+ code: z27.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
4146
+ summary: z27.string(),
4147
+ remediation: z27.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
4148
+ details: z27.record(z27.string(), z27.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
4149
+ durationMs: z27.number().int().nonnegative().describe("How long the check took to execute.")
4049
4150
  });
4050
- var doctorReportSchema = z26.object({
4151
+ var doctorReportSchema = z27.object({
4051
4152
  scope: checkScopeSchema,
4052
- project: z26.string().nullable().describe('Project name when scope is "project", null otherwise.'),
4053
- generatedAt: z26.string().describe("ISO-8601 timestamp when this doctor run started."),
4054
- durationMs: z26.number().int().nonnegative(),
4055
- summary: z26.object({
4056
- total: z26.number().int().nonnegative(),
4057
- ok: z26.number().int().nonnegative(),
4058
- warn: z26.number().int().nonnegative(),
4059
- fail: z26.number().int().nonnegative(),
4060
- skipped: z26.number().int().nonnegative()
4153
+ project: z27.string().nullable().describe('Project name when scope is "project", null otherwise.'),
4154
+ generatedAt: z27.string().describe("ISO-8601 timestamp when this doctor run started."),
4155
+ durationMs: z27.number().int().nonnegative(),
4156
+ summary: z27.object({
4157
+ total: z27.number().int().nonnegative(),
4158
+ ok: z27.number().int().nonnegative(),
4159
+ warn: z27.number().int().nonnegative(),
4160
+ fail: z27.number().int().nonnegative(),
4161
+ skipped: z27.number().int().nonnegative()
4061
4162
  }),
4062
- checks: z26.array(checkResultSchema)
4163
+ checks: z27.array(checkResultSchema)
4063
4164
  });
4064
4165
  function summarizeCheckResults(results) {
4065
4166
  const summary = { total: results.length, ok: 0, warn: 0, fail: 0, skipped: 0 };
@@ -4088,52 +4189,52 @@ function normalizeQueryText(value) {
4088
4189
  }
4089
4190
 
4090
4191
  // ../contracts/src/citations.ts
4091
- import { z as z27 } from "zod";
4092
- var citationCoverageProviderSchema = z27.object({
4093
- provider: z27.string(),
4192
+ import { z as z28 } from "zod";
4193
+ var citationCoverageProviderSchema = z28.object({
4194
+ provider: z28.string(),
4094
4195
  citationState: citationStateSchema,
4095
- cited: z27.boolean(),
4096
- mentioned: z27.boolean(),
4097
- runId: z27.string(),
4098
- runCreatedAt: z27.string()
4099
- });
4100
- var citationCoverageRowSchema = z27.object({
4101
- queryId: z27.string(),
4102
- query: z27.string(),
4103
- providers: z27.array(citationCoverageProviderSchema),
4104
- citedCount: z27.number().int().nonnegative(),
4105
- mentionedCount: z27.number().int().nonnegative(),
4106
- totalProviders: z27.number().int().nonnegative()
4107
- });
4108
- var competitorGapRowSchema = z27.object({
4109
- queryId: z27.string(),
4110
- query: z27.string(),
4111
- provider: z27.string(),
4112
- citingCompetitors: z27.array(z27.string()),
4113
- runId: z27.string(),
4114
- runCreatedAt: z27.string()
4115
- });
4116
- var citationVisibilitySummarySchema = z27.object({
4117
- providersConfigured: z27.number().int().nonnegative(),
4118
- providersCiting: z27.number().int().nonnegative(),
4119
- providersMentioning: z27.number().int().nonnegative(),
4120
- totalQueries: z27.number().int().nonnegative(),
4196
+ cited: z28.boolean(),
4197
+ mentioned: z28.boolean(),
4198
+ runId: z28.string(),
4199
+ runCreatedAt: z28.string()
4200
+ });
4201
+ var citationCoverageRowSchema = z28.object({
4202
+ queryId: z28.string(),
4203
+ query: z28.string(),
4204
+ providers: z28.array(citationCoverageProviderSchema),
4205
+ citedCount: z28.number().int().nonnegative(),
4206
+ mentionedCount: z28.number().int().nonnegative(),
4207
+ totalProviders: z28.number().int().nonnegative()
4208
+ });
4209
+ var competitorGapRowSchema = z28.object({
4210
+ queryId: z28.string(),
4211
+ query: z28.string(),
4212
+ provider: z28.string(),
4213
+ citingCompetitors: z28.array(z28.string()),
4214
+ runId: z28.string(),
4215
+ runCreatedAt: z28.string()
4216
+ });
4217
+ var citationVisibilitySummarySchema = z28.object({
4218
+ providersConfigured: z28.number().int().nonnegative(),
4219
+ providersCiting: z28.number().int().nonnegative(),
4220
+ providersMentioning: z28.number().int().nonnegative(),
4221
+ totalQueries: z28.number().int().nonnegative(),
4121
4222
  // Cross-tab buckets — each tracked query with at least one snapshot lands
4122
4223
  // in exactly one of these. Queries with zero snapshots are not counted in
4123
4224
  // any bucket; (sum of buckets) ≤ totalQueries.
4124
- queriesCitedAndMentioned: z27.number().int().nonnegative(),
4125
- queriesCitedOnly: z27.number().int().nonnegative(),
4126
- queriesMentionedOnly: z27.number().int().nonnegative(),
4127
- queriesInvisible: z27.number().int().nonnegative(),
4128
- latestRunId: z27.string().nullable(),
4129
- latestRunAt: z27.string().nullable()
4130
- });
4131
- var citationVisibilityResponseSchema = z27.object({
4225
+ queriesCitedAndMentioned: z28.number().int().nonnegative(),
4226
+ queriesCitedOnly: z28.number().int().nonnegative(),
4227
+ queriesMentionedOnly: z28.number().int().nonnegative(),
4228
+ queriesInvisible: z28.number().int().nonnegative(),
4229
+ latestRunId: z28.string().nullable(),
4230
+ latestRunAt: z28.string().nullable()
4231
+ });
4232
+ var citationVisibilityResponseSchema = z28.object({
4132
4233
  summary: citationVisibilitySummarySchema,
4133
- byQuery: z27.array(citationCoverageRowSchema),
4134
- competitorGaps: z27.array(competitorGapRowSchema),
4135
- status: z27.enum(["ready", "no-data"]),
4136
- reason: z27.enum(["no-runs-yet", "no-queries"]).optional()
4234
+ byQuery: z28.array(citationCoverageRowSchema),
4235
+ competitorGaps: z28.array(competitorGapRowSchema),
4236
+ status: z28.enum(["ready", "no-data"]),
4237
+ reason: z28.enum(["no-runs-yet", "no-queries"]).optional()
4137
4238
  });
4138
4239
  function emptyCitationVisibility(reason) {
4139
4240
  return {
@@ -4160,10 +4261,10 @@ function citationStateToCited(state) {
4160
4261
  }
4161
4262
 
4162
4263
  // ../contracts/src/report.ts
4163
- import { z as z28 } from "zod";
4264
+ import { z as z29 } from "zod";
4164
4265
  var REPORT_PERIOD_OPTIONS = [7, 14, 30, 90];
4165
4266
  var REPORT_DEFAULT_PERIOD_DAYS = 30;
4166
- var reportPeriodSchema = z28.union([z28.literal(7), z28.literal(14), z28.literal(30), z28.literal(90)]).describe("Report window in days (7, 14, 30, or 90). Defaults to 30 when omitted.");
4267
+ var reportPeriodSchema = z29.union([z29.literal(7), z29.literal(14), z29.literal(30), z29.literal(90)]).describe("Report window in days (7, 14, 30, or 90). Defaults to 30 when omitted.");
4167
4268
  function isReportPeriodDays(n) {
4168
4269
  return REPORT_PERIOD_OPTIONS.includes(n);
4169
4270
  }
@@ -4178,45 +4279,45 @@ function parseReportPeriodDays(value) {
4178
4279
  function reportComparisonWindowDays(periodDays) {
4179
4280
  return Math.max(1, Math.floor(periodDays / 2));
4180
4281
  }
4181
- var providerLocationTreatmentSchema = z28.enum([
4282
+ var providerLocationTreatmentSchema = z29.enum([
4182
4283
  "prompt",
4183
4284
  "request-param",
4184
4285
  "browser-geo",
4185
4286
  "ignored"
4186
4287
  ]);
4187
- var reportMetaLocationSchema = z28.object({
4288
+ var reportMetaLocationSchema = z29.object({
4188
4289
  /** Human-readable label as configured on the project (e.g. "michigan"). */
4189
- label: z28.string(),
4290
+ label: z29.string(),
4190
4291
  /** Resolved city/region/country from the project's `LocationContext`. */
4191
- city: z28.string(),
4192
- region: z28.string(),
4193
- country: z28.string(),
4292
+ city: z29.string(),
4293
+ region: z29.string(),
4294
+ country: z29.string(),
4194
4295
  /**
4195
4296
  * Other locations configured on the project that did NOT power this report.
4196
4297
  * When non-empty, callers should make clear that the report is location-scoped:
4197
4298
  * a separate sweep is needed to see how AI engines respond from each one.
4198
4299
  */
4199
- otherConfiguredLabels: z28.array(z28.string())
4300
+ otherConfiguredLabels: z29.array(z29.string())
4200
4301
  });
4201
- var reportProviderLocationHandlingSchema = z28.object({
4302
+ var reportProviderLocationHandlingSchema = z29.object({
4202
4303
  /** Provider name (matches `query_snapshots.provider`). */
4203
- provider: z28.string(),
4304
+ provider: z29.string(),
4204
4305
  /** How this provider applied the configured location during this run. */
4205
4306
  treatment: providerLocationTreatmentSchema,
4206
4307
  /** One-sentence explanation suitable for the report. */
4207
- description: z28.string()
4308
+ description: z29.string()
4208
4309
  });
4209
- var reportMetaSchema = z28.object({
4310
+ var reportMetaSchema = z29.object({
4210
4311
  /** ISO timestamp the report was generated (server clock). */
4211
- generatedAt: z28.string(),
4312
+ generatedAt: z29.string(),
4212
4313
  /** Project the report covers. */
4213
- project: z28.object({
4214
- id: z28.string(),
4215
- name: z28.string(),
4216
- displayName: z28.string(),
4217
- canonicalDomain: z28.string(),
4218
- country: z28.string(),
4219
- language: z28.string()
4314
+ project: z29.object({
4315
+ id: z29.string(),
4316
+ name: z29.string(),
4317
+ displayName: z29.string(),
4318
+ canonicalDomain: z29.string(),
4319
+ country: z29.string(),
4320
+ language: z29.string()
4220
4321
  }),
4221
4322
  /**
4222
4323
  * The location that powered the latest visibility run, when one was set.
@@ -4231,31 +4332,31 @@ var reportMetaSchema = z28.object({
4231
4332
  * each provider's answer — some providers append it to the prompt, some
4232
4333
  * pass it as a structured request field, and some (CDP) ignore it.
4233
4334
  */
4234
- providerLocationHandling: z28.array(reportProviderLocationHandlingSchema),
4335
+ providerLocationHandling: z29.array(reportProviderLocationHandlingSchema),
4235
4336
  /** Earliest data point referenced by the report (ISO date). */
4236
- periodStart: z28.string().nullable(),
4337
+ periodStart: z29.string().nullable(),
4237
4338
  /** Latest data point referenced by the report (ISO date). */
4238
- periodEnd: z28.string().nullable(),
4339
+ periodEnd: z29.string().nullable(),
4239
4340
  /**
4240
4341
  * The selected report window, in days (one of `REPORT_PERIOD_OPTIONS`).
4241
4342
  * Every time-windowed section scopes to this many days; renderers read it to
4242
4343
  * label the window ("Last 30 days", "(30d)"). Defaults to
4243
4344
  * `REPORT_DEFAULT_PERIOD_DAYS` when no `period` is requested.
4244
4345
  */
4245
- periodDays: z28.number().int().positive()
4346
+ periodDays: z29.number().int().positive()
4246
4347
  });
4247
- var reportExecutiveSummarySchema = z28.object({
4348
+ var reportExecutiveSummarySchema = z29.object({
4248
4349
  /**
4249
4350
  * 0..100 — share of tracked queries that were cited by at least one
4250
4351
  * provider in the latest run. "Cited" means the project's domain appeared
4251
4352
  * in the source list / grounding the AI used to answer. Computed per-query
4252
4353
  * (not per-(query × provider)) so the rate is invariant to provider count.
4253
4354
  */
4254
- citationRate: z28.number(),
4355
+ citationRate: z29.number(),
4255
4356
  /** Numerator of `citationRate` — distinct tracked queries cited by ≥1 provider in the latest run. */
4256
- citedQueryCount: z28.number(),
4357
+ citedQueryCount: z29.number(),
4257
4358
  /** Denominator of `citationRate` — total tracked queries. */
4258
- totalQueryCount: z28.number(),
4359
+ totalQueryCount: z29.number(),
4259
4360
  /**
4260
4361
  * 0..100 — share of tracked queries where the project's brand or domain
4261
4362
  * appeared in at least one provider's answer text in the latest run.
@@ -4263,71 +4364,71 @@ var reportExecutiveSummarySchema = z28.object({
4263
4364
  * the prose without citing your domain in its sources, and vice versa.
4264
4365
  * Same per-query denominator as `citationRate` for consistency.
4265
4366
  */
4266
- mentionRate: z28.number(),
4367
+ mentionRate: z29.number(),
4267
4368
  /** Numerator of `mentionRate` — distinct tracked queries mentioned in ≥1 provider's answer text. */
4268
- mentionedQueryCount: z28.number(),
4369
+ mentionedQueryCount: z29.number(),
4269
4370
  /** Compared to the previous run: 'up' | 'down' | 'flat' | 'unknown' (no prior run). */
4270
- trend: z28.enum(["up", "down", "flat", "unknown"]),
4371
+ trend: z29.enum(["up", "down", "flat", "unknown"]),
4271
4372
  /** Total tracked queries. */
4272
- queryCount: z28.number(),
4373
+ queryCount: z29.number(),
4273
4374
  /** Total tracked competitors. */
4274
- competitorCount: z28.number(),
4375
+ competitorCount: z29.number(),
4275
4376
  /** Number of providers in the latest run. */
4276
- providerCount: z28.number(),
4377
+ providerCount: z29.number(),
4277
4378
  /** GSC totals across the most-recent sync window. Null when GSC is not connected. */
4278
- gsc: z28.object({
4279
- clicks: z28.number(),
4280
- impressions: z28.number(),
4281
- ctr: z28.number(),
4282
- avgPosition: z28.number(),
4283
- periodStart: z28.string(),
4284
- periodEnd: z28.string()
4379
+ gsc: z29.object({
4380
+ clicks: z29.number(),
4381
+ impressions: z29.number(),
4382
+ ctr: z29.number(),
4383
+ avgPosition: z29.number(),
4384
+ periodStart: z29.string(),
4385
+ periodEnd: z29.string()
4285
4386
  }).nullable(),
4286
4387
  /** GA4 totals across the most-recent sync period. Null when GA4 is not connected. */
4287
- ga: z28.object({
4288
- sessions: z28.number(),
4289
- users: z28.number(),
4290
- periodStart: z28.string(),
4291
- periodEnd: z28.string()
4388
+ ga: z29.object({
4389
+ sessions: z29.number(),
4390
+ users: z29.number(),
4391
+ periodStart: z29.string(),
4392
+ periodEnd: z29.string()
4292
4393
  }).nullable(),
4293
4394
  /** Top 3-5 findings, each rendered as a single-sentence narrative. */
4294
- findings: z28.array(z28.object({
4295
- title: z28.string(),
4296
- detail: z28.string(),
4297
- tone: z28.enum(["positive", "caution", "negative", "neutral"])
4395
+ findings: z29.array(z29.object({
4396
+ title: z29.string(),
4397
+ detail: z29.string(),
4398
+ tone: z29.enum(["positive", "caution", "negative", "neutral"])
4298
4399
  }))
4299
4400
  });
4300
- var citationCellSchema = z28.object({
4301
- citationState: z28.enum(["cited", "not-cited", "pending"]),
4302
- answerMentioned: z28.boolean().nullable(),
4303
- model: z28.string().nullable()
4401
+ var citationCellSchema = z29.object({
4402
+ citationState: z29.enum(["cited", "not-cited", "pending"]),
4403
+ answerMentioned: z29.boolean().nullable(),
4404
+ model: z29.string().nullable()
4304
4405
  });
4305
- var citationScorecardSchema = z28.object({
4306
- queries: z28.array(z28.string()),
4307
- providers: z28.array(z28.string()),
4406
+ var citationScorecardSchema = z29.object({
4407
+ queries: z29.array(z29.string()),
4408
+ providers: z29.array(z29.string()),
4308
4409
  /** matrix[queryIndex][providerIndex] — null when no snapshot exists for the pair. */
4309
- matrix: z28.array(z28.array(citationCellSchema.nullable())),
4410
+ matrix: z29.array(z29.array(citationCellSchema.nullable())),
4310
4411
  /** Per-provider citation + mention rates (0..100). */
4311
- providerRates: z28.array(z28.object({
4312
- provider: z28.string(),
4313
- citedCount: z28.number(),
4412
+ providerRates: z29.array(z29.object({
4413
+ provider: z29.string(),
4414
+ citedCount: z29.number(),
4314
4415
  /** Number of snapshots for this provider where the answer text mentioned the project. */
4315
- mentionedCount: z28.number(),
4316
- totalCount: z28.number(),
4317
- citationRate: z28.number(),
4318
- mentionRate: z28.number()
4416
+ mentionedCount: z29.number(),
4417
+ totalCount: z29.number(),
4418
+ citationRate: z29.number(),
4419
+ mentionRate: z29.number()
4319
4420
  }))
4320
4421
  });
4321
- var competitorRowSchema = z28.object({
4322
- domain: z28.string(),
4422
+ var competitorRowSchema = z29.object({
4423
+ domain: z29.string(),
4323
4424
  /** Number of (query × provider) pairs that cited this competitor. */
4324
- citationCount: z28.number(),
4425
+ citationCount: z29.number(),
4325
4426
  /** Out-of count for the same denominator. */
4326
- totalCount: z28.number(),
4427
+ totalCount: z29.number(),
4327
4428
  /** 'High' | 'Moderate' | 'Low' | 'None' — from buildPortfolioProject pressure logic. */
4328
- pressureLabel: z28.enum(["High", "Moderate", "Low", "None"]),
4429
+ pressureLabel: z29.enum(["High", "Moderate", "Low", "None"]),
4329
4430
  /** Distinct queries on which this competitor was cited. */
4330
- citedQueries: z28.array(z28.string()),
4431
+ citedQueries: z29.array(z29.string()),
4331
4432
  /**
4332
4433
  * Citation share 0..100. Numerator = this competitor's `citationCount`.
4333
4434
  * Denominator = sum of `citationCount` across all competitors plus the
@@ -4335,30 +4436,30 @@ var competitorRowSchema = z28.object({
4335
4436
  * slots in the snapshot. Distinct from the project-level Mention Share
4336
4437
  * gauge — that one is brand-in-answer-text, this one is domain-in-source-list.
4337
4438
  */
4338
- sharePct: z28.number(),
4439
+ sharePct: z29.number(),
4339
4440
  /**
4340
4441
  * URLs from the latest run's grounding sources whose host matches this
4341
4442
  * competitor's domain, with the queries each URL was cited for. Empty
4342
4443
  * when no grounding-source data is available (e.g. no `rawResponse` JSON
4343
4444
  * stored for the snapshots).
4344
4445
  */
4345
- theirCitedPages: z28.array(z28.object({ url: z28.string(), citedFor: z28.array(z28.string()) }))
4446
+ theirCitedPages: z29.array(z29.object({ url: z29.string(), citedFor: z29.array(z29.string()) }))
4346
4447
  });
4347
- var competitorLandscapeSchema = z28.object({
4448
+ var competitorLandscapeSchema = z29.object({
4348
4449
  /** Project's own citation count (for the bar chart comparing project vs competitors). */
4349
- projectCitationCount: z28.number(),
4350
- competitors: z28.array(competitorRowSchema)
4450
+ projectCitationCount: z29.number(),
4451
+ competitors: z29.array(competitorRowSchema)
4351
4452
  });
4352
- var mentionRowSchema = z28.object({
4353
- domain: z28.string(),
4453
+ var mentionRowSchema = z29.object({
4454
+ domain: z29.string(),
4354
4455
  /** Number of (query × provider) pairs whose answer text mentioned this competitor's brand or domain. */
4355
- mentionCount: z28.number(),
4456
+ mentionCount: z29.number(),
4356
4457
  /** Out-of count for the same denominator (snapshots that had answer text). */
4357
- totalCount: z28.number(),
4458
+ totalCount: z29.number(),
4358
4459
  /** 'High' | 'Moderate' | 'Low' | 'None' — mention frequency tier (mirrors CompetitorRow.pressureLabel). */
4359
- pressureLabel: z28.enum(["High", "Moderate", "Low", "None"]),
4460
+ pressureLabel: z29.enum(["High", "Moderate", "Low", "None"]),
4360
4461
  /** Distinct queries on which this competitor was mentioned. */
4361
- mentionedQueries: z28.array(z28.string()),
4462
+ mentionedQueries: z29.array(z29.string()),
4362
4463
  /**
4363
4464
  * Mention share 0..100. Numerator = this competitor's `mentionCount`.
4364
4465
  * Denominator = sum of `mentionCount` across all competitors plus the
@@ -4366,135 +4467,135 @@ var mentionRowSchema = z28.object({
4366
4467
  * mention. Per-competitor split of the same head-to-head measure the
4367
4468
  * project's hero `MentionShareDto` gauge headlines.
4368
4469
  */
4369
- sharePct: z28.number()
4470
+ sharePct: z29.number()
4370
4471
  });
4371
- var mentionLandscapeSchema = z28.object({
4472
+ var mentionLandscapeSchema = z29.object({
4372
4473
  /** Project's own mention count (for the bar chart comparing project vs competitors). */
4373
- projectMentionCount: z28.number(),
4474
+ projectMentionCount: z29.number(),
4374
4475
  /** Snapshots considered — those with non-empty answerText. Drives the totalCount denominator. */
4375
- totalAnswerSnapshots: z28.number(),
4376
- competitors: z28.array(mentionRowSchema)
4476
+ totalAnswerSnapshots: z29.number(),
4477
+ competitors: z29.array(mentionRowSchema)
4377
4478
  });
4378
- var aiSourceCategoryBucketSchema = z28.object({
4479
+ var aiSourceCategoryBucketSchema = z29.object({
4379
4480
  /** Category slug from packages/contracts/src/source-categories. */
4380
- category: z28.string(),
4481
+ category: z29.string(),
4381
4482
  /** Display label. */
4382
- label: z28.string(),
4483
+ label: z29.string(),
4383
4484
  /** Number of citations falling in this category. */
4384
- count: z28.number(),
4485
+ count: z29.number(),
4385
4486
  /** 0..100 share of total citations. */
4386
- sharePct: z28.number()
4487
+ sharePct: z29.number()
4387
4488
  });
4388
- var aiSourceOriginSchema = z28.object({
4389
- categories: z28.array(aiSourceCategoryBucketSchema),
4489
+ var aiSourceOriginSchema = z29.object({
4490
+ categories: z29.array(aiSourceCategoryBucketSchema),
4390
4491
  /** Top 20 source domains by citation count (excluding the project's own domain). */
4391
- topDomains: z28.array(z28.object({
4392
- domain: z28.string(),
4393
- count: z28.number(),
4492
+ topDomains: z29.array(z29.object({
4493
+ domain: z29.string(),
4494
+ count: z29.number(),
4394
4495
  /** True when the domain is one of the project's tracked competitors. */
4395
- isCompetitor: z28.boolean()
4496
+ isCompetitor: z29.boolean()
4396
4497
  }))
4397
4498
  });
4398
- var gscQueryRowSchema = z28.object({
4399
- query: z28.string(),
4400
- clicks: z28.number(),
4401
- impressions: z28.number(),
4402
- ctr: z28.number(),
4403
- avgPosition: z28.number(),
4499
+ var gscQueryRowSchema = z29.object({
4500
+ query: z29.string(),
4501
+ clicks: z29.number(),
4502
+ impressions: z29.number(),
4503
+ ctr: z29.number(),
4504
+ avgPosition: z29.number(),
4404
4505
  /** Heuristic categorization: 'brand' | 'lead-gen' | 'industry' | 'other'. */
4405
- category: z28.enum(["brand", "lead-gen", "industry", "other"])
4406
- });
4407
- var gscSectionSchema = z28.object({
4408
- periodStart: z28.string(),
4409
- periodEnd: z28.string(),
4410
- totalClicks: z28.number(),
4411
- totalImpressions: z28.number(),
4412
- ctr: z28.number(),
4413
- avgPosition: z28.number(),
4414
- topQueries: z28.array(gscQueryRowSchema),
4415
- categoryBreakdown: z28.array(z28.object({
4416
- category: z28.enum(["brand", "lead-gen", "industry", "other"]),
4417
- clicks: z28.number(),
4418
- impressions: z28.number(),
4419
- sharePct: z28.number()
4506
+ category: z29.enum(["brand", "lead-gen", "industry", "other"])
4507
+ });
4508
+ var gscSectionSchema = z29.object({
4509
+ periodStart: z29.string(),
4510
+ periodEnd: z29.string(),
4511
+ totalClicks: z29.number(),
4512
+ totalImpressions: z29.number(),
4513
+ ctr: z29.number(),
4514
+ avgPosition: z29.number(),
4515
+ topQueries: z29.array(gscQueryRowSchema),
4516
+ categoryBreakdown: z29.array(z29.object({
4517
+ category: z29.enum(["brand", "lead-gen", "industry", "other"]),
4518
+ clicks: z29.number(),
4519
+ impressions: z29.number(),
4520
+ sharePct: z29.number()
4420
4521
  })),
4421
- trend: z28.array(z28.object({ date: z28.string(), clicks: z28.number(), impressions: z28.number() })),
4522
+ trend: z29.array(z29.object({ date: z29.string(), clicks: z29.number(), impressions: z29.number() })),
4422
4523
  /**
4423
4524
  * Tracked AEO queries that have no GSC impressions in the report window.
4424
4525
  * Surfaces queries that may not represent real search demand.
4425
4526
  */
4426
- trackedButNoGsc: z28.array(z28.string()),
4527
+ trackedButNoGsc: z29.array(z29.string()),
4427
4528
  /**
4428
4529
  * GSC top queries (sorted by impressions desc) that are not tracked as
4429
4530
  * AEO queries — the candidate set for adding to the AEO project.
4430
4531
  */
4431
- gscButNotTracked: z28.array(z28.string())
4432
- });
4433
- var gaTrafficSectionSchema = z28.object({
4434
- totalSessions: z28.number(),
4435
- totalUsers: z28.number(),
4436
- totalOrganicSessions: z28.number(),
4437
- periodStart: z28.string(),
4438
- periodEnd: z28.string(),
4439
- topLandingPages: z28.array(z28.object({
4440
- page: z28.string(),
4441
- sessions: z28.number(),
4442
- users: z28.number(),
4443
- organicSessions: z28.number()
4532
+ gscButNotTracked: z29.array(z29.string())
4533
+ });
4534
+ var gaTrafficSectionSchema = z29.object({
4535
+ totalSessions: z29.number(),
4536
+ totalUsers: z29.number(),
4537
+ totalOrganicSessions: z29.number(),
4538
+ periodStart: z29.string(),
4539
+ periodEnd: z29.string(),
4540
+ topLandingPages: z29.array(z29.object({
4541
+ page: z29.string(),
4542
+ sessions: z29.number(),
4543
+ users: z29.number(),
4544
+ organicSessions: z29.number()
4444
4545
  })),
4445
- channelBreakdown: z28.array(z28.object({
4446
- channel: z28.string(),
4447
- sessions: z28.number(),
4448
- sharePct: z28.number()
4546
+ channelBreakdown: z29.array(z29.object({
4547
+ channel: z29.string(),
4548
+ sessions: z29.number(),
4549
+ sharePct: z29.number()
4449
4550
  }))
4450
4551
  });
4451
- var socialReferralSectionSchema = z28.object({
4452
- totalSessions: z28.number(),
4453
- organicSessions: z28.number(),
4454
- paidSessions: z28.number(),
4455
- channels: z28.array(z28.object({
4456
- channelGroup: z28.string(),
4457
- sessions: z28.number(),
4458
- sharePct: z28.number()
4552
+ var socialReferralSectionSchema = z29.object({
4553
+ totalSessions: z29.number(),
4554
+ organicSessions: z29.number(),
4555
+ paidSessions: z29.number(),
4556
+ channels: z29.array(z29.object({
4557
+ channelGroup: z29.string(),
4558
+ sessions: z29.number(),
4559
+ sharePct: z29.number()
4459
4560
  })),
4460
- topCampaigns: z28.array(z28.object({
4461
- source: z28.string(),
4462
- medium: z28.string(),
4463
- sessions: z28.number()
4561
+ topCampaigns: z29.array(z29.object({
4562
+ source: z29.string(),
4563
+ medium: z29.string(),
4564
+ sessions: z29.number()
4464
4565
  }))
4465
4566
  });
4466
- var aiReferralSectionSchema = z28.object({
4467
- totalSessions: z28.number(),
4468
- totalUsers: z28.number(),
4469
- paidSessions: z28.number(),
4470
- paidUsers: z28.number(),
4471
- organicSessions: z28.number(),
4472
- organicUsers: z28.number(),
4473
- bySource: z28.array(z28.object({
4474
- source: z28.string(),
4475
- sessions: z28.number(),
4476
- users: z28.number(),
4477
- paidSessions: z28.number(),
4478
- organicSessions: z28.number(),
4479
- sharePct: z28.number()
4567
+ var aiReferralSectionSchema = z29.object({
4568
+ totalSessions: z29.number(),
4569
+ totalUsers: z29.number(),
4570
+ paidSessions: z29.number(),
4571
+ paidUsers: z29.number(),
4572
+ organicSessions: z29.number(),
4573
+ organicUsers: z29.number(),
4574
+ bySource: z29.array(z29.object({
4575
+ source: z29.string(),
4576
+ sessions: z29.number(),
4577
+ users: z29.number(),
4578
+ paidSessions: z29.number(),
4579
+ organicSessions: z29.number(),
4580
+ sharePct: z29.number()
4480
4581
  })),
4481
- trend: z28.array(z28.object({ date: z28.string(), sessions: z28.number() })),
4482
- topLandingPages: z28.array(z28.object({
4483
- page: z28.string(),
4484
- sessions: z28.number(),
4485
- users: z28.number()
4582
+ trend: z29.array(z29.object({ date: z29.string(), sessions: z29.number() })),
4583
+ topLandingPages: z29.array(z29.object({
4584
+ page: z29.string(),
4585
+ sessions: z29.number(),
4586
+ users: z29.number()
4486
4587
  }))
4487
4588
  });
4488
- var serverActivitySectionSchema = z28.object({
4589
+ var serverActivitySectionSchema = z29.object({
4489
4590
  /** ISO8601 inclusive lower bound of the report window (default: 7 days). */
4490
- windowStart: z28.string(),
4591
+ windowStart: z29.string(),
4491
4592
  /** ISO8601 inclusive upper bound. */
4492
- windowEnd: z28.string(),
4493
- hasData: z28.boolean(),
4593
+ windowEnd: z29.string(),
4594
+ hasData: z29.boolean(),
4494
4595
  /** Last-7d total verified crawler hits, with prior 7d for delta. */
4495
- verifiedCrawlerHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
4596
+ verifiedCrawlerHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4496
4597
  /** Last-7d total unverified crawler hits, separated from verified trust metrics. */
4497
- unverifiedCrawlerHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
4598
+ unverifiedCrawlerHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4498
4599
  /**
4499
4600
  * Last-7d on-demand per-user fetches from AI surfaces (ChatGPT-User,
4500
4601
  * Perplexity-User, MistralAI-User). Disjoint from `verifiedCrawlerHits` /
@@ -4503,19 +4604,34 @@ var serverActivitySectionSchema = z28.object({
4503
4604
  * because the operational question for user-fetch is "is this happening?"
4504
4605
  * not "is this a confirmed bot identity?"
4505
4606
  */
4506
- aiUserFetchHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
4507
- /** Last-7d AI-referral sessions (sessionized from server-side request evidence). */
4508
- referralArrivals: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
4607
+ aiUserFetchHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4608
+ /** Last-7d AI-referral sessions (sessionized from server-side request evidence). Paid + organic + unclassified. */
4609
+ referralArrivals: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4610
+ /**
4611
+ * `referralArrivals` split by traffic class. The three buckets sum to it.
4612
+ *
4613
+ * `unclassified` counts sessions ingested before the classifier shipped: the
4614
+ * UTM tags that carry paid-ness were never persisted, so those sessions can
4615
+ * never be resolved. Reporting them as organic would overstate earned AI
4616
+ * traffic by exactly a client's ad volume.
4617
+ */
4618
+ referralArrivalsByClass: z29.object({
4619
+ paid: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4620
+ organic: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
4621
+ unclassified: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() })
4622
+ }),
4623
+ /** Pre-rendered one-line breakdown, e.g. "Paid 1,200 · Organic 24". Empty when there is nothing to split. */
4624
+ referralArrivalsClassSummary: z29.string(),
4509
4625
  /** Per-AI-operator breakdown (OpenAI, Anthropic, Google AI, Perplexity, …). */
4510
- byOperator: z28.array(z28.object({
4511
- operator: z28.string(),
4512
- verifiedHits: z28.number(),
4626
+ byOperator: z29.array(z29.object({
4627
+ operator: z29.string(),
4628
+ verifiedHits: z29.number(),
4513
4629
  /** Shown to agency audience only: claimed-bot UA, source IP not in a published range. */
4514
- unverifiedHits: z28.number(),
4630
+ unverifiedHits: z29.number(),
4515
4631
  /** Per-user fetches from this operator's AI surface (ChatGPT-User, …). */
4516
- userFetchHits: z28.number(),
4517
- referralArrivals: z28.number(),
4518
- deltaPct: z28.number().nullable()
4632
+ userFetchHits: z29.number(),
4633
+ referralArrivals: z29.number(),
4634
+ deltaPct: z29.number().nullable()
4519
4635
  })),
4520
4636
  /**
4521
4637
  * Top crawled paths (verified only, last-7d). Path-level citation cross-reference
@@ -4525,88 +4641,88 @@ var serverActivitySectionSchema = z28.object({
4525
4641
  * citation evidence can extend this entry with a `citationState` field without
4526
4642
  * breaking the contract.
4527
4643
  */
4528
- topCrawledPaths: z28.array(z28.object({
4529
- path: z28.string(),
4530
- verifiedHits: z28.number(),
4644
+ topCrawledPaths: z29.array(z29.object({
4645
+ path: z29.string(),
4646
+ verifiedHits: z29.number(),
4531
4647
  /** How many distinct AI operators crawled this path in the window. */
4532
- distinctOperators: z28.number()
4648
+ distinctOperators: z29.number()
4533
4649
  })),
4534
4650
  /** AI products that sent ≥1 session in the window (referral by destination). */
4535
- referralProducts: z28.array(z28.object({
4536
- product: z28.string(),
4537
- arrivals: z28.number(),
4538
- distinctLandingPaths: z28.number()
4651
+ referralProducts: z29.array(z29.object({
4652
+ product: z29.string(),
4653
+ arrivals: z29.number(),
4654
+ distinctLandingPaths: z29.number()
4539
4655
  })),
4540
4656
  /** Daily trend, last 14d for sparkline / chart rendering. */
4541
- dailyTrend: z28.array(z28.object({
4542
- date: z28.string(),
4543
- verifiedCrawlerHits: z28.number(),
4544
- userFetchHits: z28.number(),
4545
- referralArrivals: z28.number()
4657
+ dailyTrend: z29.array(z29.object({
4658
+ date: z29.string(),
4659
+ verifiedCrawlerHits: z29.number(),
4660
+ userFetchHits: z29.number(),
4661
+ referralArrivals: z29.number()
4546
4662
  })),
4547
4663
  /**
4548
4664
  * Top landing paths for AI-referral sessions (last-7d).
4549
4665
  * Complements `topCrawledPaths` (what bots fetch) with what humans actually land on.
4550
4666
  */
4551
- topReferralLandingPaths: z28.array(z28.object({
4552
- path: z28.string(),
4553
- arrivals: z28.number(),
4554
- distinctProducts: z28.number()
4667
+ topReferralLandingPaths: z29.array(z29.object({
4668
+ path: z29.string(),
4669
+ arrivals: z29.number(),
4670
+ distinctProducts: z29.number()
4555
4671
  }))
4556
4672
  });
4557
- var indexingHealthSectionSchema = z28.object({
4673
+ var indexingHealthSectionSchema = z29.object({
4558
4674
  /** Source: 'google' | 'bing' | null when neither is connected. */
4559
- provider: z28.enum(["google", "bing"]).nullable(),
4560
- total: z28.number(),
4561
- indexed: z28.number(),
4562
- notIndexed: z28.number(),
4675
+ provider: z29.enum(["google", "bing"]).nullable(),
4676
+ total: z29.number(),
4677
+ indexed: z29.number(),
4678
+ notIndexed: z29.number(),
4563
4679
  /** Google-only — pages explicitly marked as deindexed. Bing reports 'unknown' instead. */
4564
- deindexed: z28.number(),
4680
+ deindexed: z29.number(),
4565
4681
  /** Bing-only — pages with no inspection data yet. */
4566
- unknown: z28.number(),
4682
+ unknown: z29.number(),
4567
4683
  /** 0..100. */
4568
- indexedPct: z28.number()
4684
+ indexedPct: z29.number()
4569
4685
  });
4570
- var citationsTrendPointSchema = z28.object({
4686
+ var citationsTrendPointSchema = z29.object({
4571
4687
  /** Run ID — anchor for cross-section linking. */
4572
- runId: z28.string(),
4688
+ runId: z29.string(),
4573
4689
  /** ISO timestamp when the run finished (or createdAt fallback). */
4574
- date: z28.string(),
4690
+ date: z29.string(),
4575
4691
  /**
4576
4692
  * 0..100 — same per-query unique-cited definition as
4577
4693
  * `ReportExecutiveSummary.citationRate`. Stable across runs with different
4578
4694
  * provider counts so the trend line measures real movement rather than
4579
4695
  * provider-count variance.
4580
4696
  */
4581
- citationRate: z28.number(),
4697
+ citationRate: z29.number(),
4582
4698
  /** Numerator of `citationRate` for this run. */
4583
- citedQueryCount: z28.number(),
4699
+ citedQueryCount: z29.number(),
4584
4700
  /** Denominator of `citationRate` for this run. */
4585
- totalQueryCount: z28.number(),
4701
+ totalQueryCount: z29.number(),
4586
4702
  /** 0..100 — same per-query unique-mentioned definition as `ReportExecutiveSummary.mentionRate`. */
4587
- mentionRate: z28.number(),
4703
+ mentionRate: z29.number(),
4588
4704
  /** Numerator of `mentionRate` for this run. */
4589
- mentionedQueryCount: z28.number(),
4705
+ mentionedQueryCount: z29.number(),
4590
4706
  /**
4591
4707
  * Per-provider rates for the same run. Each provider's rate is per-pair
4592
4708
  * within that provider (`cited / scanned`), so it remains comparable
4593
4709
  * between providers in the same run.
4594
4710
  */
4595
- providerRates: z28.array(z28.object({
4596
- provider: z28.string(),
4597
- citationRate: z28.number(),
4598
- mentionRate: z28.number()
4711
+ providerRates: z29.array(z29.object({
4712
+ provider: z29.string(),
4713
+ citationRate: z29.number(),
4714
+ mentionRate: z29.number()
4599
4715
  }))
4600
4716
  });
4601
- var reportInsightSchema = z28.object({
4602
- id: z28.string(),
4603
- type: z28.enum(["regression", "gain", "opportunity"]),
4604
- severity: z28.enum(["critical", "high", "medium", "low"]),
4605
- title: z28.string(),
4606
- query: z28.string(),
4607
- provider: z28.string(),
4608
- recommendation: z28.string().nullable(),
4609
- createdAt: z28.string(),
4717
+ var reportInsightSchema = z29.object({
4718
+ id: z29.string(),
4719
+ type: z29.enum(["regression", "gain", "opportunity"]),
4720
+ severity: z29.enum(["critical", "high", "medium", "low"]),
4721
+ title: z29.string(),
4722
+ query: z29.string(),
4723
+ provider: z29.string(),
4724
+ recommendation: z29.string().nullable(),
4725
+ createdAt: z29.string(),
4610
4726
  /**
4611
4727
  * How many times this insight fired across recent runs for the same
4612
4728
  * `(query, provider, type)` tuple. Always ≥ 1. Insights returned by the
@@ -4614,64 +4730,64 @@ var reportInsightSchema = z28.object({
4614
4730
  * surfacing the multiplicity. Use it directly instead of grouping again
4615
4731
  * client-side — counts derived from raw insight rows will overcount.
4616
4732
  */
4617
- instanceCount: z28.number()
4733
+ instanceCount: z29.number()
4618
4734
  });
4619
- var recommendedNextStepSchema = z28.object({
4735
+ var recommendedNextStepSchema = z29.object({
4620
4736
  /** 'immediate' | 'short-term' | 'medium-term' — bucketed by severity heuristic. */
4621
- horizon: z28.enum(["immediate", "short-term", "medium-term"]),
4622
- title: z28.string(),
4623
- rationale: z28.string()
4737
+ horizon: z29.enum(["immediate", "short-term", "medium-term"]),
4738
+ title: z29.string(),
4739
+ rationale: z29.string()
4624
4740
  });
4625
- var reportRateDeltaSchema = z28.object({
4741
+ var reportRateDeltaSchema = z29.object({
4626
4742
  /** Current value (0..100 for rates, raw count otherwise). When `window`
4627
4743
  * is present this is the average over the last `window` checks. */
4628
- current: z28.number(),
4744
+ current: z29.number(),
4629
4745
  /** Prior value compared against. When `window` is present this is the
4630
4746
  * average over the prior `window` checks before that. */
4631
- prior: z28.number(),
4747
+ prior: z29.number(),
4632
4748
  /** Absolute delta (current − prior). Negative = decrease. */
4633
- deltaAbs: z28.number(),
4749
+ deltaAbs: z29.number(),
4634
4750
  /**
4635
4751
  * Signed percent change vs `prior`, rounded to a whole number. Null when
4636
4752
  * `prior <= 0` (percentage undefined). Renderers route count/traffic tiles
4637
4753
  * through the "smart %" rule — percentage when the prior base is large
4638
4754
  * enough (`MIN_PCT_BASE`), otherwise a rounded raw delta.
4639
4755
  */
4640
- deltaPct: z28.number().nullable(),
4756
+ deltaPct: z29.number().nullable(),
4641
4757
  /**
4642
4758
  * Direction tag for tone mapping. Threshold is metric-specific (3pp for
4643
4759
  * rates, 0.5 for counts) so small noise lands as 'flat' rather than
4644
4760
  * flipping up/down each run.
4645
4761
  */
4646
- direction: z28.enum(["up", "down", "flat"]),
4762
+ direction: z29.enum(["up", "down", "flat"]),
4647
4763
  /**
4648
4764
  * How many points went into each side of the average. Omitted (or 1)
4649
4765
  * means point-to-point (legacy "since last check"). Higher values mean
4650
4766
  * a rolling-average comparison — renderers should label it as
4651
4767
  * "vs prior N checks" when this is ≥ 2.
4652
4768
  */
4653
- window: z28.number().optional()
4769
+ window: z29.number().optional()
4654
4770
  });
4655
- var reportProviderMovementSchema = z28.object({
4656
- provider: z28.string(),
4657
- current: z28.number(),
4658
- prior: z28.number(),
4659
- deltaAbs: z28.number(),
4660
- direction: z28.enum(["up", "down", "flat"])
4771
+ var reportProviderMovementSchema = z29.object({
4772
+ provider: z29.string(),
4773
+ current: z29.number(),
4774
+ prior: z29.number(),
4775
+ deltaAbs: z29.number(),
4776
+ direction: z29.enum(["up", "down", "flat"])
4661
4777
  });
4662
- var whatsChangedSectionSchema = z28.object({
4778
+ var whatsChangedSectionSchema = z29.object({
4663
4779
  /**
4664
4780
  * False when there's no prior run (or fewer than the trend baseline),
4665
4781
  * meaning all per-metric deltas will be null. Renderers use this to swap
4666
4782
  * in a "establishing baseline" fallback rather than rendering empty
4667
4783
  * delta tiles.
4668
4784
  */
4669
- enoughHistory: z28.boolean(),
4785
+ enoughHistory: z29.boolean(),
4670
4786
  /**
4671
4787
  * One-sentence narrative summary suitable as a section subtitle.
4672
4788
  * Always present — even on baseline, narrates whatever signal exists.
4673
4789
  */
4674
- headline: z28.string(),
4790
+ headline: z29.string(),
4675
4791
  /** Citation rate delta vs the prior completed run. Null when no prior run. */
4676
4792
  citationRate: reportRateDeltaSchema.nullable(),
4677
4793
  /** Mention rate delta vs the prior completed run. Null when no prior run. */
@@ -4699,30 +4815,30 @@ var whatsChangedSectionSchema = z28.object({
4699
4815
  * deltas "vs prior {comparisonWindowDays} days" off this single value so the
4700
4816
  * SPA and HTML stay verbatim-identical.
4701
4817
  */
4702
- comparisonWindowDays: z28.number().int().positive(),
4818
+ comparisonWindowDays: z29.number().int().positive(),
4703
4819
  /**
4704
4820
  * Per-provider citation rate movements (latest run vs prior run). Empty
4705
4821
  * when no prior run. Sorted by |deltaAbs| desc — providers with the
4706
4822
  * biggest swing first.
4707
4823
  */
4708
- providerMovements: z28.array(reportProviderMovementSchema),
4824
+ providerMovements: z29.array(reportProviderMovementSchema),
4709
4825
  /**
4710
4826
  * Top wins this period — gains surfaced by the intelligence engine.
4711
4827
  * Capped at 5; sourced from `insights` filtered to `type: 'gain'`.
4712
4828
  */
4713
- wins: z28.array(reportInsightSchema),
4829
+ wins: z29.array(reportInsightSchema),
4714
4830
  /**
4715
4831
  * Top regressions this period — citations or mentions lost. Capped at 5;
4716
4832
  * sourced from `insights` filtered to `type: 'regression'`.
4717
4833
  */
4718
- regressions: z28.array(reportInsightSchema)
4719
- });
4720
- var reportAudienceSchema = z28.enum(["agency", "client"]);
4721
- var reportActionAudienceSchema = z28.enum(["agency", "client", "both"]);
4722
- var reportActionHorizonSchema = z28.enum(["immediate", "short-term", "medium-term"]);
4723
- var reportActionConfidenceSchema = z28.enum(["high", "medium", "low"]);
4724
- var reportToneSchema = z28.enum(["positive", "caution", "negative", "neutral"]);
4725
- var reportActionCategorySchema = z28.enum([
4834
+ regressions: z29.array(reportInsightSchema)
4835
+ });
4836
+ var reportAudienceSchema = z29.enum(["agency", "client"]);
4837
+ var reportActionAudienceSchema = z29.enum(["agency", "client", "both"]);
4838
+ var reportActionHorizonSchema = z29.enum(["immediate", "short-term", "medium-term"]);
4839
+ var reportActionConfidenceSchema = z29.enum(["high", "medium", "low"]);
4840
+ var reportToneSchema = z29.enum(["positive", "caution", "negative", "neutral"]);
4841
+ var reportActionCategorySchema = z29.enum([
4726
4842
  "content",
4727
4843
  "competitors",
4728
4844
  "provider",
@@ -4731,23 +4847,23 @@ var reportActionCategorySchema = z28.enum([
4731
4847
  "location",
4732
4848
  "monitoring"
4733
4849
  ]);
4734
- var reportActionPlanItemSchema = z28.object({
4850
+ var reportActionPlanItemSchema = z29.object({
4735
4851
  /** Which report audience should see this action. `both` renders in both modes. */
4736
4852
  audience: reportActionAudienceSchema,
4737
4853
  /** Stable sort priority. Lower numbers render earlier. */
4738
- priority: z28.number(),
4854
+ priority: z29.number(),
4739
4855
  /** When this should be tackled. */
4740
4856
  horizon: reportActionHorizonSchema,
4741
4857
  category: reportActionCategorySchema,
4742
- title: z28.string(),
4858
+ title: z29.string(),
4743
4859
  /** Direct next step written as an operator/client-friendly imperative. */
4744
- action: z28.string(),
4860
+ action: z29.string(),
4745
4861
  /** Why this matters. Keep each entry concise and evidence-backed. */
4746
- why: z28.array(z28.string()),
4862
+ why: z29.array(z29.string()),
4747
4863
  /** Specific observations that justify the action. */
4748
- evidence: z28.array(z28.string()),
4864
+ evidence: z29.array(z29.string()),
4749
4865
  /** What should move if the action worked. */
4750
- successMetric: z28.string(),
4866
+ successMetric: z29.string(),
4751
4867
  /** Confidence in the recommendation based on the available evidence. */
4752
4868
  confidence: reportActionConfidenceSchema,
4753
4869
  /**
@@ -4759,23 +4875,23 @@ var reportActionPlanItemSchema = z28.object({
4759
4875
  * load. Actions sourced from other signals (competitor gaps, indexing
4760
4876
  * issues, etc.) omit this and use their own dismiss flows.
4761
4877
  */
4762
- targetRef: z28.string().optional()
4878
+ targetRef: z29.string().optional()
4763
4879
  });
4764
- var reportClientSummarySchema = z28.object({
4765
- headline: z28.string(),
4766
- overview: z28.string(),
4767
- actionItems: z28.array(reportActionPlanItemSchema),
4768
- confidenceNotes: z28.array(z28.string())
4880
+ var reportClientSummarySchema = z29.object({
4881
+ headline: z29.string(),
4882
+ overview: z29.string(),
4883
+ actionItems: z29.array(reportActionPlanItemSchema),
4884
+ confidenceNotes: z29.array(z29.string())
4769
4885
  });
4770
- var reportAgencyDiagnosticSchema = z28.object({
4771
- title: z28.string(),
4772
- detail: z28.string(),
4773
- severity: z28.enum(["positive", "caution", "negative", "neutral"]),
4774
- evidence: z28.array(z28.string())
4886
+ var reportAgencyDiagnosticSchema = z29.object({
4887
+ title: z29.string(),
4888
+ detail: z29.string(),
4889
+ severity: z29.enum(["positive", "caution", "negative", "neutral"]),
4890
+ evidence: z29.array(z29.string())
4775
4891
  });
4776
- var reportAgencyDiagnosticsSchema = z28.object({
4777
- priorities: z28.array(reportActionPlanItemSchema),
4778
- diagnostics: z28.array(reportAgencyDiagnosticSchema)
4892
+ var reportAgencyDiagnosticsSchema = z29.object({
4893
+ priorities: z29.array(reportActionPlanItemSchema),
4894
+ diagnostics: z29.array(reportAgencyDiagnosticSchema)
4779
4895
  });
4780
4896
  function reportActionTone(action) {
4781
4897
  if (action.horizon === "immediate") return "negative";
@@ -4833,7 +4949,7 @@ function reportConfidenceLabel(confidence) {
4833
4949
  return "Low";
4834
4950
  }
4835
4951
  }
4836
- var projectReportDtoSchema = z28.object({
4952
+ var projectReportDtoSchema = z29.object({
4837
4953
  meta: reportMetaSchema,
4838
4954
  executiveSummary: reportExecutiveSummarySchema,
4839
4955
  citationScorecard: citationScorecardSchema,
@@ -4847,16 +4963,16 @@ var projectReportDtoSchema = z28.object({
4847
4963
  /** Server-side log-evidence visibility (crawls + click-through sessions). Null when no traffic source connected. */
4848
4964
  serverActivity: serverActivitySectionSchema.nullable(),
4849
4965
  indexingHealth: indexingHealthSectionSchema.nullable(),
4850
- citationsTrend: z28.array(citationsTrendPointSchema),
4966
+ citationsTrend: z29.array(citationsTrendPointSchema),
4851
4967
  /**
4852
4968
  * Trend-focused "what's changed" summary for the report's act 2. Always
4853
4969
  * present; renderers gate empty/baseline states via `enoughHistory`.
4854
4970
  */
4855
4971
  whatsChanged: whatsChangedSectionSchema,
4856
- insights: z28.array(reportInsightSchema),
4857
- recommendedNextSteps: z28.array(recommendedNextStepSchema),
4972
+ insights: z29.array(reportInsightSchema),
4973
+ recommendedNextSteps: z29.array(recommendedNextStepSchema),
4858
4974
  /** Canonical structured actions shared by the client and agency render modes. */
4859
- actionPlan: z28.array(reportActionPlanItemSchema),
4975
+ actionPlan: z29.array(reportActionPlanItemSchema),
4860
4976
  /** Polished client-facing summary and action shortlist. */
4861
4977
  clientSummary: reportClientSummarySchema,
4862
4978
  /** Technical, evidence-oriented operator diagnostics for agency mode. */
@@ -4866,17 +4982,17 @@ var projectReportDtoSchema = z28.object({
4866
4982
  * intelligence layer (`buildContentTargetRows`). Empty when no run has
4867
4983
  * produced candidate queries with demand or competitor signal.
4868
4984
  */
4869
- contentOpportunities: z28.array(contentTargetRowDtoSchema),
4985
+ contentOpportunities: z29.array(contentTargetRowDtoSchema),
4870
4986
  /**
4871
4987
  * Queries where competitors were cited but the project was not. Sourced
4872
4988
  * from `buildContentGapRows`. Empty until the first answer-visibility run.
4873
4989
  */
4874
- contentGaps: z28.array(contentGapRowDtoSchema),
4990
+ contentGaps: z29.array(contentGapRowDtoSchema),
4875
4991
  /**
4876
4992
  * Per-query grounding source map (own + competitor cited URLs). Sourced
4877
4993
  * from `buildContentSourceRows`. Empty until the first answer-visibility run.
4878
4994
  */
4879
- groundingSources: z28.array(contentSourceRowDtoSchema)
4995
+ groundingSources: z29.array(contentSourceRowDtoSchema)
4880
4996
  });
4881
4997
 
4882
4998
  // ../contracts/src/report-dedup.ts
@@ -4947,10 +5063,10 @@ function dedupeReportOpportunities(report) {
4947
5063
  }
4948
5064
 
4949
5065
  // ../contracts/src/skills.ts
4950
- import { z as z29 } from "zod";
4951
- var codingAgentSchema = z29.enum(["claude", "codex"]);
5066
+ import { z as z30 } from "zod";
5067
+ var codingAgentSchema = z30.enum(["claude", "codex"]);
4952
5068
  var CodingAgents = codingAgentSchema.enum;
4953
- var skillsClientSchema = z29.enum(["claude", "codex", "all"]);
5069
+ var skillsClientSchema = z30.enum(["claude", "codex", "all"]);
4954
5070
  var SkillsClients = skillsClientSchema.enum;
4955
5071
  var SKILL_MANIFEST_FILENAME = ".canonry-skill-manifest.json";
4956
5072
  function classifySkillFile(params) {
@@ -4968,22 +5084,22 @@ function coerceSkillManifest(parsed) {
4968
5084
  }
4969
5085
 
4970
5086
  // ../contracts/src/traffic.ts
4971
- import { z as z30 } from "zod";
4972
- var trafficCrawlerSegmentsSchema = z30.object({
4973
- content: z30.number().int().nonnegative(),
4974
- sitemap: z30.number().int().nonnegative(),
4975
- robots: z30.number().int().nonnegative(),
4976
- asset: z30.number().int().nonnegative(),
4977
- other: z30.number().int().nonnegative()
4978
- });
4979
- var trafficPathClassSchema = z30.enum([
5087
+ import { z as z31 } from "zod";
5088
+ var trafficCrawlerSegmentsSchema = z31.object({
5089
+ content: z31.number().int().nonnegative(),
5090
+ sitemap: z31.number().int().nonnegative(),
5091
+ robots: z31.number().int().nonnegative(),
5092
+ asset: z31.number().int().nonnegative(),
5093
+ other: z31.number().int().nonnegative()
5094
+ });
5095
+ var trafficPathClassSchema = z31.enum([
4980
5096
  "content",
4981
5097
  "sitemap",
4982
5098
  "robots",
4983
5099
  "asset",
4984
5100
  "other"
4985
5101
  ]);
4986
- var trafficSourceTypeSchema = z30.enum([
5102
+ var trafficSourceTypeSchema = z31.enum([
4987
5103
  "cloud-run",
4988
5104
  "wordpress",
4989
5105
  "cloudflare",
@@ -4991,7 +5107,7 @@ var trafficSourceTypeSchema = z30.enum([
4991
5107
  "generic-log"
4992
5108
  ]);
4993
5109
  var TrafficSourceTypes = trafficSourceTypeSchema.enum;
4994
- var trafficAdapterCapabilitySchema = z30.enum([
5110
+ var trafficAdapterCapabilitySchema = z31.enum([
4995
5111
  "raw-request-events",
4996
5112
  "aggregate-request-metrics",
4997
5113
  "request-url",
@@ -5002,234 +5118,252 @@ var trafficAdapterCapabilitySchema = z30.enum([
5002
5118
  "cursor-pull"
5003
5119
  ]);
5004
5120
  var TrafficAdapterCapabilities = trafficAdapterCapabilitySchema.enum;
5005
- var trafficEvidenceKindSchema = z30.enum(["raw-request", "aggregate-bucket"]);
5121
+ var trafficEvidenceKindSchema = z31.enum(["raw-request", "aggregate-bucket"]);
5006
5122
  var TrafficEvidenceKinds = trafficEvidenceKindSchema.enum;
5007
- var trafficEventConfidenceSchema = z30.enum(["observed", "provider-aggregated", "inferred"]);
5123
+ var trafficEventConfidenceSchema = z31.enum(["observed", "provider-aggregated", "inferred"]);
5008
5124
  var TrafficEventConfidences = trafficEventConfidenceSchema.enum;
5009
- var trafficProviderResourceSchema = z30.object({
5010
- type: z30.string().nullable(),
5011
- labels: z30.record(z30.string(), z30.string())
5125
+ var trafficProviderResourceSchema = z31.object({
5126
+ type: z31.string().nullable(),
5127
+ labels: z31.record(z31.string(), z31.string())
5012
5128
  });
5013
- var normalizedTrafficRequestSchema = z30.object({
5129
+ var normalizedTrafficRequestSchema = z31.object({
5014
5130
  sourceType: trafficSourceTypeSchema,
5015
- evidenceKind: z30.literal(TrafficEvidenceKinds["raw-request"]),
5016
- confidence: z30.literal(TrafficEventConfidences.observed),
5017
- eventId: z30.string().min(1),
5018
- observedAt: z30.string().min(1),
5019
- method: z30.string().nullable(),
5020
- requestUrl: z30.string().nullable(),
5021
- host: z30.string().nullable(),
5022
- path: z30.string().min(1),
5023
- queryString: z30.string().nullable(),
5024
- status: z30.number().int().nullable(),
5025
- userAgent: z30.string().nullable(),
5026
- remoteIp: z30.string().nullable(),
5027
- referer: z30.string().nullable(),
5028
- latencyMs: z30.number().nullable(),
5029
- requestSizeBytes: z30.number().int().nullable(),
5030
- responseSizeBytes: z30.number().int().nullable(),
5131
+ evidenceKind: z31.literal(TrafficEvidenceKinds["raw-request"]),
5132
+ confidence: z31.literal(TrafficEventConfidences.observed),
5133
+ eventId: z31.string().min(1),
5134
+ observedAt: z31.string().min(1),
5135
+ method: z31.string().nullable(),
5136
+ requestUrl: z31.string().nullable(),
5137
+ host: z31.string().nullable(),
5138
+ path: z31.string().min(1),
5139
+ queryString: z31.string().nullable(),
5140
+ status: z31.number().int().nullable(),
5141
+ userAgent: z31.string().nullable(),
5142
+ remoteIp: z31.string().nullable(),
5143
+ referer: z31.string().nullable(),
5144
+ latencyMs: z31.number().nullable(),
5145
+ requestSizeBytes: z31.number().int().nullable(),
5146
+ responseSizeBytes: z31.number().int().nullable(),
5031
5147
  providerResource: trafficProviderResourceSchema,
5032
- providerLabels: z30.record(z30.string(), z30.string())
5148
+ providerLabels: z31.record(z31.string(), z31.string())
5033
5149
  });
5034
- var normalizedTrafficPullPageSchema = z30.object({
5035
- events: z30.array(normalizedTrafficRequestSchema),
5036
- rawEntryCount: z30.number().int().nonnegative(),
5037
- skippedEntryCount: z30.number().int().nonnegative(),
5038
- nextPageToken: z30.string().optional(),
5039
- filter: z30.string()
5150
+ var normalizedTrafficPullPageSchema = z31.object({
5151
+ events: z31.array(normalizedTrafficRequestSchema),
5152
+ rawEntryCount: z31.number().int().nonnegative(),
5153
+ skippedEntryCount: z31.number().int().nonnegative(),
5154
+ nextPageToken: z31.string().optional(),
5155
+ filter: z31.string()
5040
5156
  });
5041
- var trafficSourceStatusSchema = z30.enum(["connected", "paused", "error", "archived"]);
5157
+ var trafficSourceStatusSchema = z31.enum(["connected", "paused", "error", "archived"]);
5042
5158
  var TrafficSourceStatuses = trafficSourceStatusSchema.enum;
5043
- var trafficSourceAuthModeSchema = z30.enum(["oauth", "service-account"]);
5159
+ var trafficSourceAuthModeSchema = z31.enum(["oauth", "service-account"]);
5044
5160
  var TrafficSourceAuthModes = trafficSourceAuthModeSchema.enum;
5045
- var verificationStatusSchema = z30.enum(["verified", "claimed_unverified", "unknown_ai_like"]);
5161
+ var verificationStatusSchema = z31.enum(["verified", "claimed_unverified", "unknown_ai_like"]);
5046
5162
  var VerificationStatuses = verificationStatusSchema.enum;
5047
- var cloudRunSourceConfigSchema = z30.object({
5048
- gcpProjectId: z30.string().min(1),
5049
- serviceName: z30.string().nullable().optional(),
5050
- location: z30.string().nullable().optional(),
5163
+ var cloudRunSourceConfigSchema = z31.object({
5164
+ gcpProjectId: z31.string().min(1),
5165
+ serviceName: z31.string().nullable().optional(),
5166
+ location: z31.string().nullable().optional(),
5051
5167
  authMode: trafficSourceAuthModeSchema
5052
5168
  });
5053
- var wordpressTrafficSourceConfigSchema = z30.object({
5054
- baseUrl: z30.string().url(),
5055
- username: z30.string().min(1)
5169
+ var wordpressTrafficSourceConfigSchema = z31.object({
5170
+ baseUrl: z31.string().url(),
5171
+ username: z31.string().min(1)
5056
5172
  });
5057
- var vercelTrafficEnvironmentSchema = z30.enum(["production", "preview"]);
5173
+ var vercelTrafficEnvironmentSchema = z31.enum(["production", "preview"]);
5058
5174
  var VercelTrafficEnvironments = vercelTrafficEnvironmentSchema.enum;
5059
- var vercelTrafficSourceConfigSchema = z30.object({
5175
+ var vercelTrafficSourceConfigSchema = z31.object({
5060
5176
  /** Vercel project id (e.g. `prj_...`). */
5061
- projectId: z30.string().min(1),
5177
+ projectId: z31.string().min(1),
5062
5178
  /** Vercel team or account id: the org that owns the project. */
5063
- teamId: z30.string().min(1),
5179
+ teamId: z31.string().min(1),
5064
5180
  environment: vercelTrafficEnvironmentSchema
5065
5181
  });
5066
- var trafficSourceDtoSchema = z30.object({
5067
- id: z30.string(),
5068
- projectId: z30.string(),
5182
+ var trafficSourceDtoSchema = z31.object({
5183
+ id: z31.string(),
5184
+ projectId: z31.string(),
5069
5185
  sourceType: trafficSourceTypeSchema,
5070
- displayName: z30.string(),
5186
+ displayName: z31.string(),
5071
5187
  status: trafficSourceStatusSchema,
5072
- lastSyncedAt: z30.string().nullable(),
5073
- lastCursor: z30.string().nullable(),
5074
- lastError: z30.string().nullable(),
5075
- archivedAt: z30.string().nullable(),
5076
- config: z30.record(z30.string(), z30.unknown()),
5077
- createdAt: z30.string(),
5078
- updatedAt: z30.string()
5079
- });
5080
- var trafficConnectCloudRunRequestSchema = z30.object({
5081
- gcpProjectId: z30.string().min(1),
5082
- serviceName: z30.string().min(1).optional(),
5083
- location: z30.string().min(1).optional(),
5084
- displayName: z30.string().min(1).optional(),
5188
+ lastSyncedAt: z31.string().nullable(),
5189
+ lastCursor: z31.string().nullable(),
5190
+ lastError: z31.string().nullable(),
5191
+ archivedAt: z31.string().nullable(),
5192
+ config: z31.record(z31.string(), z31.unknown()),
5193
+ createdAt: z31.string(),
5194
+ updatedAt: z31.string()
5195
+ });
5196
+ var trafficConnectCloudRunRequestSchema = z31.object({
5197
+ gcpProjectId: z31.string().min(1),
5198
+ serviceName: z31.string().min(1).optional(),
5199
+ location: z31.string().min(1).optional(),
5200
+ displayName: z31.string().min(1).optional(),
5085
5201
  /** Service-account JSON content (string). When omitted, defaults to OAuth via `canonry google connect <project> --type ga4` flow. */
5086
- keyJson: z30.string().optional()
5202
+ keyJson: z31.string().optional()
5087
5203
  });
5088
- var trafficConnectWordpressRequestSchema = z30.object({
5089
- baseUrl: z30.string().url(),
5090
- username: z30.string().min(1),
5204
+ var trafficConnectWordpressRequestSchema = z31.object({
5205
+ baseUrl: z31.string().url(),
5206
+ username: z31.string().min(1),
5091
5207
  /** WordPress Application Password (the same auth used by the content client). */
5092
- applicationPassword: z30.string().min(1),
5093
- displayName: z30.string().min(1).optional()
5208
+ applicationPassword: z31.string().min(1),
5209
+ displayName: z31.string().min(1).optional()
5094
5210
  });
5095
- var trafficConnectVercelRequestSchema = z30.object({
5211
+ var trafficConnectVercelRequestSchema = z31.object({
5096
5212
  /** Vercel project id (e.g. `prj_...`) — from the Vercel dashboard or `.vercel/project.json`. */
5097
- projectId: z30.string().min(1),
5213
+ projectId: z31.string().min(1),
5098
5214
  /** Vercel team or account id: the org that owns the project ("orgId" in .vercel/project.json). */
5099
- teamId: z30.string().min(1),
5215
+ teamId: z31.string().min(1),
5100
5216
  /** Vercel personal access token. Stored in `~/.canonry/config.yaml`, never the DB. */
5101
- token: z30.string().min(1),
5217
+ token: z31.string().min(1),
5102
5218
  /** Which deployment environment's request logs to pull. Default: `production`. */
5103
5219
  environment: vercelTrafficEnvironmentSchema.optional(),
5104
- displayName: z30.string().min(1).optional()
5220
+ displayName: z31.string().min(1).optional()
5105
5221
  });
5106
- var trafficSyncResponseSchema = z30.object({
5107
- sourceId: z30.string(),
5108
- runId: z30.string(),
5109
- syncedAt: z30.string(),
5110
- pulledEvents: z30.number().int().nonnegative(),
5222
+ var trafficSyncResponseSchema = z31.object({
5223
+ sourceId: z31.string(),
5224
+ runId: z31.string(),
5225
+ syncedAt: z31.string(),
5226
+ pulledEvents: z31.number().int().nonnegative(),
5111
5227
  /** Self-traffic events (Canonry's own tooling) dropped before rollup. */
5112
- selfTrafficExcluded: z30.number().int().nonnegative(),
5113
- crawlerHits: z30.number().int().nonnegative(),
5114
- aiUserFetchHits: z30.number().int().nonnegative(),
5115
- aiReferralHits: z30.number().int().nonnegative(),
5116
- unknownHits: z30.number().int().nonnegative(),
5117
- crawlerBucketRows: z30.number().int().nonnegative(),
5118
- aiUserFetchBucketRows: z30.number().int().nonnegative(),
5119
- aiReferralBucketRows: z30.number().int().nonnegative(),
5120
- sampleRows: z30.number().int().nonnegative(),
5121
- windowStart: z30.string(),
5122
- windowEnd: z30.string()
5123
- });
5124
- var trafficBackfillRequestSchema = z30.object({
5228
+ selfTrafficExcluded: z31.number().int().nonnegative(),
5229
+ crawlerHits: z31.number().int().nonnegative(),
5230
+ aiUserFetchHits: z31.number().int().nonnegative(),
5231
+ aiReferralHits: z31.number().int().nonnegative(),
5232
+ unknownHits: z31.number().int().nonnegative(),
5233
+ crawlerBucketRows: z31.number().int().nonnegative(),
5234
+ aiUserFetchBucketRows: z31.number().int().nonnegative(),
5235
+ aiReferralBucketRows: z31.number().int().nonnegative(),
5236
+ sampleRows: z31.number().int().nonnegative(),
5237
+ windowStart: z31.string(),
5238
+ windowEnd: z31.string()
5239
+ });
5240
+ var trafficBackfillRequestSchema = z31.object({
5125
5241
  /** Lookback window in days. Capped server-side at the upstream log retention ceiling (Cloud Logging _Default = 30d). Default: 30. */
5126
- days: z30.number().int().positive().optional()
5242
+ days: z31.number().int().positive().optional()
5127
5243
  });
5128
- var trafficResetRequestSchema = z30.object({
5129
- advanceToNow: z30.literal(true)
5244
+ var trafficResetRequestSchema = z31.object({
5245
+ advanceToNow: z31.literal(true)
5130
5246
  });
5131
- var trafficBackfillResponseSchema = z30.object({
5132
- sourceId: z30.string(),
5133
- runId: z30.string(),
5247
+ var trafficBackfillResponseSchema = z31.object({
5248
+ sourceId: z31.string(),
5249
+ runId: z31.string(),
5134
5250
  status: runStatusSchema,
5135
- windowStart: z30.string(),
5136
- windowEnd: z30.string(),
5251
+ windowStart: z31.string(),
5252
+ windowEnd: z31.string(),
5137
5253
  /** Days actually used after server-side clamping (≤ requested). */
5138
- daysRequested: z30.number().int().positive(),
5139
- daysApplied: z30.number().int().positive()
5254
+ daysRequested: z31.number().int().positive(),
5255
+ daysApplied: z31.number().int().positive()
5140
5256
  });
5141
- var trafficSourceTotalsSchema = z30.object({
5257
+ var trafficSourceTotalsSchema = z31.object({
5142
5258
  /**
5143
5259
  * Total classified-crawler hits in the window. UNCHANGED contract — still the
5144
5260
  * full count across every path class. Use `crawlerContentHits` for the
5145
5261
  * "content was actually crawled" signal.
5146
5262
  */
5147
- crawlerHits: z30.number().int().nonnegative(),
5263
+ crawlerHits: z31.number().int().nonnegative(),
5148
5264
  /** Crawler hits against content/document paths only (= `crawlerSegments.content`). */
5149
- crawlerContentHits: z30.number().int().nonnegative(),
5265
+ crawlerContentHits: z31.number().int().nonnegative(),
5150
5266
  /** Infrastructure crawler hits — sitemap + robots + asset fetches (`crawlerSegments.{sitemap,robots,asset}`). */
5151
- crawlerInfraHits: z30.number().int().nonnegative(),
5267
+ crawlerInfraHits: z31.number().int().nonnegative(),
5152
5268
  /** Full per-class crawler-hit breakdown; the five buckets sum to `crawlerHits`. */
5153
5269
  crawlerSegments: trafficCrawlerSegmentsSchema,
5154
- aiUserFetchHits: z30.number().int().nonnegative(),
5155
- aiReferralHits: z30.number().int().nonnegative(),
5156
- sampleCount: z30.number().int().nonnegative()
5270
+ aiUserFetchHits: z31.number().int().nonnegative(),
5271
+ aiReferralHits: z31.number().int().nonnegative(),
5272
+ sampleCount: z31.number().int().nonnegative()
5157
5273
  });
5158
- var trafficSourceListResponseSchema = z30.object({
5159
- sources: z30.array(trafficSourceDtoSchema)
5274
+ var trafficSourceListResponseSchema = z31.object({
5275
+ sources: z31.array(trafficSourceDtoSchema)
5160
5276
  });
5161
5277
  var trafficSourceDetailDtoSchema = trafficSourceDtoSchema.extend({
5162
5278
  totals24h: trafficSourceTotalsSchema,
5163
- latestRun: z30.object({
5164
- runId: z30.string(),
5279
+ latestRun: z31.object({
5280
+ runId: z31.string(),
5165
5281
  status: runStatusSchema,
5166
- startedAt: z30.string().nullable(),
5167
- finishedAt: z30.string().nullable(),
5168
- error: z30.string().nullable()
5282
+ startedAt: z31.string().nullable(),
5283
+ finishedAt: z31.string().nullable(),
5284
+ error: z31.string().nullable()
5169
5285
  }).nullable()
5170
5286
  });
5171
- var trafficStatusResponseSchema = z30.object({
5172
- sources: z30.array(trafficSourceDetailDtoSchema)
5287
+ var trafficStatusResponseSchema = z31.object({
5288
+ sources: z31.array(trafficSourceDetailDtoSchema)
5173
5289
  });
5174
- var trafficEventKindSchema = z30.enum(["crawler", "ai-user-fetch", "ai-referral"]);
5290
+ var trafficEventKindSchema = z31.enum(["crawler", "ai-user-fetch", "ai-referral"]);
5175
5291
  var TrafficEventKinds = trafficEventKindSchema.enum;
5176
- var trafficCrawlerEventEntrySchema = z30.object({
5177
- kind: z30.literal(TrafficEventKinds.crawler),
5178
- sourceId: z30.string(),
5179
- tsHour: z30.string(),
5180
- botId: z30.string(),
5181
- operator: z30.string(),
5182
- verificationStatus: z30.string(),
5183
- pathNormalized: z30.string(),
5292
+ var trafficCrawlerEventEntrySchema = z31.object({
5293
+ kind: z31.literal(TrafficEventKinds.crawler),
5294
+ sourceId: z31.string(),
5295
+ tsHour: z31.string(),
5296
+ botId: z31.string(),
5297
+ operator: z31.string(),
5298
+ verificationStatus: z31.string(),
5299
+ pathNormalized: z31.string(),
5184
5300
  /** Coarse class of the fetched path — lets the UI split content crawls from sitemap/robots/asset polling. */
5185
5301
  pathClass: trafficPathClassSchema,
5186
- status: z30.number().int(),
5187
- hits: z30.number().int().nonnegative()
5188
- });
5189
- var trafficAiUserFetchEventEntrySchema = z30.object({
5190
- kind: z30.literal(TrafficEventKinds["ai-user-fetch"]),
5191
- sourceId: z30.string(),
5192
- tsHour: z30.string(),
5193
- botId: z30.string(),
5194
- operator: z30.string(),
5195
- verificationStatus: z30.string(),
5196
- pathNormalized: z30.string(),
5197
- status: z30.number().int(),
5198
- hits: z30.number().int().nonnegative()
5199
- });
5200
- var trafficAiReferralEventEntrySchema = z30.object({
5201
- kind: z30.literal(TrafficEventKinds["ai-referral"]),
5202
- sourceId: z30.string(),
5203
- tsHour: z30.string(),
5204
- product: z30.string(),
5205
- operator: z30.string(),
5206
- sourceDomain: z30.string(),
5207
- evidenceType: z30.string(),
5208
- landingPathNormalized: z30.string(),
5209
- status: z30.number().int(),
5210
- hits: z30.number().int().nonnegative()
5211
- });
5212
- var trafficEventEntrySchema = z30.discriminatedUnion("kind", [
5302
+ status: z31.number().int(),
5303
+ hits: z31.number().int().nonnegative()
5304
+ });
5305
+ var trafficAiUserFetchEventEntrySchema = z31.object({
5306
+ kind: z31.literal(TrafficEventKinds["ai-user-fetch"]),
5307
+ sourceId: z31.string(),
5308
+ tsHour: z31.string(),
5309
+ botId: z31.string(),
5310
+ operator: z31.string(),
5311
+ verificationStatus: z31.string(),
5312
+ pathNormalized: z31.string(),
5313
+ status: z31.number().int(),
5314
+ hits: z31.number().int().nonnegative()
5315
+ });
5316
+ var trafficAiReferralEventEntrySchema = z31.object({
5317
+ kind: z31.literal(TrafficEventKinds["ai-referral"]),
5318
+ sourceId: z31.string(),
5319
+ tsHour: z31.string(),
5320
+ product: z31.string(),
5321
+ operator: z31.string(),
5322
+ sourceDomain: z31.string(),
5323
+ evidenceType: z31.string(),
5324
+ landingPathNormalized: z31.string(),
5325
+ status: z31.number().int(),
5326
+ /** Total AI-referral sessions in the bucket. `paidHits + organicHits + unknownHits === hits`. */
5327
+ hits: z31.number().int().nonnegative(),
5328
+ /** Sessions carrying paid-attribution UTM evidence (`utm_medium=cpc`, …). */
5329
+ paidHits: z31.number().int().nonnegative(),
5330
+ /** Sessions with no paid-attribution evidence. Not proof the click was unpaid — an untagged ad click is indistinguishable. */
5331
+ organicHits: z31.number().int().nonnegative(),
5332
+ /**
5333
+ * Sessions ingested before the classifier shipped. Their UTM tags were never
5334
+ * persisted, so they can never be resolved to paid or organic. Never fold
5335
+ * these into `organicHits`.
5336
+ */
5337
+ unknownHits: z31.number().int().nonnegative()
5338
+ });
5339
+ var trafficEventEntrySchema = z31.discriminatedUnion("kind", [
5213
5340
  trafficCrawlerEventEntrySchema,
5214
5341
  trafficAiUserFetchEventEntrySchema,
5215
5342
  trafficAiReferralEventEntrySchema
5216
5343
  ]);
5217
- var trafficEventsResponseSchema = z30.object({
5218
- windowStart: z30.string(),
5219
- windowEnd: z30.string(),
5220
- totals: z30.object({
5344
+ var trafficEventsResponseSchema = z31.object({
5345
+ windowStart: z31.string(),
5346
+ windowEnd: z31.string(),
5347
+ totals: z31.object({
5221
5348
  /** Total classified-crawler hits across the window. UNCHANGED contract. */
5222
- crawlerHits: z30.number().int().nonnegative(),
5349
+ crawlerHits: z31.number().int().nonnegative(),
5223
5350
  /** Crawler hits against content/document paths only (= `crawlerSegments.content`). */
5224
- crawlerContentHits: z30.number().int().nonnegative(),
5351
+ crawlerContentHits: z31.number().int().nonnegative(),
5225
5352
  /** Infrastructure crawler hits — sitemap + robots + asset fetches. */
5226
- crawlerInfraHits: z30.number().int().nonnegative(),
5353
+ crawlerInfraHits: z31.number().int().nonnegative(),
5227
5354
  /** Full per-class crawler-hit breakdown; the five buckets sum to `crawlerHits`. */
5228
5355
  crawlerSegments: trafficCrawlerSegmentsSchema,
5229
- aiUserFetchHits: z30.number().int().nonnegative(),
5230
- aiReferralHits: z30.number().int().nonnegative()
5356
+ aiUserFetchHits: z31.number().int().nonnegative(),
5357
+ /** Total AI-referral sessions. The three class buckets below sum to it. */
5358
+ aiReferralHits: z31.number().int().nonnegative(),
5359
+ /** AI-referral sessions carrying paid-attribution UTM evidence. */
5360
+ aiReferralPaidHits: z31.number().int().nonnegative(),
5361
+ /** AI-referral sessions with no paid-attribution evidence. */
5362
+ aiReferralOrganicHits: z31.number().int().nonnegative(),
5363
+ /** AI-referral sessions ingested before the classifier shipped; unresolvable, never organic. */
5364
+ aiReferralUnknownHits: z31.number().int().nonnegative()
5231
5365
  }),
5232
- events: z30.array(trafficEventEntrySchema)
5366
+ events: z31.array(trafficEventEntrySchema)
5233
5367
  });
5234
5368
 
5235
5369
  // ../contracts/src/traffic-path.ts
@@ -5367,199 +5501,115 @@ function sumInfraHits(segments) {
5367
5501
  return segments.sitemap + segments.robots + segments.asset;
5368
5502
  }
5369
5503
 
5370
- // ../contracts/src/formatting.ts
5371
- function formatRatio(value) {
5372
- if (!Number.isFinite(value) || value === 0) return "0%";
5373
- return `${(value * 100).toFixed(1)}%`;
5374
- }
5375
- function formatNumber(value) {
5376
- if (!Number.isFinite(value)) return "\u2014";
5377
- if (Math.abs(value) >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
5378
- if (Math.abs(value) >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
5379
- return value.toLocaleString("en-US");
5380
- }
5381
- function formatDate(iso) {
5382
- if (!iso) return "\u2014";
5383
- try {
5384
- const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
5385
- const options = { month: "short", day: "numeric", year: "numeric" };
5386
- const d = dateOnly && dateOnly[1] && dateOnly[2] && dateOnly[3] ? new Date(Date.UTC(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))) : new Date(iso);
5387
- if (Number.isNaN(d.getTime())) return iso;
5388
- return d.toLocaleDateString("en-US", dateOnly ? { ...options, timeZone: "UTC" } : options);
5389
- } catch {
5390
- return iso;
5391
- }
5392
- }
5393
- function formatIsoDate(iso) {
5394
- if (!iso) return "\u2014";
5395
- try {
5396
- const d = new Date(iso);
5397
- if (Number.isNaN(d.getTime())) return iso;
5398
- const yyyy = d.getUTCFullYear();
5399
- const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
5400
- const dd = String(d.getUTCDate()).padStart(2, "0");
5401
- return `${yyyy}-${mm}-${dd}`;
5402
- } catch {
5403
- return iso;
5404
- }
5405
- }
5406
- function formatDateRange(start, end) {
5407
- if (!start && !end) return "";
5408
- if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
5409
- return formatDate(start || end);
5410
- }
5411
- var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
5412
- function parseInclusiveEndMs(iso) {
5413
- const ms = Date.parse(iso);
5414
- if (Number.isNaN(ms)) return null;
5415
- return DATE_ONLY_PATTERN.test(iso) ? ms + 864e5 - 1 : ms;
5416
- }
5417
- function deltaPercent(current, prior) {
5418
- if (prior <= 0) return null;
5419
- return Math.round((current - prior) / prior * 100);
5420
- }
5421
- function deltaTone(deltaPct) {
5422
- if (deltaPct === null || deltaPct === 0) return "neutral";
5423
- return deltaPct > 0 ? "positive" : "negative";
5424
- }
5425
- function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
5426
- if (d.deltaPct === null) {
5427
- return d.prior === 0 ? "First baseline week" : "";
5428
- }
5429
- if (d.deltaPct > 0) return `Up ${d.deltaPct}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
5430
- if (d.deltaPct < 0) return `Down ${Math.abs(d.deltaPct)}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
5431
- return `Flat ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
5432
- }
5433
- var MIN_PCT_BASE = 30;
5434
- function round1(value) {
5435
- return Math.round(value * 10) / 10;
5436
- }
5437
- function formatAverageDelta(d) {
5438
- if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
5439
- const sign2 = d.deltaPct > 0 ? "+" : "";
5440
- return `${sign2}${d.deltaPct}% vs prior`;
5441
- }
5442
- const sign = d.deltaAbs > 0 ? "+" : "";
5443
- return `${sign}${round1(d.deltaAbs)} vs ${round1(d.prior)}`;
5444
- }
5445
- function formatWindowCountDelta(d, countLabel, windowLabel) {
5446
- if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
5447
- const sign2 = d.deltaPct > 0 ? "+" : "";
5448
- return `${sign2}${d.deltaPct}% ${windowLabel}`;
5449
- }
5450
- const sign = d.deltaAbs > 0 ? "+" : "";
5451
- return `${sign}${formatNumber(Math.round(d.deltaAbs))} ${countLabel} ${windowLabel}`;
5452
- }
5453
-
5454
5504
  // ../contracts/src/ads.ts
5455
- import { z as z31 } from "zod";
5456
- var adsConnectRequestSchema = z31.object({
5505
+ import { z as z32 } from "zod";
5506
+ var adsConnectRequestSchema = z32.object({
5457
5507
  /** Ads Manager "SDK key" scoped to one ad account. Stored in config.yaml, never the DB. */
5458
- apiKey: z31.string().min(1)
5459
- });
5460
- var adsConnectionStatusDtoSchema = z31.object({
5461
- connected: z31.boolean(),
5462
- adAccountId: z31.string().nullable().optional(),
5463
- displayName: z31.string().nullable().optional(),
5464
- currencyCode: z31.string().nullable().optional(),
5465
- timezone: z31.string().nullable().optional(),
5466
- status: z31.string().nullable().optional(),
5467
- lastSyncedAt: z31.string().nullable().optional(),
5508
+ apiKey: z32.string().min(1)
5509
+ });
5510
+ var adsConnectionStatusDtoSchema = z32.object({
5511
+ connected: z32.boolean(),
5512
+ adAccountId: z32.string().nullable().optional(),
5513
+ displayName: z32.string().nullable().optional(),
5514
+ currencyCode: z32.string().nullable().optional(),
5515
+ timezone: z32.string().nullable().optional(),
5516
+ status: z32.string().nullable().optional(),
5517
+ lastSyncedAt: z32.string().nullable().optional(),
5468
5518
  /** Whether the ad account has OpenAI conversion tracking (pixel or CAPI) configured,
5469
5519
  * detected from synced campaigns carrying conversion_event_setting_ids. Optional:
5470
5520
  * only present when connected. */
5471
- conversionTrackingConfigured: z31.boolean().optional()
5472
- });
5473
- var adsDisconnectResponseSchema = z31.object({
5474
- disconnected: z31.boolean()
5475
- });
5476
- var adsSyncResponseSchema = z31.object({
5477
- runId: z31.string(),
5478
- status: z31.string()
5479
- });
5480
- var adsCreativeDtoSchema = z31.object({
5481
- type: z31.string().nullable().optional(),
5482
- title: z31.string().nullable().optional(),
5483
- body: z31.string().nullable().optional(),
5484
- targetUrl: z31.string().nullable().optional()
5485
- });
5486
- var adsAdDtoSchema = z31.object({
5487
- id: z31.string(),
5488
- adGroupId: z31.string(),
5489
- name: z31.string(),
5490
- status: z31.string(),
5491
- reviewStatus: z31.string().nullable().optional(),
5521
+ conversionTrackingConfigured: z32.boolean().optional()
5522
+ });
5523
+ var adsDisconnectResponseSchema = z32.object({
5524
+ disconnected: z32.boolean()
5525
+ });
5526
+ var adsSyncResponseSchema = z32.object({
5527
+ runId: z32.string(),
5528
+ status: z32.string()
5529
+ });
5530
+ var adsCreativeDtoSchema = z32.object({
5531
+ type: z32.string().nullable().optional(),
5532
+ title: z32.string().nullable().optional(),
5533
+ body: z32.string().nullable().optional(),
5534
+ targetUrl: z32.string().nullable().optional()
5535
+ });
5536
+ var adsAdDtoSchema = z32.object({
5537
+ id: z32.string(),
5538
+ adGroupId: z32.string(),
5539
+ name: z32.string(),
5540
+ status: z32.string(),
5541
+ reviewStatus: z32.string().nullable().optional(),
5492
5542
  creative: adsCreativeDtoSchema.nullable().optional()
5493
5543
  });
5494
- var adsAdGroupDtoSchema = z31.object({
5495
- id: z31.string(),
5496
- campaignId: z31.string(),
5497
- name: z31.string(),
5498
- status: z31.string(),
5499
- billingEventType: z31.string().nullable().optional(),
5500
- maxBidMicros: z31.number().int().nullable().optional(),
5544
+ var adsAdGroupDtoSchema = z32.object({
5545
+ id: z32.string(),
5546
+ campaignId: z32.string(),
5547
+ name: z32.string(),
5548
+ status: z32.string(),
5549
+ billingEventType: z32.string().nullable().optional(),
5550
+ maxBidMicros: z32.number().int().nullable().optional(),
5501
5551
  /**
5502
5552
  * The targeting primitive: entries are multi-line strings of
5503
5553
  * newline-separated example queries (the live Ads Manager format).
5504
5554
  */
5505
- contextHints: z31.array(z31.string()).default([]),
5506
- ads: z31.array(adsAdDtoSchema).default([])
5507
- });
5508
- var adsCampaignDtoSchema = z31.object({
5509
- id: z31.string(),
5510
- name: z31.string(),
5511
- status: z31.string(),
5512
- biddingType: z31.string().nullable().optional(),
5513
- dailySpendLimitMicros: z31.number().int().nullable().optional(),
5514
- lifetimeSpendLimitMicros: z31.number().int().nullable().optional(),
5515
- adGroups: z31.array(adsAdGroupDtoSchema).default([])
5516
- });
5517
- var adsCampaignListResponseSchema = z31.object({
5518
- campaigns: z31.array(adsCampaignDtoSchema)
5519
- });
5520
- var adsInsightLevelSchema = z31.enum(["campaign", "ad_group"]);
5555
+ contextHints: z32.array(z32.string()).default([]),
5556
+ ads: z32.array(adsAdDtoSchema).default([])
5557
+ });
5558
+ var adsCampaignDtoSchema = z32.object({
5559
+ id: z32.string(),
5560
+ name: z32.string(),
5561
+ status: z32.string(),
5562
+ biddingType: z32.string().nullable().optional(),
5563
+ dailySpendLimitMicros: z32.number().int().nullable().optional(),
5564
+ lifetimeSpendLimitMicros: z32.number().int().nullable().optional(),
5565
+ adGroups: z32.array(adsAdGroupDtoSchema).default([])
5566
+ });
5567
+ var adsCampaignListResponseSchema = z32.object({
5568
+ campaigns: z32.array(adsCampaignDtoSchema)
5569
+ });
5570
+ var adsInsightLevelSchema = z32.enum(["campaign", "ad_group"]);
5521
5571
  var AdsInsightLevels = adsInsightLevelSchema.enum;
5522
- var adsInsightRowDtoSchema = z31.object({
5572
+ var adsInsightRowDtoSchema = z32.object({
5523
5573
  level: adsInsightLevelSchema,
5524
- entityId: z31.string(),
5525
- date: z31.string(),
5526
- impressions: z31.number().int(),
5527
- clicks: z31.number().int(),
5528
- spendMicros: z31.number().int(),
5574
+ entityId: z32.string(),
5575
+ date: z32.string(),
5576
+ impressions: z32.number().int(),
5577
+ clicks: z32.number().int(),
5578
+ spendMicros: z32.number().int(),
5529
5579
  /** Conversion count for the row. 0 when conversion tracking is not configured.
5530
5580
  * Conversion VALUE (for ROAS) is a deliberate follow-up: the upstream value
5531
5581
  * field is not yet captured against a live conversion-tracking account. */
5532
- conversions: z31.number().int(),
5582
+ conversions: z32.number().int(),
5533
5583
  /** clicks / impressions; null when impressions is 0. */
5534
- ctr: z31.number().nullable(),
5584
+ ctr: z32.number().nullable(),
5535
5585
  /** spendMicros / clicks, rounded to integer micros; null when clicks is 0. */
5536
- cpcMicros: z31.number().int().nullable()
5586
+ cpcMicros: z32.number().int().nullable()
5537
5587
  });
5538
- var adsInsightsResponseSchema = z31.object({
5539
- rows: z31.array(adsInsightRowDtoSchema),
5588
+ var adsInsightsResponseSchema = z32.object({
5589
+ rows: z32.array(adsInsightRowDtoSchema),
5540
5590
  /** Account currency for rendering spend/cpc; null before the first sync. */
5541
- currencyCode: z31.string().nullable().optional()
5542
- });
5543
- var adsTotalsDtoSchema = z31.object({
5544
- impressions: z31.number().int(),
5545
- clicks: z31.number().int(),
5546
- spendMicros: z31.number().int(),
5547
- conversions: z31.number().int(),
5548
- ctr: z31.number().nullable(),
5549
- cpcMicros: z31.number().int().nullable()
5550
- });
5551
- var adsSummaryDtoSchema = z31.object({
5552
- connected: z31.boolean(),
5553
- displayName: z31.string().nullable().optional(),
5554
- currencyCode: z31.string().nullable().optional(),
5555
- lastSyncedAt: z31.string().nullable().optional(),
5556
- campaignCount: z31.number().int(),
5557
- adGroupCount: z31.number().int(),
5558
- adCount: z31.number().int(),
5591
+ currencyCode: z32.string().nullable().optional()
5592
+ });
5593
+ var adsTotalsDtoSchema = z32.object({
5594
+ impressions: z32.number().int(),
5595
+ clicks: z32.number().int(),
5596
+ spendMicros: z32.number().int(),
5597
+ conversions: z32.number().int(),
5598
+ ctr: z32.number().nullable(),
5599
+ cpcMicros: z32.number().int().nullable()
5600
+ });
5601
+ var adsSummaryDtoSchema = z32.object({
5602
+ connected: z32.boolean(),
5603
+ displayName: z32.string().nullable().optional(),
5604
+ currencyCode: z32.string().nullable().optional(),
5605
+ lastSyncedAt: z32.string().nullable().optional(),
5606
+ campaignCount: z32.number().int(),
5607
+ adGroupCount: z32.number().int(),
5608
+ adCount: z32.number().int(),
5559
5609
  /** Date range the totals cover (oldest/newest rollup date), null when empty. */
5560
- window: z31.object({
5561
- from: z31.string().nullable(),
5562
- to: z31.string().nullable()
5610
+ window: z32.object({
5611
+ from: z32.string().nullable(),
5612
+ to: z32.string().nullable()
5563
5613
  }),
5564
5614
  /** Campaign-level rollup totals over the window (levels are not summed across). */
5565
5615
  totals: adsTotalsDtoSchema
@@ -5935,8 +5985,21 @@ export {
5935
5985
  windowCutoff,
5936
5986
  visibilityStatsDtoSchema,
5937
5987
  calendarMonthBounds,
5938
- GA4AiReferralTrafficClasses,
5939
- classifyGa4AiReferralTrafficClass,
5988
+ formatRatio,
5989
+ formatNumber,
5990
+ formatDate,
5991
+ formatIsoDate,
5992
+ formatDateRange,
5993
+ parseInclusiveEndMs,
5994
+ deltaPercent,
5995
+ deltaTone,
5996
+ formatDeltaCopy,
5997
+ formatAverageDelta,
5998
+ formatWindowCountDelta,
5999
+ AiReferralTrafficClasses,
6000
+ classifyAiReferralTrafficClass,
6001
+ aiReferralClassCounts,
6002
+ formatAiReferralClassSummary,
5940
6003
  ga4StatusDtoSchema,
5941
6004
  ga4SyncResponseDtoSchema,
5942
6005
  ga4AiReferralHistoryEntrySchema,
@@ -6034,17 +6097,6 @@ export {
6034
6097
  classifyTrafficPath,
6035
6098
  segmentCrawlerHits,
6036
6099
  sumInfraHits,
6037
- formatRatio,
6038
- formatNumber,
6039
- formatDate,
6040
- formatIsoDate,
6041
- formatDateRange,
6042
- parseInclusiveEndMs,
6043
- deltaPercent,
6044
- deltaTone,
6045
- formatDeltaCopy,
6046
- formatAverageDelta,
6047
- formatWindowCountDelta,
6048
6100
  adsConnectRequestSchema,
6049
6101
  adsConnectionStatusDtoSchema,
6050
6102
  adsDisconnectResponseSchema,