@ainyc/canonry 4.84.0 → 4.86.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 (24) hide show
  1. package/assets/agent-workspace/skills/aero/references/regression-playbook.md +2 -0
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +22 -1
  3. package/assets/assets/{BacklinksPage-OrSg_iPA.js → BacklinksPage-BNrvc-gV.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-DPBhAT_r.js → ChartPrimitives-BlIkdUdy.js} +1 -1
  5. package/assets/assets/{ProjectPage-CpMcEmtw.js → ProjectPage-CAyx_xNr.js} +2 -2
  6. package/assets/assets/{RunRow-2Rty0BAH.js → RunRow-CAPnKzi7.js} +1 -1
  7. package/assets/assets/{RunsPage-B3ahqf8s.js → RunsPage-idnuzKBn.js} +1 -1
  8. package/assets/assets/{SettingsPage-BIjeI85q.js → SettingsPage-Bka67uJq.js} +1 -1
  9. package/assets/assets/{TrafficPage-DjGoj691.js → TrafficPage-C_o-rA5o.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-BgKG-2q3.js → TrafficSourceDetailPage-D_jvoSTV.js} +1 -1
  11. package/assets/assets/{arrow-left-Cf7wmru1.js → arrow-left-B-JfzARi.js} +1 -1
  12. package/assets/assets/{extract-error-message-CANxezte.js → extract-error-message-BhPbjIX6.js} +1 -1
  13. package/assets/assets/{index-CGlPx_cu.js → index-uPSrDA8e.js} +77 -77
  14. package/assets/assets/{trash-2-6nHJZrvy.js → trash-2-BbRvn40h.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-BNF3HXBW.js → chunk-23HGQV22.js} +1460 -1149
  17. package/dist/{chunk-VJBO4VIK.js → chunk-DLBQU3VG.js} +712 -423
  18. package/dist/{chunk-Y3O3HBMN.js → chunk-LLJPZKHG.js} +94 -1
  19. package/dist/{chunk-M3IYKTSF.js → chunk-SELXBOAP.js} +19 -4
  20. package/dist/cli.js +210 -20
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-PDIAMP5I.js → intelligence-service-ZHUJKZRO.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +10 -10
@@ -1832,6 +1832,210 @@ import { z as z18 } from "zod";
1832
1832
 
1833
1833
  // ../contracts/src/discovery.ts
1834
1834
  import { z as z17 } from "zod";
1835
+
1836
+ // ../contracts/src/embeddings.ts
1837
+ function cosineSimilarity(a, b) {
1838
+ if (a.length === 0 || b.length === 0) {
1839
+ throw new Error("cosineSimilarity: vectors must be non-empty");
1840
+ }
1841
+ if (a.length !== b.length) {
1842
+ throw new Error(`cosineSimilarity: vector length mismatch (${a.length} vs ${b.length})`);
1843
+ }
1844
+ let dot = 0;
1845
+ let magA = 0;
1846
+ let magB = 0;
1847
+ for (let i = 0; i < a.length; i++) {
1848
+ dot += a[i] * b[i];
1849
+ magA += a[i] * a[i];
1850
+ magB += b[i] * b[i];
1851
+ }
1852
+ if (magA === 0 || magB === 0) return 0;
1853
+ return dot / (Math.sqrt(magA) * Math.sqrt(magB));
1854
+ }
1855
+ function clusterByCosine(items, vectors, threshold) {
1856
+ if (threshold < 0 || threshold > 1) {
1857
+ throw new Error(`clusterByCosine: threshold must be in [0, 1], got ${threshold}`);
1858
+ }
1859
+ if (items.length !== vectors.length) {
1860
+ throw new Error(`clusterByCosine: items/vectors length mismatch (${items.length} vs ${vectors.length})`);
1861
+ }
1862
+ if (items.length === 0) return [];
1863
+ const parent = items.map((_, i) => i);
1864
+ const find = (x) => {
1865
+ let root = x;
1866
+ while (parent[root] !== root) root = parent[root];
1867
+ let cur = x;
1868
+ while (parent[cur] !== root) {
1869
+ const next = parent[cur];
1870
+ parent[cur] = root;
1871
+ cur = next;
1872
+ }
1873
+ return root;
1874
+ };
1875
+ const union = (a, b) => {
1876
+ const ra = find(a);
1877
+ const rb = find(b);
1878
+ if (ra !== rb) parent[ra] = rb;
1879
+ };
1880
+ for (let i = 0; i < items.length; i++) {
1881
+ for (let j = i + 1; j < items.length; j++) {
1882
+ if (cosineSimilarity(vectors[i], vectors[j]) >= threshold) {
1883
+ union(i, j);
1884
+ }
1885
+ }
1886
+ }
1887
+ const byRoot = /* @__PURE__ */ new Map();
1888
+ for (let i = 0; i < items.length; i++) {
1889
+ const root = find(i);
1890
+ const existing = byRoot.get(root);
1891
+ if (existing) existing.push(i);
1892
+ else byRoot.set(root, [i]);
1893
+ }
1894
+ return Array.from(byRoot.values()).map((indices) => indices.map((idx) => items[idx]));
1895
+ }
1896
+ function pickClusterRepresentative(cluster) {
1897
+ if (cluster.length === 0) throw new Error("pickClusterRepresentative: cluster is empty");
1898
+ let best = cluster[0];
1899
+ for (let i = 1; i < cluster.length; i++) {
1900
+ if (cluster[i].length < best.length) best = cluster[i];
1901
+ }
1902
+ return best;
1903
+ }
1904
+
1905
+ // ../contracts/src/url-normalize.ts
1906
+ var STRIP_KEYS = /* @__PURE__ */ new Set([
1907
+ // Click identifiers
1908
+ "fbclid",
1909
+ "gclid",
1910
+ "msclkid",
1911
+ "ttclid",
1912
+ "li_fat_id",
1913
+ "igshid",
1914
+ "yclid",
1915
+ "dclid",
1916
+ "gbraid",
1917
+ "wbraid",
1918
+ "bingid",
1919
+ // Mailchimp
1920
+ "mc_cid",
1921
+ "mc_eid",
1922
+ // Google Analytics linkers
1923
+ "_ga",
1924
+ "_gl",
1925
+ // Google Tag Manager debug
1926
+ "gtm_latency",
1927
+ "gtm_debug",
1928
+ // WordPress internal noise
1929
+ "preview",
1930
+ "preview_id",
1931
+ "preview_nonce",
1932
+ "_thumbnail_id",
1933
+ // Common cache-busters/versioning
1934
+ "v",
1935
+ "ver",
1936
+ "version"
1937
+ ]);
1938
+ function shouldStrip(key) {
1939
+ if (STRIP_KEYS.has(key)) return true;
1940
+ if (key.startsWith("utm_")) return true;
1941
+ return false;
1942
+ }
1943
+ function parseQuery(query) {
1944
+ if (query === "") return [];
1945
+ return query.split("&").map((pair) => {
1946
+ const eq = pair.indexOf("=");
1947
+ if (eq === -1) return { key: pair, value: null };
1948
+ return { key: pair.slice(0, eq), value: pair.slice(eq + 1) };
1949
+ });
1950
+ }
1951
+ function encodeQuery(pairs) {
1952
+ return pairs.map((p) => p.value === null ? p.key : `${p.key}=${p.value}`).join("&");
1953
+ }
1954
+ function collapseRootIndex(path) {
1955
+ if (path === "/index.html" || path === "/index.php") return "/";
1956
+ return path;
1957
+ }
1958
+ function dropTrailingSlash(path) {
1959
+ if (path.length > 1 && path.endsWith("/")) {
1960
+ return path.replace(/\/+$/, "");
1961
+ }
1962
+ return path;
1963
+ }
1964
+ function absolutizeProjectUrl(url, canonicalDomain) {
1965
+ if (!url) return "";
1966
+ const trimmed = url.trim();
1967
+ if (!trimmed) return "";
1968
+ if (/^https?:\/\//i.test(trimmed)) return trimmed;
1969
+ if (trimmed.startsWith("//")) return `https:${trimmed}`;
1970
+ const host = canonicalDomain.trim().replace(/^https?:\/\//i, "").replace(/\/+$/, "");
1971
+ if (!host) return trimmed;
1972
+ if (trimmed.startsWith("/")) return `https://${host}${trimmed}`;
1973
+ return `https://${host}/${trimmed}`;
1974
+ }
1975
+ function hostOf(value) {
1976
+ if (value == null) return null;
1977
+ const trimmed = value.trim();
1978
+ if (!trimmed) return null;
1979
+ try {
1980
+ const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
1981
+ return url.hostname.replace(/^www\./, "").toLowerCase();
1982
+ } catch {
1983
+ return null;
1984
+ }
1985
+ }
1986
+ function normalizeUrlPath(input) {
1987
+ if (input == null) return null;
1988
+ let trimmed = input.trim();
1989
+ if (trimmed === "") return null;
1990
+ trimmed = trimmed.replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
1991
+ if (trimmed === "" || trimmed === "/") return "/";
1992
+ if (trimmed === "(not set)") return null;
1993
+ trimmed = trimmed.replace(/([a-z0-9])[).]+$/i, "$1");
1994
+ if (trimmed.startsWith("/)") || trimmed.startsWith("/ ")) {
1995
+ trimmed = "/";
1996
+ }
1997
+ if (trimmed.includes(" ")) {
1998
+ trimmed = trimmed.split(" ")[0];
1999
+ }
2000
+ if (trimmed === "" || trimmed === "/") return "/";
2001
+ let pathPart;
2002
+ let queryPart;
2003
+ if (/^https?:\/\//i.test(trimmed)) {
2004
+ let url;
2005
+ try {
2006
+ url = new URL(trimmed);
2007
+ } catch {
2008
+ return null;
2009
+ }
2010
+ pathPart = url.pathname || "/";
2011
+ queryPart = url.search.startsWith("?") ? url.search.slice(1) : url.search;
2012
+ } else {
2013
+ let raw = trimmed;
2014
+ const hashIdx = raw.indexOf("#");
2015
+ if (hashIdx !== -1) raw = raw.slice(0, hashIdx);
2016
+ const qIdx = raw.indexOf("?");
2017
+ if (qIdx === -1) {
2018
+ pathPart = raw;
2019
+ queryPart = "";
2020
+ } else {
2021
+ pathPart = raw.slice(0, qIdx);
2022
+ queryPart = raw.slice(qIdx + 1);
2023
+ }
2024
+ }
2025
+ if (pathPart === "") pathPart = "/";
2026
+ pathPart = collapseRootIndex(pathPart);
2027
+ pathPart = dropTrailingSlash(pathPart);
2028
+ const pairs = parseQuery(queryPart).filter((p) => !shouldStrip(p.key));
2029
+ pairs.sort((a, b) => {
2030
+ if (a.key < b.key) return -1;
2031
+ if (a.key > b.key) return 1;
2032
+ return 0;
2033
+ });
2034
+ if (pairs.length === 0) return pathPart;
2035
+ return `${pathPart}?${encodeQuery(pairs)}`;
2036
+ }
2037
+
2038
+ // ../contracts/src/discovery.ts
1835
2039
  var discoveryBucketSchema = z17.enum(["cited", "aspirational", "wasted-surface"]);
1836
2040
  var DiscoveryBuckets = discoveryBucketSchema.enum;
1837
2041
  var DEFAULT_DISCOVERY_PROMOTE_BUCKETS = [
@@ -1963,6 +2167,231 @@ var queryProvenanceSchema = z17.union([
1963
2167
  z17.literal("cli"),
1964
2168
  z17.string().regex(/^discovery:.+$/)
1965
2169
  ]);
2170
+ var DISCOVERY_HARVEST_MAX_WORDS = 12;
2171
+ var DISCOVERY_HARVEST_MIN_CHARS = 3;
2172
+ var DISCOVERY_HARVEST_NOVELTY_THRESHOLD = DISCOVERY_DEFAULT_DEDUP_THRESHOLD;
2173
+ var DISCOVERY_HARVEST_MIN_ANCHOR_TERMS = 1;
2174
+ var DISCOVERY_HARVEST_PHONE_DIGITS = 7;
2175
+ var HARVEST_SIGNIFICANT_TOKEN_MIN = 4;
2176
+ var HARVEST_STOPWORDS = /* @__PURE__ */ new Set([
2177
+ "the",
2178
+ "and",
2179
+ "for",
2180
+ "with",
2181
+ "near",
2182
+ "best",
2183
+ "top",
2184
+ "your",
2185
+ "you",
2186
+ "are",
2187
+ "how",
2188
+ "what",
2189
+ "does",
2190
+ "this",
2191
+ "that",
2192
+ "from",
2193
+ "into",
2194
+ "about",
2195
+ "who",
2196
+ "why"
2197
+ ]);
2198
+ var HARVEST_NAV_MARKERS = [
2199
+ "address",
2200
+ "directions",
2201
+ "hours",
2202
+ "login",
2203
+ "log in",
2204
+ "sign in",
2205
+ "signin",
2206
+ "phone number",
2207
+ "zip code",
2208
+ "postal code",
2209
+ "email address"
2210
+ ];
2211
+ var discoveryHarvestCandidateSchema = z17.object({
2212
+ query: z17.string().min(1),
2213
+ probeHits: z17.number().int().positive()
2214
+ });
2215
+ var discoveryHarvestStatsSchema = z17.object({
2216
+ /** Distinct candidates extracted before gating. */
2217
+ rawCandidates: z17.number().int().nonnegative(),
2218
+ /** Candidates that passed every gate. */
2219
+ admitted: z17.number().int().nonnegative(),
2220
+ /** Per-reason rejection tally (each rejected candidate counted exactly once,
2221
+ * at the first gate it failed). Lexical-gate order: belowFloor → length →
2222
+ * navigational → duplicate (EXACT already-tracked) → offAnchor; then
2223
+ * `semanticDuplicate` for candidates dropped by the cosine novelty pass.
2224
+ * Invariant: `admitted + Σ(rejected) === rawCandidates`. */
2225
+ rejected: z17.object({
2226
+ belowFloor: z17.number().int().nonnegative(),
2227
+ length: z17.number().int().nonnegative(),
2228
+ navigational: z17.number().int().nonnegative(),
2229
+ /** Dropped by the cheap exact-match check against the tracked basket. */
2230
+ duplicate: z17.number().int().nonnegative(),
2231
+ offAnchor: z17.number().int().nonnegative(),
2232
+ /** Dropped by the embedding cosine novelty pass (a paraphrase / synonym /
2233
+ * stem variant of a tracked query that exact match can't see). 0 when the
2234
+ * semantic pass did not run — see `semanticNoveltyApplied`. */
2235
+ semanticDuplicate: z17.number().int().nonnegative()
2236
+ })
2237
+ });
2238
+ var discoveryHarvestDtoSchema = z17.object({
2239
+ sessionId: z17.string(),
2240
+ projectId: z17.string(),
2241
+ /** The provider whose probes were harvested (the session's seed provider).
2242
+ * Discovery is Gemini-only today; carried so a future multi-provider
2243
+ * discovery can attribute candidates. */
2244
+ provider: z17.string(),
2245
+ status: discoverySessionStatusSchema,
2246
+ /** Recurrence floor applied: a candidate must have appeared in ≥ this many
2247
+ * distinct probes to be admitted. */
2248
+ minProbeHits: z17.number().int().positive(),
2249
+ /** Whether the subject-anchor filter actually ran (requested AND the corpus
2250
+ * had ≥ `DISCOVERY_HARVEST_MIN_ANCHOR_TERMS` significant terms). */
2251
+ anchorApplied: z17.boolean(),
2252
+ /** Whether the embedding cosine novelty pass ran. False when embeddings were
2253
+ * unavailable (no Gemini key / no tracked queries / no candidates), in which
2254
+ * case novelty fell back to the cheap exact-match check only. */
2255
+ semanticNoveltyApplied: z17.boolean(),
2256
+ candidates: z17.array(discoveryHarvestCandidateSchema),
2257
+ stats: discoveryHarvestStatsSchema
2258
+ });
2259
+ function normalizeHarvestQuery(query) {
2260
+ return query.trim().replace(/\s+/g, " ").toLowerCase();
2261
+ }
2262
+ function harvestTokens(query) {
2263
+ return normalizeHarvestQuery(query).split(/[^a-z0-9]+/).filter(Boolean);
2264
+ }
2265
+ function significantHarvestTokens(query) {
2266
+ return harvestTokens(query).filter(
2267
+ (t) => t.length >= HARVEST_SIGNIFICANT_TOKEN_MIN && !HARVEST_STOPWORDS.has(t)
2268
+ );
2269
+ }
2270
+ function longestDigitRun(query) {
2271
+ let max = 0;
2272
+ let run = 0;
2273
+ for (const ch of query) {
2274
+ if (ch >= "0" && ch <= "9") {
2275
+ run++;
2276
+ if (run > max) max = run;
2277
+ } else {
2278
+ run = 0;
2279
+ }
2280
+ }
2281
+ return max;
2282
+ }
2283
+ function isNavigationalHarvestQuery(query) {
2284
+ const norm = normalizeHarvestQuery(query);
2285
+ if (longestDigitRun(norm) >= DISCOVERY_HARVEST_PHONE_DIGITS) return true;
2286
+ const tokens = new Set(harvestTokens(norm));
2287
+ for (const marker of HARVEST_NAV_MARKERS) {
2288
+ if (marker.includes(" ")) {
2289
+ const re = new RegExp(`(?<![a-z0-9])${marker.replace(/ /g, "\\s+")}(?![a-z0-9])`);
2290
+ if (re.test(norm)) return true;
2291
+ } else if (tokens.has(marker)) {
2292
+ return true;
2293
+ }
2294
+ }
2295
+ return false;
2296
+ }
2297
+ function buildHarvestAnchorTerms(corpus, domains = []) {
2298
+ const set = /* @__PURE__ */ new Set();
2299
+ for (const text of corpus) {
2300
+ for (const token of significantHarvestTokens(text)) set.add(token);
2301
+ }
2302
+ for (const domain of domains) {
2303
+ const host = hostOf(domain);
2304
+ if (!host) continue;
2305
+ const label = host.replace(/\.[a-z0-9]+$/, "");
2306
+ for (const token of significantHarvestTokens(label)) set.add(token);
2307
+ }
2308
+ return [...set];
2309
+ }
2310
+ function gateHarvestedSearchQueries(input) {
2311
+ const minProbeHits = Math.max(1, Math.floor(input.minProbeHits ?? 1));
2312
+ const anchorTermSet = new Set(input.anchorTerms ?? []);
2313
+ const applyAnchor = (input.applyAnchor ?? true) && anchorTermSet.size >= DISCOVERY_HARVEST_MIN_ANCHOR_TERMS;
2314
+ const trackedNorm = new Set(
2315
+ input.trackedQueries.map(normalizeHarvestQuery).filter(Boolean)
2316
+ );
2317
+ const stats = {
2318
+ rawCandidates: input.candidates.length,
2319
+ admitted: 0,
2320
+ rejected: { belowFloor: 0, length: 0, navigational: 0, duplicate: 0, offAnchor: 0, semanticDuplicate: 0 }
2321
+ };
2322
+ const admitted = [];
2323
+ for (const candidate of input.candidates) {
2324
+ if (candidate.probeHits < minProbeHits) {
2325
+ stats.rejected.belowFloor++;
2326
+ continue;
2327
+ }
2328
+ const norm = normalizeHarvestQuery(candidate.query);
2329
+ const words = norm ? norm.split(" ").length : 0;
2330
+ if (norm.length < DISCOVERY_HARVEST_MIN_CHARS || words > DISCOVERY_HARVEST_MAX_WORDS) {
2331
+ stats.rejected.length++;
2332
+ continue;
2333
+ }
2334
+ if (isNavigationalHarvestQuery(norm)) {
2335
+ stats.rejected.navigational++;
2336
+ continue;
2337
+ }
2338
+ if (trackedNorm.has(norm)) {
2339
+ stats.rejected.duplicate++;
2340
+ continue;
2341
+ }
2342
+ if (applyAnchor) {
2343
+ const sig = significantHarvestTokens(norm);
2344
+ if (!sig.some((t) => anchorTermSet.has(t))) {
2345
+ stats.rejected.offAnchor++;
2346
+ continue;
2347
+ }
2348
+ }
2349
+ admitted.push({ query: norm, probeHits: candidate.probeHits });
2350
+ stats.admitted++;
2351
+ }
2352
+ admitted.sort((a, b) => b.probeHits - a.probeHits || a.query.localeCompare(b.query));
2353
+ return { admitted, anchorApplied: applyAnchor, stats };
2354
+ }
2355
+ function applyHarvestSemanticNovelty(input) {
2356
+ const { result, candidateVectors, trackedVectors } = input;
2357
+ const threshold = input.threshold ?? DISCOVERY_HARVEST_NOVELTY_THRESHOLD;
2358
+ if (candidateVectors.length !== result.admitted.length || trackedVectors.length === 0) {
2359
+ return result;
2360
+ }
2361
+ const admitted = [];
2362
+ let semanticDuplicate = 0;
2363
+ for (let i = 0; i < result.admitted.length; i++) {
2364
+ const vec = candidateVectors[i];
2365
+ const isDup = trackedVectors.some((t) => cosineSimilarity(vec, t) >= threshold);
2366
+ if (isDup) semanticDuplicate++;
2367
+ else admitted.push(result.admitted[i]);
2368
+ }
2369
+ return {
2370
+ admitted,
2371
+ anchorApplied: result.anchorApplied,
2372
+ stats: {
2373
+ ...result.stats,
2374
+ admitted: admitted.length,
2375
+ rejected: { ...result.stats.rejected, semanticDuplicate }
2376
+ }
2377
+ };
2378
+ }
2379
+ function aggregateHarvestedQueries(probes) {
2380
+ const counts = /* @__PURE__ */ new Map();
2381
+ for (const probe of probes) {
2382
+ const seenInProbe = /* @__PURE__ */ new Set();
2383
+ for (const raw of probe.searchQueries) {
2384
+ if (typeof raw !== "string") continue;
2385
+ const norm = normalizeHarvestQuery(raw);
2386
+ if (!norm || seenInProbe.has(norm)) continue;
2387
+ seenInProbe.add(norm);
2388
+ const existing = counts.get(norm);
2389
+ if (existing) existing.probeHits++;
2390
+ else counts.set(norm, { query: norm, probeHits: 1 });
2391
+ }
2392
+ }
2393
+ return [...counts.values()];
2394
+ }
1966
2395
 
1967
2396
  // ../contracts/src/surface-class.ts
1968
2397
  var surfaceClassSchema = z18.enum([
@@ -2142,30 +2571,101 @@ function windowCutoff(window) {
2142
2571
  return d.toISOString();
2143
2572
  }
2144
2573
 
2145
- // ../contracts/src/ga.ts
2574
+ // ../contracts/src/visibility-stats.ts
2146
2575
  import { z as z20 } from "zod";
2147
- var ga4ConnectionDtoSchema = z20.object({
2148
- id: z20.string(),
2149
- projectId: z20.string(),
2150
- propertyId: z20.string(),
2151
- clientEmail: z20.string(),
2152
- connected: z20.boolean(),
2153
- createdAt: z20.string(),
2154
- updatedAt: z20.string()
2155
- });
2156
- var ga4TrafficSnapshotDtoSchema = z20.object({
2157
- date: z20.string(),
2158
- landingPage: z20.string(),
2159
- sessions: z20.number(),
2160
- organicSessions: z20.number(),
2161
- users: z20.number()
2162
- });
2163
- var ga4SourceDimensionSchema = z20.enum(["session", "first_user", "manual_utm"]);
2164
- var ga4AiReferralDtoSchema = z20.object({
2165
- source: z20.string(),
2166
- medium: z20.string(),
2167
- sessions: z20.number(),
2168
- users: z20.number(),
2576
+ var visibilityStatsCountsSchema = z20.object({
2577
+ /** Total (query × provider) snapshots observed in the window. Denominator for `citedRate`. */
2578
+ total: z20.number().int(),
2579
+ /**
2580
+ * Snapshots where `answerMentioned !== null` — the sample size (`n`) for the
2581
+ * mention proportion. Tri-state `null` ("not checked") is excluded.
2582
+ */
2583
+ checked: z20.number().int(),
2584
+ /** Snapshots where `answerMentioned === true` (brand named in the answer text). */
2585
+ mentioned: z20.number().int(),
2586
+ /** Snapshots where `citationState === 'cited'` (domain in the grounding / source list). */
2587
+ cited: z20.number().int(),
2588
+ /** `mentioned / checked`, rounded to 4 dp; `null` when `checked === 0` (rate undefined over no samples). */
2589
+ mentionRate: z20.number().nullable(),
2590
+ /** `cited / total`, rounded to 4 dp; `null` when `total === 0`. */
2591
+ citedRate: z20.number().nullable()
2592
+ });
2593
+ var visibilityStatsProviderEntrySchema = visibilityStatsCountsSchema.extend({
2594
+ provider: z20.string(),
2595
+ /** Earliest snapshot `createdAt` (ISO 8601) contributing to these counts. */
2596
+ firstObserved: z20.string(),
2597
+ /** Latest snapshot `createdAt` (ISO 8601) contributing to these counts. */
2598
+ lastObserved: z20.string()
2599
+ });
2600
+ var visibilityStatsQueryEntrySchema = visibilityStatsCountsSchema.extend({
2601
+ /** The tracked query's id. */
2602
+ queryId: z20.string().nullable(),
2603
+ /** The query text. */
2604
+ query: z20.string(),
2605
+ firstObserved: z20.string(),
2606
+ lastObserved: z20.string(),
2607
+ /**
2608
+ * Per-provider breakdown — present only when `groupBy=provider`. Each
2609
+ * entry's counts sum to this query's pooled counts.
2610
+ */
2611
+ providers: z20.array(visibilityStatsProviderEntrySchema).optional()
2612
+ });
2613
+ var visibilityStatsWindowSchema = z20.object({
2614
+ /** Inclusive lower bound on `runs.createdAt` (ISO 8601; a date-only value is that UTC day's start), or `null` for unbounded. Echoes the raw value the caller passed. */
2615
+ since: z20.string().nullable(),
2616
+ /** Inclusive upper bound on `runs.createdAt` (ISO 8601; a date-only value covers the whole UTC day), or `null` for unbounded. Echoes the raw value the caller passed. */
2617
+ until: z20.string().nullable(),
2618
+ /** When set, the aggregation used the most recent N answer-visibility runs instead of a date window. */
2619
+ lastRuns: z20.number().int().nullable(),
2620
+ /** Number of (completed / partial, non-probe) answer-visibility runs included. */
2621
+ runCount: z20.number().int()
2622
+ });
2623
+ var visibilityStatsGroupBySchema = z20.enum(["provider"]);
2624
+ var visibilityStatsDtoSchema = z20.object({
2625
+ project: z20.string(),
2626
+ /**
2627
+ * `'provider'` when a per-provider breakdown was requested; OMITTED otherwise
2628
+ * (absent = no breakdown). Optional rather than nullable so the generated SDK
2629
+ * types it `groupBy?: 'provider'` — hey-api drops `null` on a single-value
2630
+ * nullable enum, which would mistype the no-breakdown (common) case.
2631
+ */
2632
+ groupBy: visibilityStatsGroupBySchema.optional(),
2633
+ window: visibilityStatsWindowSchema,
2634
+ /** Pooled counts across every tracked query in the window. */
2635
+ totals: visibilityStatsCountsSchema,
2636
+ /**
2637
+ * Pooled per-provider counts across every tracked query — present only when
2638
+ * `groupBy=provider`. Each entry's counts sum to `totals`.
2639
+ */
2640
+ byProvider: z20.array(visibilityStatsProviderEntrySchema).optional(),
2641
+ /** Per-query stats, sorted by query text. Only queries with ≥1 snapshot in the window appear. */
2642
+ queries: z20.array(visibilityStatsQueryEntrySchema)
2643
+ });
2644
+
2645
+ // ../contracts/src/ga.ts
2646
+ import { z as z21 } from "zod";
2647
+ var ga4ConnectionDtoSchema = z21.object({
2648
+ id: z21.string(),
2649
+ projectId: z21.string(),
2650
+ propertyId: z21.string(),
2651
+ clientEmail: z21.string(),
2652
+ connected: z21.boolean(),
2653
+ createdAt: z21.string(),
2654
+ updatedAt: z21.string()
2655
+ });
2656
+ var ga4TrafficSnapshotDtoSchema = z21.object({
2657
+ date: z21.string(),
2658
+ landingPage: z21.string(),
2659
+ sessions: z21.number(),
2660
+ organicSessions: z21.number(),
2661
+ users: z21.number()
2662
+ });
2663
+ var ga4SourceDimensionSchema = z21.enum(["session", "first_user", "manual_utm"]);
2664
+ var ga4AiReferralDtoSchema = z21.object({
2665
+ source: z21.string(),
2666
+ medium: z21.string(),
2667
+ sessions: z21.number(),
2668
+ users: z21.number(),
2169
2669
  /**
2170
2670
  * The winning attribution dimension for this (source, medium) tuple — the
2171
2671
  * one with the highest session count. GA4 emits one row per dimension
@@ -2175,144 +2675,144 @@ var ga4AiReferralDtoSchema = z20.object({
2175
2675
  */
2176
2676
  sourceDimension: ga4SourceDimensionSchema
2177
2677
  });
2178
- var ga4AiReferralLandingPageDtoSchema = z20.object({
2179
- source: z20.string(),
2180
- medium: z20.string(),
2678
+ var ga4AiReferralLandingPageDtoSchema = z21.object({
2679
+ source: z21.string(),
2680
+ medium: z21.string(),
2181
2681
  /**
2182
2682
  * The winning attribution dimension for this (source, medium, landingPage)
2183
2683
  * tuple — the one with the highest session count.
2184
2684
  */
2185
2685
  sourceDimension: ga4SourceDimensionSchema,
2186
- landingPage: z20.string(),
2187
- sessions: z20.number(),
2188
- users: z20.number()
2189
- });
2190
- var ga4SocialReferralDtoSchema = z20.object({
2191
- source: z20.string(),
2192
- medium: z20.string(),
2193
- sessions: z20.number(),
2194
- users: z20.number(),
2686
+ landingPage: z21.string(),
2687
+ sessions: z21.number(),
2688
+ users: z21.number()
2689
+ });
2690
+ var ga4SocialReferralDtoSchema = z21.object({
2691
+ source: z21.string(),
2692
+ medium: z21.string(),
2693
+ sessions: z21.number(),
2694
+ users: z21.number(),
2195
2695
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
2196
- channelGroup: z20.string()
2696
+ channelGroup: z21.string()
2197
2697
  });
2198
- var ga4ChannelBucketDtoSchema = z20.object({
2199
- sessions: z20.number(),
2200
- sharePct: z20.number(),
2201
- sharePctDisplay: z20.string()
2698
+ var ga4ChannelBucketDtoSchema = z21.object({
2699
+ sessions: z21.number(),
2700
+ sharePct: z21.number(),
2701
+ sharePctDisplay: z21.string()
2202
2702
  });
2203
- var ga4ChannelBreakdownDtoSchema = z20.object({
2703
+ var ga4ChannelBreakdownDtoSchema = z21.object({
2204
2704
  organic: ga4ChannelBucketDtoSchema,
2205
2705
  social: ga4ChannelBucketDtoSchema,
2206
2706
  direct: ga4ChannelBucketDtoSchema,
2207
2707
  ai: ga4ChannelBucketDtoSchema,
2208
2708
  other: ga4ChannelBucketDtoSchema
2209
2709
  });
2210
- var ga4TrafficSummaryDtoSchema = z20.object({
2211
- totalSessions: z20.number(),
2212
- totalOrganicSessions: z20.number(),
2710
+ var ga4TrafficSummaryDtoSchema = z21.object({
2711
+ totalSessions: z21.number(),
2712
+ totalOrganicSessions: z21.number(),
2213
2713
  /** 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. */
2214
- totalDirectSessions: z20.number(),
2215
- totalUsers: z20.number(),
2216
- topPages: z20.array(z20.object({
2217
- landingPage: z20.string(),
2218
- sessions: z20.number(),
2219
- organicSessions: z20.number(),
2714
+ totalDirectSessions: z21.number(),
2715
+ totalUsers: z21.number(),
2716
+ topPages: z21.array(z21.object({
2717
+ landingPage: z21.string(),
2718
+ sessions: z21.number(),
2719
+ organicSessions: z21.number(),
2220
2720
  /** Per-page Direct-channel sessions. 0 for legacy rows. */
2221
- directSessions: z20.number(),
2222
- users: z20.number()
2721
+ directSessions: z21.number(),
2722
+ users: z21.number()
2223
2723
  })),
2224
- aiReferrals: z20.array(ga4AiReferralDtoSchema),
2225
- aiReferralLandingPages: z20.array(ga4AiReferralLandingPageDtoSchema),
2724
+ aiReferrals: z21.array(ga4AiReferralDtoSchema),
2725
+ aiReferralLandingPages: z21.array(ga4AiReferralLandingPageDtoSchema),
2226
2726
  /** 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. */
2227
- aiSessionsDeduped: z20.number(),
2727
+ aiSessionsDeduped: z21.number(),
2228
2728
  /** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
2229
- aiUsersDeduped: z20.number(),
2729
+ aiUsersDeduped: z21.number(),
2230
2730
  /** AI sessions whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals; `channelBreakdown` removes those overlaps for display. */
2231
- aiSessionsBySession: z20.number(),
2731
+ aiSessionsBySession: z21.number(),
2232
2732
  /** AI users whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals. */
2233
- aiUsersBySession: z20.number(),
2234
- socialReferrals: z20.array(ga4SocialReferralDtoSchema),
2733
+ aiUsersBySession: z21.number(),
2734
+ socialReferrals: z21.array(ga4SocialReferralDtoSchema),
2235
2735
  /** Total social sessions (session-scoped, no cross-dimension dedup needed). */
2236
- socialSessions: z20.number(),
2736
+ socialSessions: z21.number(),
2237
2737
  /** Total social users (session-scoped, no cross-dimension dedup needed). */
2238
- socialUsers: z20.number(),
2738
+ socialUsers: z21.number(),
2239
2739
  /** Five disjoint buckets used for the channel breakdown. Known AI session-source matches are removed from their native GA4 bucket before shares are computed. */
2240
2740
  channelBreakdown: ga4ChannelBreakdownDtoSchema,
2241
2741
  /** Organic sessions as a percentage of total sessions (0–100, rounded). */
2242
- organicSharePct: z20.number(),
2742
+ organicSharePct: z21.number(),
2243
2743
  /** Deduped AI sessions as a percentage of total sessions (0–100, rounded). Cross-cutting: can overlap with Direct/Organic/Social. */
2244
- aiSharePct: z20.number(),
2744
+ aiSharePct: z21.number(),
2245
2745
  /** Session-source-only AI sessions as a percentage of total sessions (0–100, rounded). Can overlap with raw Organic/Social/Direct totals. */
2246
- aiSharePctBySession: z20.number(),
2746
+ aiSharePctBySession: z21.number(),
2247
2747
  /** Direct-channel sessions as a percentage of total sessions (0–100, rounded). */
2248
- directSharePct: z20.number(),
2748
+ directSharePct: z21.number(),
2249
2749
  /** Social sessions as a percentage of total sessions (0–100, rounded). */
2250
- socialSharePct: z20.number(),
2750
+ socialSharePct: z21.number(),
2251
2751
  /** Display string for organicSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2252
- organicSharePctDisplay: z20.string(),
2752
+ organicSharePctDisplay: z21.string(),
2253
2753
  /** Display string for aiSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2254
- aiSharePctDisplay: z20.string(),
2754
+ aiSharePctDisplay: z21.string(),
2255
2755
  /** Display string for aiSharePctBySession: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2256
- aiSharePctBySessionDisplay: z20.string(),
2756
+ aiSharePctBySessionDisplay: z21.string(),
2257
2757
  /** Display string for directSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2258
- directSharePctDisplay: z20.string(),
2758
+ directSharePctDisplay: z21.string(),
2259
2759
  /** Display string for socialSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2260
- socialSharePctDisplay: z20.string(),
2760
+ socialSharePctDisplay: z21.string(),
2261
2761
  /** 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). */
2262
- otherSessions: z20.number(),
2762
+ otherSessions: z21.number(),
2263
2763
  /** Other sessions as a percentage of total sessions (0–100, rounded). */
2264
- otherSharePct: z20.number(),
2764
+ otherSharePct: z21.number(),
2265
2765
  /** Display string for otherSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
2266
- otherSharePctDisplay: z20.string(),
2267
- lastSyncedAt: z20.string().nullable()
2268
- });
2269
- var ga4StatusDtoSchema = z20.object({
2270
- connected: z20.boolean(),
2271
- propertyId: z20.string().nullable(),
2272
- clientEmail: z20.string().nullable(),
2273
- authMethod: z20.enum(["service-account", "oauth"]).nullable(),
2274
- lastSyncedAt: z20.string().nullable(),
2275
- createdAt: z20.string().nullable().optional(),
2276
- updatedAt: z20.string().nullable().optional()
2277
- });
2278
- var ga4SyncResponseDtoSchema = z20.object({
2279
- synced: z20.boolean(),
2280
- rowCount: z20.number().int().nonnegative(),
2281
- aiReferralCount: z20.number().int().nonnegative(),
2282
- socialReferralCount: z20.number().int().nonnegative(),
2283
- days: z20.number().int().nonnegative(),
2284
- syncedAt: z20.string(),
2766
+ otherSharePctDisplay: z21.string(),
2767
+ lastSyncedAt: z21.string().nullable()
2768
+ });
2769
+ var ga4StatusDtoSchema = z21.object({
2770
+ connected: z21.boolean(),
2771
+ propertyId: z21.string().nullable(),
2772
+ clientEmail: z21.string().nullable(),
2773
+ authMethod: z21.enum(["service-account", "oauth"]).nullable(),
2774
+ lastSyncedAt: z21.string().nullable(),
2775
+ createdAt: z21.string().nullable().optional(),
2776
+ updatedAt: z21.string().nullable().optional()
2777
+ });
2778
+ var ga4SyncResponseDtoSchema = z21.object({
2779
+ synced: z21.boolean(),
2780
+ rowCount: z21.number().int().nonnegative(),
2781
+ aiReferralCount: z21.number().int().nonnegative(),
2782
+ socialReferralCount: z21.number().int().nonnegative(),
2783
+ days: z21.number().int().nonnegative(),
2784
+ syncedAt: z21.string(),
2285
2785
  /**
2286
2786
  * Components that were written this run. Present when `only` is set.
2287
2787
  * Always includes `traffic` and `summary` (the share denominator) plus
2288
2788
  * the requested channel breakdown — `ai` and/or `social`.
2289
2789
  */
2290
- syncedComponents: z20.array(z20.string()).optional()
2291
- });
2292
- var ga4AiReferralHistoryEntrySchema = z20.object({
2293
- date: z20.string(),
2294
- source: z20.string(),
2295
- medium: z20.string(),
2296
- landingPage: z20.string(),
2297
- sessions: z20.number(),
2298
- users: z20.number(),
2790
+ syncedComponents: z21.array(z21.string()).optional()
2791
+ });
2792
+ var ga4AiReferralHistoryEntrySchema = z21.object({
2793
+ date: z21.string(),
2794
+ source: z21.string(),
2795
+ medium: z21.string(),
2796
+ landingPage: z21.string(),
2797
+ sessions: z21.number(),
2798
+ users: z21.number(),
2299
2799
  /** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
2300
2800
  sourceDimension: ga4SourceDimensionSchema
2301
2801
  });
2302
- var ga4SocialReferralHistoryEntrySchema = z20.object({
2303
- date: z20.string(),
2304
- source: z20.string(),
2305
- medium: z20.string(),
2306
- sessions: z20.number(),
2307
- users: z20.number(),
2802
+ var ga4SocialReferralHistoryEntrySchema = z21.object({
2803
+ date: z21.string(),
2804
+ source: z21.string(),
2805
+ medium: z21.string(),
2806
+ sessions: z21.number(),
2807
+ users: z21.number(),
2308
2808
  /** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
2309
- channelGroup: z20.string()
2809
+ channelGroup: z21.string()
2310
2810
  });
2311
- var ga4SessionHistoryEntrySchema = z20.object({
2312
- date: z20.string(),
2313
- sessions: z20.number(),
2314
- organicSessions: z20.number(),
2315
- users: z20.number()
2811
+ var ga4SessionHistoryEntrySchema = z21.object({
2812
+ date: z21.string(),
2813
+ sessions: z21.number(),
2814
+ organicSessions: z21.number(),
2815
+ users: z21.number()
2316
2816
  });
2317
2817
 
2318
2818
  // ../contracts/src/answer-visibility.ts
@@ -2480,50 +2980,50 @@ function escapeRegExp(value) {
2480
2980
  }
2481
2981
 
2482
2982
  // ../contracts/src/agent.ts
2483
- import { z as z21 } from "zod";
2484
- var agentProviderIdSchema = z21.enum(["claude", "openai", "gemini", "zai"]);
2485
- var agentProviderOptionDtoSchema = z21.object({
2983
+ import { z as z22 } from "zod";
2984
+ var agentProviderIdSchema = z22.enum(["claude", "openai", "gemini", "zai"]);
2985
+ var agentProviderOptionDtoSchema = z22.object({
2486
2986
  /** Stable identifier — what clients pass back as `provider` on the prompt endpoint. */
2487
2987
  id: agentProviderIdSchema,
2488
2988
  /** Human-readable label for UI pickers, e.g. "Anthropic (Claude)". */
2489
- label: z21.string(),
2989
+ label: z22.string(),
2490
2990
  /** Default model if the caller doesn't pick one. */
2491
- defaultModel: z21.string(),
2991
+ defaultModel: z22.string(),
2492
2992
  /** Whether a usable API key was found (config.yaml or provider env var). */
2493
- configured: z21.boolean(),
2993
+ configured: z22.boolean(),
2494
2994
  /**
2495
2995
  * Where the key resolved from, if any. `null` when `configured === false`.
2496
2996
  * Surfaced so the UI can nudge users toward their preferred source of truth.
2497
2997
  */
2498
- keySource: z21.enum(["config", "env"]).nullable()
2998
+ keySource: z22.enum(["config", "env"]).nullable()
2499
2999
  });
2500
- var agentProvidersResponseDtoSchema = z21.object({
3000
+ var agentProvidersResponseDtoSchema = z22.object({
2501
3001
  /**
2502
3002
  * Every provider Aero knows about. `configured === false` entries are
2503
3003
  * included so the UI can render them disabled with an onboarding hint.
2504
3004
  */
2505
- providers: z21.array(agentProviderOptionDtoSchema).default([]),
3005
+ providers: z22.array(agentProviderOptionDtoSchema).default([]),
2506
3006
  /**
2507
3007
  * Provider Aero auto-picks when no explicit override is passed. Null if
2508
3008
  * nothing is configured (install never exchanged a key).
2509
3009
  */
2510
3010
  defaultProvider: agentProviderIdSchema.nullable()
2511
3011
  });
2512
- var memorySourceSchema = z21.enum(["aero", "user", "compaction"]);
3012
+ var memorySourceSchema = z22.enum(["aero", "user", "compaction"]);
2513
3013
  var MemorySources = memorySourceSchema.enum;
2514
3014
  var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
2515
3015
  var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
2516
- var agentMemoryUpsertRequestSchema = z21.object({
2517
- key: z21.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
2518
- value: z21.string().min(1)
3016
+ var agentMemoryUpsertRequestSchema = z22.object({
3017
+ key: z22.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
3018
+ value: z22.string().min(1)
2519
3019
  });
2520
- var agentMemoryDeleteRequestSchema = z21.object({
2521
- key: z21.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
3020
+ var agentMemoryDeleteRequestSchema = z22.object({
3021
+ key: z22.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
2522
3022
  });
2523
3023
 
2524
3024
  // ../contracts/src/backlinks.ts
2525
- import { z as z22 } from "zod";
2526
- var backlinkSourceSchema = z22.enum(["commoncrawl", "bing-webmaster"]);
3025
+ import { z as z23 } from "zod";
3026
+ var backlinkSourceSchema = z23.enum(["commoncrawl", "bing-webmaster"]);
2527
3027
  var BacklinkSources = backlinkSourceSchema.enum;
2528
3028
  function computeBacklinkSummaryMetrics(rows) {
2529
3029
  if (rows.length === 0) {
@@ -2539,148 +3039,148 @@ function computeBacklinkSummaryMetrics(rows) {
2539
3039
  top10HostsShare: share.toFixed(6)
2540
3040
  };
2541
3041
  }
2542
- var ccReleaseSyncStatusSchema = z22.enum(["queued", "downloading", "querying", "ready", "failed"]);
3042
+ var ccReleaseSyncStatusSchema = z23.enum(["queued", "downloading", "querying", "ready", "failed"]);
2543
3043
  var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
2544
- var ccReleaseSyncDtoSchema = z22.object({
2545
- id: z22.string(),
2546
- release: z22.string(),
3044
+ var ccReleaseSyncDtoSchema = z23.object({
3045
+ id: z23.string(),
3046
+ release: z23.string(),
2547
3047
  status: ccReleaseSyncStatusSchema,
2548
- phaseDetail: z22.string().nullable().optional(),
2549
- vertexPath: z22.string().nullable().optional(),
2550
- edgesPath: z22.string().nullable().optional(),
2551
- vertexSha256: z22.string().nullable().optional(),
2552
- edgesSha256: z22.string().nullable().optional(),
2553
- vertexBytes: z22.number().int().nullable().optional(),
2554
- edgesBytes: z22.number().int().nullable().optional(),
2555
- projectsProcessed: z22.number().int().nullable().optional(),
2556
- domainsDiscovered: z22.number().int().nullable().optional(),
2557
- downloadStartedAt: z22.string().nullable().optional(),
2558
- downloadFinishedAt: z22.string().nullable().optional(),
2559
- queryStartedAt: z22.string().nullable().optional(),
2560
- queryFinishedAt: z22.string().nullable().optional(),
2561
- error: z22.string().nullable().optional(),
2562
- createdAt: z22.string(),
2563
- updatedAt: z22.string()
2564
- });
2565
- var backlinkDomainDtoSchema = z22.object({
2566
- linkingDomain: z22.string(),
3048
+ phaseDetail: z23.string().nullable().optional(),
3049
+ vertexPath: z23.string().nullable().optional(),
3050
+ edgesPath: z23.string().nullable().optional(),
3051
+ vertexSha256: z23.string().nullable().optional(),
3052
+ edgesSha256: z23.string().nullable().optional(),
3053
+ vertexBytes: z23.number().int().nullable().optional(),
3054
+ edgesBytes: z23.number().int().nullable().optional(),
3055
+ projectsProcessed: z23.number().int().nullable().optional(),
3056
+ domainsDiscovered: z23.number().int().nullable().optional(),
3057
+ downloadStartedAt: z23.string().nullable().optional(),
3058
+ downloadFinishedAt: z23.string().nullable().optional(),
3059
+ queryStartedAt: z23.string().nullable().optional(),
3060
+ queryFinishedAt: z23.string().nullable().optional(),
3061
+ error: z23.string().nullable().optional(),
3062
+ createdAt: z23.string(),
3063
+ updatedAt: z23.string()
3064
+ });
3065
+ var backlinkDomainDtoSchema = z23.object({
3066
+ linkingDomain: z23.string(),
2567
3067
  // For Common Crawl this is the count of distinct hosts within the linking
2568
3068
  // domain; for Bing Webmaster it is the count of distinct linking pages (URLs)
2569
3069
  // from that linking host. Read alongside `source` — the unit differs per source.
2570
- numHosts: z22.number().int(),
3070
+ numHosts: z23.number().int(),
2571
3071
  source: backlinkSourceSchema
2572
3072
  });
2573
- var backlinkSummaryDtoSchema = z22.object({
2574
- projectId: z22.string(),
3073
+ var backlinkSummaryDtoSchema = z23.object({
3074
+ projectId: z23.string(),
2575
3075
  // Window identifier. Common Crawl uses the release slug
2576
3076
  // (`cc-main-YYYY-<mon>-<mon>-<mon>`); Bing Webmaster uses a synthetic
2577
3077
  // per-sync-day window (`bing-YYYY-MM-DD`).
2578
- release: z22.string(),
2579
- targetDomain: z22.string(),
2580
- totalLinkingDomains: z22.number().int(),
2581
- totalHosts: z22.number().int(),
2582
- top10HostsShare: z22.string(),
2583
- queriedAt: z22.string(),
3078
+ release: z23.string(),
3079
+ targetDomain: z23.string(),
3080
+ totalLinkingDomains: z23.number().int(),
3081
+ totalHosts: z23.number().int(),
3082
+ top10HostsShare: z23.string(),
3083
+ queriedAt: z23.string(),
2584
3084
  source: backlinkSourceSchema,
2585
3085
  // Populated when the response is filtered (e.g. ?excludeCrawlers=1).
2586
3086
  // Counts the rows omitted from totalLinkingDomains/totalHosts so callers
2587
3087
  // can show "N hidden" hints without re-deriving them.
2588
- excludedLinkingDomains: z22.number().int().optional(),
2589
- excludedHosts: z22.number().int().optional()
3088
+ excludedLinkingDomains: z23.number().int().optional(),
3089
+ excludedHosts: z23.number().int().optional()
2590
3090
  });
2591
- var backlinkListResponseSchema = z22.object({
3091
+ var backlinkListResponseSchema = z23.object({
2592
3092
  // The source this response was filtered to (defaults to commoncrawl when the
2593
3093
  // caller omits `?source`).
2594
3094
  source: backlinkSourceSchema,
2595
3095
  summary: backlinkSummaryDtoSchema.nullable(),
2596
- total: z22.number().int(),
2597
- rows: z22.array(backlinkDomainDtoSchema)
2598
- });
2599
- var backlinkHistoryEntrySchema = z22.object({
2600
- release: z22.string(),
2601
- totalLinkingDomains: z22.number().int(),
2602
- totalHosts: z22.number().int(),
2603
- top10HostsShare: z22.string(),
2604
- queriedAt: z22.string(),
3096
+ total: z23.number().int(),
3097
+ rows: z23.array(backlinkDomainDtoSchema)
3098
+ });
3099
+ var backlinkHistoryEntrySchema = z23.object({
3100
+ release: z23.string(),
3101
+ totalLinkingDomains: z23.number().int(),
3102
+ totalHosts: z23.number().int(),
3103
+ top10HostsShare: z23.string(),
3104
+ queriedAt: z23.string(),
2605
3105
  source: backlinkSourceSchema
2606
3106
  });
2607
- var backlinkSourceAvailabilityDtoSchema = z22.object({
3107
+ var backlinkSourceAvailabilityDtoSchema = z23.object({
2608
3108
  source: backlinkSourceSchema,
2609
3109
  /**
2610
3110
  * The source is set up for this project:
2611
3111
  * - commoncrawl: `autoExtractBacklinks` enabled AND a `ready` release sync exists.
2612
3112
  * - bing-webmaster: a Bing Webmaster connection exists for the project domain.
2613
3113
  */
2614
- connected: z22.boolean(),
3114
+ connected: z23.boolean(),
2615
3115
  /** Backlink rows exist for this project + source. */
2616
- hasData: z22.boolean(),
3116
+ hasData: z23.boolean(),
2617
3117
  /** Latest window id with data for this source, null when none. */
2618
- latestRelease: z22.string().nullable(),
3118
+ latestRelease: z23.string().nullable(),
2619
3119
  /**
2620
3120
  * Linking-domain count in the latest window. Excludes crawler/proxy hosts only
2621
3121
  * when the request sets `?excludeCrawlers=1` (default off, matching the summary
2622
3122
  * and domains endpoints); the dashboard passes it so the switcher pill matches
2623
3123
  * the metric card.
2624
3124
  */
2625
- totalLinkingDomains: z22.number().int(),
3125
+ totalLinkingDomains: z23.number().int(),
2626
3126
  /** Freshness: `queriedAt` of the latest summary for this source, null when none. */
2627
- lastSyncedAt: z22.string().nullable()
3127
+ lastSyncedAt: z23.string().nullable()
2628
3128
  });
2629
- var backlinkSourcesResponseSchema = z22.object({
2630
- projectId: z22.string(),
2631
- targetDomain: z22.string(),
3129
+ var backlinkSourcesResponseSchema = z23.object({
3130
+ projectId: z23.string(),
3131
+ targetDomain: z23.string(),
2632
3132
  /** Availability for every known source, in a stable order. */
2633
- sources: z22.array(backlinkSourceAvailabilityDtoSchema),
2634
- anyConnected: z22.boolean(),
2635
- anyData: z22.boolean()
2636
- });
2637
- var backlinksInstallStatusDtoSchema = z22.object({
2638
- duckdbInstalled: z22.boolean(),
2639
- duckdbVersion: z22.string().nullable().optional(),
2640
- duckdbSpec: z22.string(),
2641
- pluginDir: z22.string()
2642
- });
2643
- var backlinksInstallResultDtoSchema = z22.object({
2644
- installed: z22.boolean(),
2645
- version: z22.string(),
2646
- path: z22.string(),
2647
- alreadyPresent: z22.boolean()
2648
- });
2649
- var ccAvailableReleaseSchema = z22.object({
2650
- release: z22.string(),
2651
- vertexUrl: z22.string(),
2652
- edgesUrl: z22.string(),
2653
- vertexBytes: z22.number().int().nullable(),
2654
- edgesBytes: z22.number().int().nullable(),
2655
- lastModified: z22.string().nullable()
2656
- });
2657
- var ccCachedReleaseSchema = z22.object({
2658
- release: z22.string(),
3133
+ sources: z23.array(backlinkSourceAvailabilityDtoSchema),
3134
+ anyConnected: z23.boolean(),
3135
+ anyData: z23.boolean()
3136
+ });
3137
+ var backlinksInstallStatusDtoSchema = z23.object({
3138
+ duckdbInstalled: z23.boolean(),
3139
+ duckdbVersion: z23.string().nullable().optional(),
3140
+ duckdbSpec: z23.string(),
3141
+ pluginDir: z23.string()
3142
+ });
3143
+ var backlinksInstallResultDtoSchema = z23.object({
3144
+ installed: z23.boolean(),
3145
+ version: z23.string(),
3146
+ path: z23.string(),
3147
+ alreadyPresent: z23.boolean()
3148
+ });
3149
+ var ccAvailableReleaseSchema = z23.object({
3150
+ release: z23.string(),
3151
+ vertexUrl: z23.string(),
3152
+ edgesUrl: z23.string(),
3153
+ vertexBytes: z23.number().int().nullable(),
3154
+ edgesBytes: z23.number().int().nullable(),
3155
+ lastModified: z23.string().nullable()
3156
+ });
3157
+ var ccCachedReleaseSchema = z23.object({
3158
+ release: z23.string(),
2659
3159
  syncStatus: ccReleaseSyncStatusSchema.nullable(),
2660
- bytes: z22.number().int(),
2661
- lastUsedAt: z22.string().nullable()
3160
+ bytes: z23.number().int(),
3161
+ lastUsedAt: z23.string().nullable()
2662
3162
  });
2663
3163
 
2664
3164
  // ../contracts/src/composites.ts
2665
- import { z as z23 } from "zod";
2666
- var searchHitKindSchema = z23.enum(["snapshot", "insight"]);
2667
- var projectSearchSnapshotHitSchema = z23.object({
2668
- kind: z23.literal("snapshot"),
2669
- id: z23.string(),
2670
- runId: z23.string(),
2671
- query: z23.string(),
2672
- provider: z23.string(),
2673
- model: z23.string().nullable(),
3165
+ import { z as z24 } from "zod";
3166
+ var searchHitKindSchema = z24.enum(["snapshot", "insight"]);
3167
+ var projectSearchSnapshotHitSchema = z24.object({
3168
+ kind: z24.literal("snapshot"),
3169
+ id: z24.string(),
3170
+ runId: z24.string(),
3171
+ query: z24.string(),
3172
+ provider: z24.string(),
3173
+ model: z24.string().nullable(),
2674
3174
  citationState: citationStateSchema,
2675
- matchedField: z23.enum(["answerText", "citedDomains", "searchQueries", "query"]),
2676
- snippet: z23.string(),
2677
- createdAt: z23.string()
2678
- });
2679
- var projectSearchInsightHitSchema = z23.object({
2680
- kind: z23.literal("insight"),
2681
- id: z23.string(),
2682
- runId: z23.string().nullable(),
2683
- type: z23.enum([
3175
+ matchedField: z24.enum(["answerText", "citedDomains", "searchQueries", "query"]),
3176
+ snippet: z24.string(),
3177
+ createdAt: z24.string()
3178
+ });
3179
+ var projectSearchInsightHitSchema = z24.object({
3180
+ kind: z24.literal("insight"),
3181
+ id: z24.string(),
3182
+ runId: z24.string().nullable(),
3183
+ type: z24.enum([
2684
3184
  "regression",
2685
3185
  "gain",
2686
3186
  "opportunity",
@@ -2696,29 +3196,29 @@ var projectSearchInsightHitSchema = z23.object({
2696
3196
  "gbp-metric-drop",
2697
3197
  "gbp-keyword-drop"
2698
3198
  ]),
2699
- severity: z23.enum(["critical", "high", "medium", "low"]),
2700
- title: z23.string(),
2701
- query: z23.string(),
2702
- provider: z23.string(),
2703
- matchedField: z23.enum(["title", "query", "recommendation", "cause"]),
2704
- snippet: z23.string(),
2705
- dismissed: z23.boolean(),
2706
- createdAt: z23.string()
2707
- });
2708
- var projectSearchHitSchema = z23.discriminatedUnion("kind", [
3199
+ severity: z24.enum(["critical", "high", "medium", "low"]),
3200
+ title: z24.string(),
3201
+ query: z24.string(),
3202
+ provider: z24.string(),
3203
+ matchedField: z24.enum(["title", "query", "recommendation", "cause"]),
3204
+ snippet: z24.string(),
3205
+ dismissed: z24.boolean(),
3206
+ createdAt: z24.string()
3207
+ });
3208
+ var projectSearchHitSchema = z24.discriminatedUnion("kind", [
2709
3209
  projectSearchSnapshotHitSchema,
2710
3210
  projectSearchInsightHitSchema
2711
3211
  ]);
2712
- var projectSearchResponseSchema = z23.object({
2713
- query: z23.string(),
2714
- totalHits: z23.number().int().nonnegative(),
2715
- truncated: z23.boolean(),
2716
- hits: z23.array(projectSearchHitSchema)
3212
+ var projectSearchResponseSchema = z24.object({
3213
+ query: z24.string(),
3214
+ totalHits: z24.number().int().nonnegative(),
3215
+ truncated: z24.boolean(),
3216
+ hits: z24.array(projectSearchHitSchema)
2717
3217
  });
2718
3218
 
2719
3219
  // ../contracts/src/content.ts
2720
- import { z as z24 } from "zod";
2721
- var contentActionSchema = z24.enum(["create", "expand", "refresh", "add-schema"]);
3220
+ import { z as z25 } from "zod";
3221
+ var contentActionSchema = z25.enum(["create", "expand", "refresh", "add-schema"]);
2722
3222
  var ContentActions = contentActionSchema.enum;
2723
3223
  function contentActionLabel(action) {
2724
3224
  switch (action) {
@@ -2732,9 +3232,9 @@ function contentActionLabel(action) {
2732
3232
  return "Add schema";
2733
3233
  }
2734
3234
  }
2735
- var demandSourceSchema = z24.enum(["gsc", "competitor-evidence", "both"]);
3235
+ var demandSourceSchema = z25.enum(["gsc", "competitor-evidence", "both"]);
2736
3236
  var DemandSources = demandSourceSchema.enum;
2737
- var actionConfidenceSchema = z24.enum(["high", "medium", "low"]);
3237
+ var actionConfidenceSchema = z25.enum(["high", "medium", "low"]);
2738
3238
  var ActionConfidences = actionConfidenceSchema.enum;
2739
3239
  function actionConfidenceLabel(confidence) {
2740
3240
  switch (confidence) {
@@ -2746,7 +3246,7 @@ function actionConfidenceLabel(confidence) {
2746
3246
  return "Low";
2747
3247
  }
2748
3248
  }
2749
- var pageTypeSchema = z24.enum([
3249
+ var pageTypeSchema = z25.enum([
2750
3250
  "blog-post",
2751
3251
  "comparison",
2752
3252
  "listicle",
@@ -2755,7 +3255,7 @@ var pageTypeSchema = z24.enum([
2755
3255
  "glossary"
2756
3256
  ]);
2757
3257
  var PageTypes = pageTypeSchema.enum;
2758
- var contentActionStateSchema = z24.enum([
3258
+ var contentActionStateSchema = z25.enum([
2759
3259
  "proposed",
2760
3260
  "briefed",
2761
3261
  "payload-generated",
@@ -2765,7 +3265,7 @@ var contentActionStateSchema = z24.enum([
2765
3265
  "dismissed"
2766
3266
  ]);
2767
3267
  var ContentActionStates = contentActionStateSchema.enum;
2768
- var winnabilityClassSchema = z24.enum(["ownable", "ceded"]);
3268
+ var winnabilityClassSchema = z25.enum(["ownable", "ceded"]);
2769
3269
  var WinnabilityClasses = winnabilityClassSchema.enum;
2770
3270
  function winnabilityClassLabel(winnabilityClass) {
2771
3271
  switch (winnabilityClass) {
@@ -2800,40 +3300,40 @@ function deriveWinnabilityClass(citedSurfaceDomains, surfaceClasses, threshold =
2800
3300
  winnability
2801
3301
  };
2802
3302
  }
2803
- var ourBestPageSchema = z24.object({
2804
- url: z24.string(),
2805
- gscImpressions: z24.number().nonnegative(),
2806
- gscClicks: z24.number().nonnegative(),
3303
+ var ourBestPageSchema = z25.object({
3304
+ url: z25.string(),
3305
+ gscImpressions: z25.number().nonnegative(),
3306
+ gscClicks: z25.number().nonnegative(),
2807
3307
  // Null when the page came from the inventory fallback (no GSC ranking data).
2808
- gscAvgPosition: z24.number().nonnegative().nullable(),
2809
- organicSessions: z24.number().nonnegative()
3308
+ gscAvgPosition: z25.number().nonnegative().nullable(),
3309
+ organicSessions: z25.number().nonnegative()
2810
3310
  });
2811
- var winningCompetitorSchema = z24.object({
2812
- domain: z24.string(),
2813
- url: z24.string(),
2814
- title: z24.string(),
2815
- citationCount: z24.number().int().nonnegative()
3311
+ var winningCompetitorSchema = z25.object({
3312
+ domain: z25.string(),
3313
+ url: z25.string(),
3314
+ title: z25.string(),
3315
+ citationCount: z25.number().int().nonnegative()
2816
3316
  });
2817
- var scoreBreakdownSchema = z24.object({
2818
- demand: z24.number(),
2819
- competitor: z24.number(),
2820
- absence: z24.number(),
2821
- gapSeverity: z24.number()
3317
+ var scoreBreakdownSchema = z25.object({
3318
+ demand: z25.number(),
3319
+ competitor: z25.number(),
3320
+ absence: z25.number(),
3321
+ gapSeverity: z25.number()
2822
3322
  });
2823
- var existingActionRefSchema = z24.object({
2824
- actionId: z24.string(),
3323
+ var existingActionRefSchema = z25.object({
3324
+ actionId: z25.string(),
2825
3325
  state: contentActionStateSchema,
2826
- lastUpdated: z24.string()
3326
+ lastUpdated: z25.string()
2827
3327
  });
2828
- var contentTargetRowDtoSchema = z24.object({
2829
- targetRef: z24.string(),
2830
- query: z24.string(),
3328
+ var contentTargetRowDtoSchema = z25.object({
3329
+ targetRef: z25.string(),
3330
+ query: z25.string(),
2831
3331
  action: contentActionSchema,
2832
3332
  ourBestPage: ourBestPageSchema.nullable(),
2833
3333
  winningCompetitor: winningCompetitorSchema.nullable(),
2834
- score: z24.number(),
3334
+ score: z25.number(),
2835
3335
  scoreBreakdown: scoreBreakdownSchema,
2836
- drivers: z24.array(z24.string()),
3336
+ drivers: z25.array(z25.string()),
2837
3337
  demandSource: demandSourceSchema,
2838
3338
  actionConfidence: actionConfidenceSchema,
2839
3339
  existingAction: existingActionRefSchema.nullable(),
@@ -2848,134 +3348,134 @@ var contentTargetRowDtoSchema = z24.object({
2848
3348
  * `[0, 1]`. `null` when the gate failed open (no classification coverage for
2849
3349
  * the cited surface) — distinct from a computed `1.0`.
2850
3350
  */
2851
- winnability: z24.number().min(0).max(1).nullable()
2852
- });
2853
- var contentTargetsResponseDtoSchema = z24.object({
2854
- targets: z24.array(contentTargetRowDtoSchema),
2855
- contextMetrics: z24.object({
2856
- totalAiReferralSessions: z24.number().int().nonnegative(),
2857
- latestRunId: z24.string(),
2858
- runTimestamp: z24.string()
3351
+ winnability: z25.number().min(0).max(1).nullable()
3352
+ });
3353
+ var contentTargetsResponseDtoSchema = z25.object({
3354
+ targets: z25.array(contentTargetRowDtoSchema),
3355
+ contextMetrics: z25.object({
3356
+ totalAiReferralSessions: z25.number().int().nonnegative(),
3357
+ latestRunId: z25.string(),
3358
+ runTimestamp: z25.string()
2859
3359
  })
2860
3360
  });
2861
- var contentTargetDismissalDtoSchema = z24.object({
2862
- targetRef: z24.string(),
2863
- addressedUrl: z24.string().nullable(),
2864
- note: z24.string().nullable(),
2865
- dismissedAt: z24.string()
3361
+ var contentTargetDismissalDtoSchema = z25.object({
3362
+ targetRef: z25.string(),
3363
+ addressedUrl: z25.string().nullable(),
3364
+ note: z25.string().nullable(),
3365
+ dismissedAt: z25.string()
2866
3366
  });
2867
- var contentTargetDismissalsResponseDtoSchema = z24.object({
2868
- dismissals: z24.array(contentTargetDismissalDtoSchema)
3367
+ var contentTargetDismissalsResponseDtoSchema = z25.object({
3368
+ dismissals: z25.array(contentTargetDismissalDtoSchema)
2869
3369
  });
2870
- var contentTargetDismissRequestSchema = z24.object({
2871
- targetRef: z24.string().min(1),
3370
+ var contentTargetDismissRequestSchema = z25.object({
3371
+ targetRef: z25.string().min(1),
2872
3372
  /** 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. */
2873
- addressedUrl: z24.string().url().optional(),
3373
+ addressedUrl: z25.string().url().optional(),
2874
3374
  /** Free-form note (e.g. "covered in our Q1 content sprint"). 500 char cap is the API surface limit; the DB column is unbounded. */
2875
- note: z24.string().max(500).optional()
3375
+ note: z25.string().max(500).optional()
2876
3376
  });
2877
- var recommendationExplanationDtoSchema = z24.object({
2878
- targetRef: z24.string(),
3377
+ var recommendationExplanationDtoSchema = z25.object({
3378
+ targetRef: z25.string(),
2879
3379
  /** Version of the prompt template used. Bumping the version invalidates the cache forward without touching the table. */
2880
- promptVersion: z24.string(),
3380
+ promptVersion: z25.string(),
2881
3381
  /** Provider that produced the explanation (e.g. "claude", "gemini"). */
2882
- provider: z24.string(),
3382
+ provider: z25.string(),
2883
3383
  /** Model id within that provider (e.g. "claude-sonnet-4-6"). */
2884
- model: z24.string(),
3384
+ model: z25.string(),
2885
3385
  /** Markdown-formatted rationale + recommended next steps. */
2886
- responseText: z24.string(),
3386
+ responseText: z25.string(),
2887
3387
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
2888
- costMillicents: z24.number().int().nonnegative(),
2889
- generatedAt: z24.string()
3388
+ costMillicents: z25.number().int().nonnegative(),
3389
+ generatedAt: z25.string()
2890
3390
  });
2891
- var recommendationExplainRequestSchema = z24.object({
3391
+ var recommendationExplainRequestSchema = z25.object({
2892
3392
  /**
2893
3393
  * Optional provider override (e.g. "claude" to force Claude even if
2894
3394
  * the project's default is Gemini). Falls through to project default
2895
3395
  * → auto-detect when omitted.
2896
3396
  */
2897
- provider: z24.string().optional(),
3397
+ provider: z25.string().optional(),
2898
3398
  /**
2899
3399
  * Optional model override within the chosen provider. Falls through to
2900
3400
  * the `analyze`-tier default model when omitted.
2901
3401
  */
2902
- model: z24.string().optional(),
3402
+ model: z25.string().optional(),
2903
3403
  /**
2904
3404
  * Force a fresh LLM call even if a cached explanation exists for the
2905
3405
  * current prompt version. Use sparingly — defeats the cache.
2906
3406
  */
2907
- forceRefresh: z24.boolean().optional()
3407
+ forceRefresh: z25.boolean().optional()
2908
3408
  });
2909
- var contentBriefDtoSchema = z24.object({
3409
+ var contentBriefDtoSchema = z25.object({
2910
3410
  /** The query the brief is for (echoed from the recommendation). */
2911
- targetQuery: z24.string().trim().min(1),
3411
+ targetQuery: z25.string().trim().min(1),
2912
3412
  /** Always `ownable` in practice — the gate rejects `ceded` before synthesis. */
2913
3413
  winnabilityClass: winnabilityClassSchema,
2914
3414
  /** The differentiated content angle to take. */
2915
- angle: z24.string().trim().min(1),
3415
+ angle: z25.string().trim().min(1),
2916
3416
  /** Why this query is winnable, citing the cited-surface signal. */
2917
- whyWinnable: z24.string().trim().min(1),
3417
+ whyWinnable: z25.string().trim().min(1),
2918
3418
  /** The schema.org type or markup to add or extend. */
2919
- schemaHookup: z24.string().trim().min(1),
3419
+ schemaHookup: z25.string().trim().min(1),
2920
3420
  /** Why the cited surface is controllable (the ownable-vs-ceded reasoning). */
2921
- controllableSurfaceRationale: z24.string().trim().min(1)
3421
+ controllableSurfaceRationale: z25.string().trim().min(1)
2922
3422
  });
2923
- var recommendationBriefDtoSchema = z24.object({
2924
- targetRef: z24.string(),
3423
+ var recommendationBriefDtoSchema = z25.object({
3424
+ targetRef: z25.string(),
2925
3425
  /** Version of the brief prompt template; bumping invalidates the cache forward. */
2926
- promptVersion: z24.string(),
2927
- provider: z24.string(),
2928
- model: z24.string(),
3426
+ promptVersion: z25.string(),
3427
+ provider: z25.string(),
3428
+ model: z25.string(),
2929
3429
  brief: contentBriefDtoSchema,
2930
3430
  /** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
2931
- costMillicents: z24.number().int().nonnegative(),
2932
- generatedAt: z24.string()
3431
+ costMillicents: z25.number().int().nonnegative(),
3432
+ generatedAt: z25.string()
2933
3433
  });
2934
- var domainClassificationDtoSchema = z24.object({
2935
- domain: z24.string(),
3434
+ var domainClassificationDtoSchema = z25.object({
3435
+ domain: z25.string(),
2936
3436
  competitorType: discoveryCompetitorTypeSchema,
2937
- hits: z24.number().int().nonnegative(),
2938
- updatedAt: z24.string()
2939
- });
2940
- var domainClassificationsResponseDtoSchema = z24.object({
2941
- classifications: z24.array(domainClassificationDtoSchema)
3437
+ hits: z25.number().int().nonnegative(),
3438
+ updatedAt: z25.string()
2942
3439
  });
2943
- var contentGroundingSourceSchema = z24.object({
2944
- uri: z24.string(),
2945
- title: z24.string(),
2946
- domain: z24.string(),
2947
- isOurDomain: z24.boolean(),
2948
- isCompetitor: z24.boolean(),
2949
- citationCount: z24.number().int().nonnegative(),
2950
- providers: z24.array(providerNameSchema)
2951
- });
2952
- var contentSourceRowDtoSchema = z24.object({
2953
- query: z24.string(),
2954
- groundingSources: z24.array(contentGroundingSourceSchema)
3440
+ var domainClassificationsResponseDtoSchema = z25.object({
3441
+ classifications: z25.array(domainClassificationDtoSchema)
2955
3442
  });
2956
- var contentSourcesResponseDtoSchema = z24.object({
2957
- sources: z24.array(contentSourceRowDtoSchema),
2958
- latestRunId: z24.string()
2959
- });
2960
- var contentGapRowDtoSchema = z24.object({
2961
- query: z24.string(),
2962
- competitorDomains: z24.array(z24.string()),
2963
- competitorCount: z24.number().int().nonnegative(),
2964
- missRate: z24.number().min(0).max(1),
2965
- lastSeenInRunId: z24.string()
2966
- });
2967
- var contentGapsResponseDtoSchema = z24.object({
2968
- gaps: z24.array(contentGapRowDtoSchema),
2969
- latestRunId: z24.string()
3443
+ var contentGroundingSourceSchema = z25.object({
3444
+ uri: z25.string(),
3445
+ title: z25.string(),
3446
+ domain: z25.string(),
3447
+ isOurDomain: z25.boolean(),
3448
+ isCompetitor: z25.boolean(),
3449
+ citationCount: z25.number().int().nonnegative(),
3450
+ providers: z25.array(providerNameSchema)
3451
+ });
3452
+ var contentSourceRowDtoSchema = z25.object({
3453
+ query: z25.string(),
3454
+ groundingSources: z25.array(contentGroundingSourceSchema)
3455
+ });
3456
+ var contentSourcesResponseDtoSchema = z25.object({
3457
+ sources: z25.array(contentSourceRowDtoSchema),
3458
+ latestRunId: z25.string()
3459
+ });
3460
+ var contentGapRowDtoSchema = z25.object({
3461
+ query: z25.string(),
3462
+ competitorDomains: z25.array(z25.string()),
3463
+ competitorCount: z25.number().int().nonnegative(),
3464
+ missRate: z25.number().min(0).max(1),
3465
+ lastSeenInRunId: z25.string()
3466
+ });
3467
+ var contentGapsResponseDtoSchema = z25.object({
3468
+ gaps: z25.array(contentGapRowDtoSchema),
3469
+ latestRunId: z25.string()
2970
3470
  });
2971
3471
 
2972
3472
  // ../contracts/src/doctor.ts
2973
- import { z as z25 } from "zod";
2974
- var checkStatusSchema = z25.enum(["ok", "warn", "fail", "skipped"]);
3473
+ import { z as z26 } from "zod";
3474
+ var checkStatusSchema = z26.enum(["ok", "warn", "fail", "skipped"]);
2975
3475
  var CheckStatuses = checkStatusSchema.enum;
2976
- var checkScopeSchema = z25.enum(["global", "project"]);
3476
+ var checkScopeSchema = z26.enum(["global", "project"]);
2977
3477
  var CheckScopes = checkScopeSchema.enum;
2978
- var checkCategorySchema = z25.enum([
3478
+ var checkCategorySchema = z26.enum([
2979
3479
  "auth",
2980
3480
  "config",
2981
3481
  "providers",
@@ -2986,31 +3486,31 @@ var checkCategorySchema = z25.enum([
2986
3486
  "agent"
2987
3487
  ]);
2988
3488
  var CheckCategories = checkCategorySchema.enum;
2989
- var checkResultSchema = z25.object({
2990
- id: z25.string(),
3489
+ var checkResultSchema = z26.object({
3490
+ id: z26.string(),
2991
3491
  category: checkCategorySchema,
2992
3492
  scope: checkScopeSchema,
2993
- title: z25.string(),
3493
+ title: z26.string(),
2994
3494
  status: checkStatusSchema,
2995
- code: z25.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
2996
- summary: z25.string(),
2997
- remediation: z25.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
2998
- details: z25.record(z25.string(), z25.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
2999
- durationMs: z25.number().int().nonnegative().describe("How long the check took to execute.")
3495
+ code: z26.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
3496
+ summary: z26.string(),
3497
+ remediation: z26.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
3498
+ details: z26.record(z26.string(), z26.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
3499
+ durationMs: z26.number().int().nonnegative().describe("How long the check took to execute.")
3000
3500
  });
3001
- var doctorReportSchema = z25.object({
3501
+ var doctorReportSchema = z26.object({
3002
3502
  scope: checkScopeSchema,
3003
- project: z25.string().nullable().describe('Project name when scope is "project", null otherwise.'),
3004
- generatedAt: z25.string().describe("ISO-8601 timestamp when this doctor run started."),
3005
- durationMs: z25.number().int().nonnegative(),
3006
- summary: z25.object({
3007
- total: z25.number().int().nonnegative(),
3008
- ok: z25.number().int().nonnegative(),
3009
- warn: z25.number().int().nonnegative(),
3010
- fail: z25.number().int().nonnegative(),
3011
- skipped: z25.number().int().nonnegative()
3503
+ project: z26.string().nullable().describe('Project name when scope is "project", null otherwise.'),
3504
+ generatedAt: z26.string().describe("ISO-8601 timestamp when this doctor run started."),
3505
+ durationMs: z26.number().int().nonnegative(),
3506
+ summary: z26.object({
3507
+ total: z26.number().int().nonnegative(),
3508
+ ok: z26.number().int().nonnegative(),
3509
+ warn: z26.number().int().nonnegative(),
3510
+ fail: z26.number().int().nonnegative(),
3511
+ skipped: z26.number().int().nonnegative()
3012
3512
  }),
3013
- checks: z25.array(checkResultSchema)
3513
+ checks: z26.array(checkResultSchema)
3014
3514
  });
3015
3515
  function summarizeCheckResults(results) {
3016
3516
  const summary = { total: results.length, ok: 0, warn: 0, fail: 0, skipped: 0 };
@@ -3033,186 +3533,53 @@ function summarizeCheckResults(results) {
3033
3533
  return summary;
3034
3534
  }
3035
3535
 
3036
- // ../contracts/src/url-normalize.ts
3037
- var STRIP_KEYS = /* @__PURE__ */ new Set([
3038
- // Click identifiers
3039
- "fbclid",
3040
- "gclid",
3041
- "msclkid",
3042
- "ttclid",
3043
- "li_fat_id",
3044
- "igshid",
3045
- "yclid",
3046
- "dclid",
3047
- "gbraid",
3048
- "wbraid",
3049
- "bingid",
3050
- // Mailchimp
3051
- "mc_cid",
3052
- "mc_eid",
3053
- // Google Analytics linkers
3054
- "_ga",
3055
- "_gl",
3056
- // Google Tag Manager debug
3057
- "gtm_latency",
3058
- "gtm_debug",
3059
- // WordPress internal noise
3060
- "preview",
3061
- "preview_id",
3062
- "preview_nonce",
3063
- "_thumbnail_id",
3064
- // Common cache-busters/versioning
3065
- "v",
3066
- "ver",
3067
- "version"
3068
- ]);
3069
- function shouldStrip(key) {
3070
- if (STRIP_KEYS.has(key)) return true;
3071
- if (key.startsWith("utm_")) return true;
3072
- return false;
3073
- }
3074
- function parseQuery(query) {
3075
- if (query === "") return [];
3076
- return query.split("&").map((pair) => {
3077
- const eq = pair.indexOf("=");
3078
- if (eq === -1) return { key: pair, value: null };
3079
- return { key: pair.slice(0, eq), value: pair.slice(eq + 1) };
3080
- });
3081
- }
3082
- function encodeQuery(pairs) {
3083
- return pairs.map((p) => p.value === null ? p.key : `${p.key}=${p.value}`).join("&");
3084
- }
3085
- function collapseRootIndex(path) {
3086
- if (path === "/index.html" || path === "/index.php") return "/";
3087
- return path;
3088
- }
3089
- function dropTrailingSlash(path) {
3090
- if (path.length > 1 && path.endsWith("/")) {
3091
- return path.replace(/\/+$/, "");
3092
- }
3093
- return path;
3094
- }
3095
- function absolutizeProjectUrl(url, canonicalDomain) {
3096
- if (!url) return "";
3097
- const trimmed = url.trim();
3098
- if (!trimmed) return "";
3099
- if (/^https?:\/\//i.test(trimmed)) return trimmed;
3100
- if (trimmed.startsWith("//")) return `https:${trimmed}`;
3101
- const host = canonicalDomain.trim().replace(/^https?:\/\//i, "").replace(/\/+$/, "");
3102
- if (!host) return trimmed;
3103
- if (trimmed.startsWith("/")) return `https://${host}${trimmed}`;
3104
- return `https://${host}/${trimmed}`;
3105
- }
3106
- function hostOf(value) {
3107
- if (value == null) return null;
3108
- const trimmed = value.trim();
3109
- if (!trimmed) return null;
3110
- try {
3111
- const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
3112
- return url.hostname.replace(/^www\./, "").toLowerCase();
3113
- } catch {
3114
- return null;
3115
- }
3116
- }
3117
- function normalizeUrlPath(input) {
3118
- if (input == null) return null;
3119
- let trimmed = input.trim();
3120
- if (trimmed === "") return null;
3121
- trimmed = trimmed.replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
3122
- if (trimmed === "" || trimmed === "/") return "/";
3123
- if (trimmed === "(not set)") return null;
3124
- trimmed = trimmed.replace(/([a-z0-9])[).]+$/i, "$1");
3125
- if (trimmed.startsWith("/)") || trimmed.startsWith("/ ")) {
3126
- trimmed = "/";
3127
- }
3128
- if (trimmed.includes(" ")) {
3129
- trimmed = trimmed.split(" ")[0];
3130
- }
3131
- if (trimmed === "" || trimmed === "/") return "/";
3132
- let pathPart;
3133
- let queryPart;
3134
- if (/^https?:\/\//i.test(trimmed)) {
3135
- let url;
3136
- try {
3137
- url = new URL(trimmed);
3138
- } catch {
3139
- return null;
3140
- }
3141
- pathPart = url.pathname || "/";
3142
- queryPart = url.search.startsWith("?") ? url.search.slice(1) : url.search;
3143
- } else {
3144
- let raw = trimmed;
3145
- const hashIdx = raw.indexOf("#");
3146
- if (hashIdx !== -1) raw = raw.slice(0, hashIdx);
3147
- const qIdx = raw.indexOf("?");
3148
- if (qIdx === -1) {
3149
- pathPart = raw;
3150
- queryPart = "";
3151
- } else {
3152
- pathPart = raw.slice(0, qIdx);
3153
- queryPart = raw.slice(qIdx + 1);
3154
- }
3155
- }
3156
- if (pathPart === "") pathPart = "/";
3157
- pathPart = collapseRootIndex(pathPart);
3158
- pathPart = dropTrailingSlash(pathPart);
3159
- const pairs = parseQuery(queryPart).filter((p) => !shouldStrip(p.key));
3160
- pairs.sort((a, b) => {
3161
- if (a.key < b.key) return -1;
3162
- if (a.key > b.key) return 1;
3163
- return 0;
3164
- });
3165
- if (pairs.length === 0) return pathPart;
3166
- return `${pathPart}?${encodeQuery(pairs)}`;
3167
- }
3168
-
3169
3536
  // ../contracts/src/citations.ts
3170
- import { z as z26 } from "zod";
3171
- var citationCoverageProviderSchema = z26.object({
3172
- provider: z26.string(),
3537
+ import { z as z27 } from "zod";
3538
+ var citationCoverageProviderSchema = z27.object({
3539
+ provider: z27.string(),
3173
3540
  citationState: citationStateSchema,
3174
- cited: z26.boolean(),
3175
- mentioned: z26.boolean(),
3176
- runId: z26.string(),
3177
- runCreatedAt: z26.string()
3178
- });
3179
- var citationCoverageRowSchema = z26.object({
3180
- queryId: z26.string(),
3181
- query: z26.string(),
3182
- providers: z26.array(citationCoverageProviderSchema),
3183
- citedCount: z26.number().int().nonnegative(),
3184
- mentionedCount: z26.number().int().nonnegative(),
3185
- totalProviders: z26.number().int().nonnegative()
3186
- });
3187
- var competitorGapRowSchema = z26.object({
3188
- queryId: z26.string(),
3189
- query: z26.string(),
3190
- provider: z26.string(),
3191
- citingCompetitors: z26.array(z26.string()),
3192
- runId: z26.string(),
3193
- runCreatedAt: z26.string()
3194
- });
3195
- var citationVisibilitySummarySchema = z26.object({
3196
- providersConfigured: z26.number().int().nonnegative(),
3197
- providersCiting: z26.number().int().nonnegative(),
3198
- providersMentioning: z26.number().int().nonnegative(),
3199
- totalQueries: z26.number().int().nonnegative(),
3541
+ cited: z27.boolean(),
3542
+ mentioned: z27.boolean(),
3543
+ runId: z27.string(),
3544
+ runCreatedAt: z27.string()
3545
+ });
3546
+ var citationCoverageRowSchema = z27.object({
3547
+ queryId: z27.string(),
3548
+ query: z27.string(),
3549
+ providers: z27.array(citationCoverageProviderSchema),
3550
+ citedCount: z27.number().int().nonnegative(),
3551
+ mentionedCount: z27.number().int().nonnegative(),
3552
+ totalProviders: z27.number().int().nonnegative()
3553
+ });
3554
+ var competitorGapRowSchema = z27.object({
3555
+ queryId: z27.string(),
3556
+ query: z27.string(),
3557
+ provider: z27.string(),
3558
+ citingCompetitors: z27.array(z27.string()),
3559
+ runId: z27.string(),
3560
+ runCreatedAt: z27.string()
3561
+ });
3562
+ var citationVisibilitySummarySchema = z27.object({
3563
+ providersConfigured: z27.number().int().nonnegative(),
3564
+ providersCiting: z27.number().int().nonnegative(),
3565
+ providersMentioning: z27.number().int().nonnegative(),
3566
+ totalQueries: z27.number().int().nonnegative(),
3200
3567
  // Cross-tab buckets — each tracked query with at least one snapshot lands
3201
3568
  // in exactly one of these. Queries with zero snapshots are not counted in
3202
3569
  // any bucket; (sum of buckets) ≤ totalQueries.
3203
- queriesCitedAndMentioned: z26.number().int().nonnegative(),
3204
- queriesCitedOnly: z26.number().int().nonnegative(),
3205
- queriesMentionedOnly: z26.number().int().nonnegative(),
3206
- queriesInvisible: z26.number().int().nonnegative(),
3207
- latestRunId: z26.string().nullable(),
3208
- latestRunAt: z26.string().nullable()
3209
- });
3210
- var citationVisibilityResponseSchema = z26.object({
3570
+ queriesCitedAndMentioned: z27.number().int().nonnegative(),
3571
+ queriesCitedOnly: z27.number().int().nonnegative(),
3572
+ queriesMentionedOnly: z27.number().int().nonnegative(),
3573
+ queriesInvisible: z27.number().int().nonnegative(),
3574
+ latestRunId: z27.string().nullable(),
3575
+ latestRunAt: z27.string().nullable()
3576
+ });
3577
+ var citationVisibilityResponseSchema = z27.object({
3211
3578
  summary: citationVisibilitySummarySchema,
3212
- byQuery: z26.array(citationCoverageRowSchema),
3213
- competitorGaps: z26.array(competitorGapRowSchema),
3214
- status: z26.enum(["ready", "no-data"]),
3215
- reason: z26.enum(["no-runs-yet", "no-queries"]).optional()
3579
+ byQuery: z27.array(citationCoverageRowSchema),
3580
+ competitorGaps: z27.array(competitorGapRowSchema),
3581
+ status: z27.enum(["ready", "no-data"]),
3582
+ reason: z27.enum(["no-runs-yet", "no-queries"]).optional()
3216
3583
  });
3217
3584
  function emptyCitationVisibility(reason) {
3218
3585
  return {
@@ -3239,46 +3606,46 @@ function citationStateToCited(state) {
3239
3606
  }
3240
3607
 
3241
3608
  // ../contracts/src/report.ts
3242
- import { z as z27 } from "zod";
3243
- var providerLocationTreatmentSchema = z27.enum([
3609
+ import { z as z28 } from "zod";
3610
+ var providerLocationTreatmentSchema = z28.enum([
3244
3611
  "prompt",
3245
3612
  "request-param",
3246
3613
  "browser-geo",
3247
3614
  "ignored"
3248
3615
  ]);
3249
- var reportMetaLocationSchema = z27.object({
3616
+ var reportMetaLocationSchema = z28.object({
3250
3617
  /** Human-readable label as configured on the project (e.g. "michigan"). */
3251
- label: z27.string(),
3618
+ label: z28.string(),
3252
3619
  /** Resolved city/region/country from the project's `LocationContext`. */
3253
- city: z27.string(),
3254
- region: z27.string(),
3255
- country: z27.string(),
3620
+ city: z28.string(),
3621
+ region: z28.string(),
3622
+ country: z28.string(),
3256
3623
  /**
3257
3624
  * Other locations configured on the project that did NOT power this report.
3258
3625
  * When non-empty, callers should make clear that the report is location-scoped:
3259
3626
  * a separate sweep is needed to see how AI engines respond from each one.
3260
3627
  */
3261
- otherConfiguredLabels: z27.array(z27.string())
3628
+ otherConfiguredLabels: z28.array(z28.string())
3262
3629
  });
3263
- var reportProviderLocationHandlingSchema = z27.object({
3630
+ var reportProviderLocationHandlingSchema = z28.object({
3264
3631
  /** Provider name (matches `query_snapshots.provider`). */
3265
- provider: z27.string(),
3632
+ provider: z28.string(),
3266
3633
  /** How this provider applied the configured location during this run. */
3267
3634
  treatment: providerLocationTreatmentSchema,
3268
3635
  /** One-sentence explanation suitable for the report. */
3269
- description: z27.string()
3636
+ description: z28.string()
3270
3637
  });
3271
- var reportMetaSchema = z27.object({
3638
+ var reportMetaSchema = z28.object({
3272
3639
  /** ISO timestamp the report was generated (server clock). */
3273
- generatedAt: z27.string(),
3640
+ generatedAt: z28.string(),
3274
3641
  /** Project the report covers. */
3275
- project: z27.object({
3276
- id: z27.string(),
3277
- name: z27.string(),
3278
- displayName: z27.string(),
3279
- canonicalDomain: z27.string(),
3280
- country: z27.string(),
3281
- language: z27.string()
3642
+ project: z28.object({
3643
+ id: z28.string(),
3644
+ name: z28.string(),
3645
+ displayName: z28.string(),
3646
+ canonicalDomain: z28.string(),
3647
+ country: z28.string(),
3648
+ language: z28.string()
3282
3649
  }),
3283
3650
  /**
3284
3651
  * The location that powered the latest visibility run, when one was set.
@@ -3293,24 +3660,24 @@ var reportMetaSchema = z27.object({
3293
3660
  * each provider's answer — some providers append it to the prompt, some
3294
3661
  * pass it as a structured request field, and some (CDP) ignore it.
3295
3662
  */
3296
- providerLocationHandling: z27.array(reportProviderLocationHandlingSchema),
3663
+ providerLocationHandling: z28.array(reportProviderLocationHandlingSchema),
3297
3664
  /** Earliest data point referenced by the report (ISO date). */
3298
- periodStart: z27.string().nullable(),
3665
+ periodStart: z28.string().nullable(),
3299
3666
  /** Latest data point referenced by the report (ISO date). */
3300
- periodEnd: z27.string().nullable()
3667
+ periodEnd: z28.string().nullable()
3301
3668
  });
3302
- var reportExecutiveSummarySchema = z27.object({
3669
+ var reportExecutiveSummarySchema = z28.object({
3303
3670
  /**
3304
3671
  * 0..100 — share of tracked queries that were cited by at least one
3305
3672
  * provider in the latest run. "Cited" means the project's domain appeared
3306
3673
  * in the source list / grounding the AI used to answer. Computed per-query
3307
3674
  * (not per-(query × provider)) so the rate is invariant to provider count.
3308
3675
  */
3309
- citationRate: z27.number(),
3676
+ citationRate: z28.number(),
3310
3677
  /** Numerator of `citationRate` — distinct tracked queries cited by ≥1 provider in the latest run. */
3311
- citedQueryCount: z27.number(),
3678
+ citedQueryCount: z28.number(),
3312
3679
  /** Denominator of `citationRate` — total tracked queries. */
3313
- totalQueryCount: z27.number(),
3680
+ totalQueryCount: z28.number(),
3314
3681
  /**
3315
3682
  * 0..100 — share of tracked queries where the project's brand or domain
3316
3683
  * appeared in at least one provider's answer text in the latest run.
@@ -3318,68 +3685,68 @@ var reportExecutiveSummarySchema = z27.object({
3318
3685
  * the prose without citing your domain in its sources, and vice versa.
3319
3686
  * Same per-query denominator as `citationRate` for consistency.
3320
3687
  */
3321
- mentionRate: z27.number(),
3688
+ mentionRate: z28.number(),
3322
3689
  /** Numerator of `mentionRate` — distinct tracked queries mentioned in ≥1 provider's answer text. */
3323
- mentionedQueryCount: z27.number(),
3690
+ mentionedQueryCount: z28.number(),
3324
3691
  /** Compared to the previous run: 'up' | 'down' | 'flat' | 'unknown' (no prior run). */
3325
- trend: z27.enum(["up", "down", "flat", "unknown"]),
3692
+ trend: z28.enum(["up", "down", "flat", "unknown"]),
3326
3693
  /** Total tracked queries. */
3327
- queryCount: z27.number(),
3694
+ queryCount: z28.number(),
3328
3695
  /** Total tracked competitors. */
3329
- competitorCount: z27.number(),
3696
+ competitorCount: z28.number(),
3330
3697
  /** Number of providers in the latest run. */
3331
- providerCount: z27.number(),
3698
+ providerCount: z28.number(),
3332
3699
  /** GSC totals across the most-recent sync window. Null when GSC is not connected. */
3333
- gsc: z27.object({
3334
- clicks: z27.number(),
3335
- impressions: z27.number(),
3336
- ctr: z27.number(),
3337
- avgPosition: z27.number(),
3338
- periodStart: z27.string(),
3339
- periodEnd: z27.string()
3700
+ gsc: z28.object({
3701
+ clicks: z28.number(),
3702
+ impressions: z28.number(),
3703
+ ctr: z28.number(),
3704
+ avgPosition: z28.number(),
3705
+ periodStart: z28.string(),
3706
+ periodEnd: z28.string()
3340
3707
  }).nullable(),
3341
3708
  /** GA4 totals across the most-recent sync period. Null when GA4 is not connected. */
3342
- ga: z27.object({
3343
- sessions: z27.number(),
3344
- users: z27.number(),
3345
- periodStart: z27.string(),
3346
- periodEnd: z27.string()
3709
+ ga: z28.object({
3710
+ sessions: z28.number(),
3711
+ users: z28.number(),
3712
+ periodStart: z28.string(),
3713
+ periodEnd: z28.string()
3347
3714
  }).nullable(),
3348
3715
  /** Top 3-5 findings, each rendered as a single-sentence narrative. */
3349
- findings: z27.array(z27.object({
3350
- title: z27.string(),
3351
- detail: z27.string(),
3352
- tone: z27.enum(["positive", "caution", "negative", "neutral"])
3716
+ findings: z28.array(z28.object({
3717
+ title: z28.string(),
3718
+ detail: z28.string(),
3719
+ tone: z28.enum(["positive", "caution", "negative", "neutral"])
3353
3720
  }))
3354
3721
  });
3355
- var citationCellSchema = z27.object({
3356
- citationState: z27.enum(["cited", "not-cited", "pending"]),
3357
- answerMentioned: z27.boolean().nullable(),
3358
- model: z27.string().nullable()
3722
+ var citationCellSchema = z28.object({
3723
+ citationState: z28.enum(["cited", "not-cited", "pending"]),
3724
+ answerMentioned: z28.boolean().nullable(),
3725
+ model: z28.string().nullable()
3359
3726
  });
3360
- var citationScorecardSchema = z27.object({
3361
- queries: z27.array(z27.string()),
3362
- providers: z27.array(z27.string()),
3727
+ var citationScorecardSchema = z28.object({
3728
+ queries: z28.array(z28.string()),
3729
+ providers: z28.array(z28.string()),
3363
3730
  /** matrix[queryIndex][providerIndex] — null when no snapshot exists for the pair. */
3364
- matrix: z27.array(z27.array(citationCellSchema.nullable())),
3731
+ matrix: z28.array(z28.array(citationCellSchema.nullable())),
3365
3732
  /** Per-provider citation rate (0..100). */
3366
- providerRates: z27.array(z27.object({
3367
- provider: z27.string(),
3368
- citedCount: z27.number(),
3369
- totalCount: z27.number(),
3370
- citationRate: z27.number()
3733
+ providerRates: z28.array(z28.object({
3734
+ provider: z28.string(),
3735
+ citedCount: z28.number(),
3736
+ totalCount: z28.number(),
3737
+ citationRate: z28.number()
3371
3738
  }))
3372
3739
  });
3373
- var competitorRowSchema = z27.object({
3374
- domain: z27.string(),
3740
+ var competitorRowSchema = z28.object({
3741
+ domain: z28.string(),
3375
3742
  /** Number of (query × provider) pairs that cited this competitor. */
3376
- citationCount: z27.number(),
3743
+ citationCount: z28.number(),
3377
3744
  /** Out-of count for the same denominator. */
3378
- totalCount: z27.number(),
3745
+ totalCount: z28.number(),
3379
3746
  /** 'High' | 'Moderate' | 'Low' | 'None' — from buildPortfolioProject pressure logic. */
3380
- pressureLabel: z27.enum(["High", "Moderate", "Low", "None"]),
3747
+ pressureLabel: z28.enum(["High", "Moderate", "Low", "None"]),
3381
3748
  /** Distinct queries on which this competitor was cited. */
3382
- citedQueries: z27.array(z27.string()),
3749
+ citedQueries: z28.array(z28.string()),
3383
3750
  /**
3384
3751
  * Citation share 0..100. Numerator = this competitor's `citationCount`.
3385
3752
  * Denominator = sum of `citationCount` across all competitors plus the
@@ -3387,30 +3754,30 @@ var competitorRowSchema = z27.object({
3387
3754
  * slots in the snapshot. Distinct from the project-level Mention Share
3388
3755
  * gauge — that one is brand-in-answer-text, this one is domain-in-source-list.
3389
3756
  */
3390
- sharePct: z27.number(),
3757
+ sharePct: z28.number(),
3391
3758
  /**
3392
3759
  * URLs from the latest run's grounding sources whose host matches this
3393
3760
  * competitor's domain, with the queries each URL was cited for. Empty
3394
3761
  * when no grounding-source data is available (e.g. no `rawResponse` JSON
3395
3762
  * stored for the snapshots).
3396
3763
  */
3397
- theirCitedPages: z27.array(z27.object({ url: z27.string(), citedFor: z27.array(z27.string()) }))
3764
+ theirCitedPages: z28.array(z28.object({ url: z28.string(), citedFor: z28.array(z28.string()) }))
3398
3765
  });
3399
- var competitorLandscapeSchema = z27.object({
3766
+ var competitorLandscapeSchema = z28.object({
3400
3767
  /** Project's own citation count (for the bar chart comparing project vs competitors). */
3401
- projectCitationCount: z27.number(),
3402
- competitors: z27.array(competitorRowSchema)
3768
+ projectCitationCount: z28.number(),
3769
+ competitors: z28.array(competitorRowSchema)
3403
3770
  });
3404
- var mentionRowSchema = z27.object({
3405
- domain: z27.string(),
3771
+ var mentionRowSchema = z28.object({
3772
+ domain: z28.string(),
3406
3773
  /** Number of (query × provider) pairs whose answer text mentioned this competitor's brand or domain. */
3407
- mentionCount: z27.number(),
3774
+ mentionCount: z28.number(),
3408
3775
  /** Out-of count for the same denominator (snapshots that had answer text). */
3409
- totalCount: z27.number(),
3776
+ totalCount: z28.number(),
3410
3777
  /** 'High' | 'Moderate' | 'Low' | 'None' — mention frequency tier (mirrors CompetitorRow.pressureLabel). */
3411
- pressureLabel: z27.enum(["High", "Moderate", "Low", "None"]),
3778
+ pressureLabel: z28.enum(["High", "Moderate", "Low", "None"]),
3412
3779
  /** Distinct queries on which this competitor was mentioned. */
3413
- mentionedQueries: z27.array(z27.string()),
3780
+ mentionedQueries: z28.array(z28.string()),
3414
3781
  /**
3415
3782
  * Mention share 0..100. Numerator = this competitor's `mentionCount`.
3416
3783
  * Denominator = sum of `mentionCount` across all competitors plus the
@@ -3418,129 +3785,129 @@ var mentionRowSchema = z27.object({
3418
3785
  * mention. Per-competitor split of the same head-to-head measure the
3419
3786
  * project's hero `MentionShareDto` gauge headlines.
3420
3787
  */
3421
- sharePct: z27.number()
3788
+ sharePct: z28.number()
3422
3789
  });
3423
- var mentionLandscapeSchema = z27.object({
3790
+ var mentionLandscapeSchema = z28.object({
3424
3791
  /** Project's own mention count (for the bar chart comparing project vs competitors). */
3425
- projectMentionCount: z27.number(),
3792
+ projectMentionCount: z28.number(),
3426
3793
  /** Snapshots considered — those with non-empty answerText. Drives the totalCount denominator. */
3427
- totalAnswerSnapshots: z27.number(),
3428
- competitors: z27.array(mentionRowSchema)
3794
+ totalAnswerSnapshots: z28.number(),
3795
+ competitors: z28.array(mentionRowSchema)
3429
3796
  });
3430
- var aiSourceCategoryBucketSchema = z27.object({
3797
+ var aiSourceCategoryBucketSchema = z28.object({
3431
3798
  /** Category slug from packages/contracts/src/source-categories. */
3432
- category: z27.string(),
3799
+ category: z28.string(),
3433
3800
  /** Display label. */
3434
- label: z27.string(),
3801
+ label: z28.string(),
3435
3802
  /** Number of citations falling in this category. */
3436
- count: z27.number(),
3803
+ count: z28.number(),
3437
3804
  /** 0..100 share of total citations. */
3438
- sharePct: z27.number()
3805
+ sharePct: z28.number()
3439
3806
  });
3440
- var aiSourceOriginSchema = z27.object({
3441
- categories: z27.array(aiSourceCategoryBucketSchema),
3807
+ var aiSourceOriginSchema = z28.object({
3808
+ categories: z28.array(aiSourceCategoryBucketSchema),
3442
3809
  /** Top 20 source domains by citation count (excluding the project's own domain). */
3443
- topDomains: z27.array(z27.object({
3444
- domain: z27.string(),
3445
- count: z27.number(),
3810
+ topDomains: z28.array(z28.object({
3811
+ domain: z28.string(),
3812
+ count: z28.number(),
3446
3813
  /** True when the domain is one of the project's tracked competitors. */
3447
- isCompetitor: z27.boolean()
3814
+ isCompetitor: z28.boolean()
3448
3815
  }))
3449
3816
  });
3450
- var gscQueryRowSchema = z27.object({
3451
- query: z27.string(),
3452
- clicks: z27.number(),
3453
- impressions: z27.number(),
3454
- ctr: z27.number(),
3455
- avgPosition: z27.number(),
3817
+ var gscQueryRowSchema = z28.object({
3818
+ query: z28.string(),
3819
+ clicks: z28.number(),
3820
+ impressions: z28.number(),
3821
+ ctr: z28.number(),
3822
+ avgPosition: z28.number(),
3456
3823
  /** Heuristic categorization: 'brand' | 'lead-gen' | 'industry' | 'other'. */
3457
- category: z27.enum(["brand", "lead-gen", "industry", "other"])
3458
- });
3459
- var gscSectionSchema = z27.object({
3460
- periodStart: z27.string(),
3461
- periodEnd: z27.string(),
3462
- totalClicks: z27.number(),
3463
- totalImpressions: z27.number(),
3464
- ctr: z27.number(),
3465
- avgPosition: z27.number(),
3466
- topQueries: z27.array(gscQueryRowSchema),
3467
- categoryBreakdown: z27.array(z27.object({
3468
- category: z27.enum(["brand", "lead-gen", "industry", "other"]),
3469
- clicks: z27.number(),
3470
- impressions: z27.number(),
3471
- sharePct: z27.number()
3824
+ category: z28.enum(["brand", "lead-gen", "industry", "other"])
3825
+ });
3826
+ var gscSectionSchema = z28.object({
3827
+ periodStart: z28.string(),
3828
+ periodEnd: z28.string(),
3829
+ totalClicks: z28.number(),
3830
+ totalImpressions: z28.number(),
3831
+ ctr: z28.number(),
3832
+ avgPosition: z28.number(),
3833
+ topQueries: z28.array(gscQueryRowSchema),
3834
+ categoryBreakdown: z28.array(z28.object({
3835
+ category: z28.enum(["brand", "lead-gen", "industry", "other"]),
3836
+ clicks: z28.number(),
3837
+ impressions: z28.number(),
3838
+ sharePct: z28.number()
3472
3839
  })),
3473
- trend: z27.array(z27.object({ date: z27.string(), clicks: z27.number(), impressions: z27.number() })),
3840
+ trend: z28.array(z28.object({ date: z28.string(), clicks: z28.number(), impressions: z28.number() })),
3474
3841
  /**
3475
3842
  * Tracked AEO queries that have no GSC impressions in the report window.
3476
3843
  * Surfaces queries that may not represent real search demand.
3477
3844
  */
3478
- trackedButNoGsc: z27.array(z27.string()),
3845
+ trackedButNoGsc: z28.array(z28.string()),
3479
3846
  /**
3480
3847
  * GSC top queries (sorted by impressions desc) that are not tracked as
3481
3848
  * AEO queries — the candidate set for adding to the AEO project.
3482
3849
  */
3483
- gscButNotTracked: z27.array(z27.string())
3484
- });
3485
- var gaTrafficSectionSchema = z27.object({
3486
- totalSessions: z27.number(),
3487
- totalUsers: z27.number(),
3488
- totalOrganicSessions: z27.number(),
3489
- periodStart: z27.string(),
3490
- periodEnd: z27.string(),
3491
- topLandingPages: z27.array(z27.object({
3492
- page: z27.string(),
3493
- sessions: z27.number(),
3494
- users: z27.number(),
3495
- organicSessions: z27.number()
3850
+ gscButNotTracked: z28.array(z28.string())
3851
+ });
3852
+ var gaTrafficSectionSchema = z28.object({
3853
+ totalSessions: z28.number(),
3854
+ totalUsers: z28.number(),
3855
+ totalOrganicSessions: z28.number(),
3856
+ periodStart: z28.string(),
3857
+ periodEnd: z28.string(),
3858
+ topLandingPages: z28.array(z28.object({
3859
+ page: z28.string(),
3860
+ sessions: z28.number(),
3861
+ users: z28.number(),
3862
+ organicSessions: z28.number()
3496
3863
  })),
3497
- channelBreakdown: z27.array(z27.object({
3498
- channel: z27.string(),
3499
- sessions: z27.number(),
3500
- sharePct: z27.number()
3864
+ channelBreakdown: z28.array(z28.object({
3865
+ channel: z28.string(),
3866
+ sessions: z28.number(),
3867
+ sharePct: z28.number()
3501
3868
  }))
3502
3869
  });
3503
- var socialReferralSectionSchema = z27.object({
3504
- totalSessions: z27.number(),
3505
- organicSessions: z27.number(),
3506
- paidSessions: z27.number(),
3507
- channels: z27.array(z27.object({
3508
- channelGroup: z27.string(),
3509
- sessions: z27.number(),
3510
- sharePct: z27.number()
3870
+ var socialReferralSectionSchema = z28.object({
3871
+ totalSessions: z28.number(),
3872
+ organicSessions: z28.number(),
3873
+ paidSessions: z28.number(),
3874
+ channels: z28.array(z28.object({
3875
+ channelGroup: z28.string(),
3876
+ sessions: z28.number(),
3877
+ sharePct: z28.number()
3511
3878
  })),
3512
- topCampaigns: z27.array(z27.object({
3513
- source: z27.string(),
3514
- medium: z27.string(),
3515
- sessions: z27.number()
3879
+ topCampaigns: z28.array(z28.object({
3880
+ source: z28.string(),
3881
+ medium: z28.string(),
3882
+ sessions: z28.number()
3516
3883
  }))
3517
3884
  });
3518
- var aiReferralSectionSchema = z27.object({
3519
- totalSessions: z27.number(),
3520
- totalUsers: z27.number(),
3521
- bySource: z27.array(z27.object({
3522
- source: z27.string(),
3523
- sessions: z27.number(),
3524
- users: z27.number(),
3525
- sharePct: z27.number()
3885
+ var aiReferralSectionSchema = z28.object({
3886
+ totalSessions: z28.number(),
3887
+ totalUsers: z28.number(),
3888
+ bySource: z28.array(z28.object({
3889
+ source: z28.string(),
3890
+ sessions: z28.number(),
3891
+ users: z28.number(),
3892
+ sharePct: z28.number()
3526
3893
  })),
3527
- trend: z27.array(z27.object({ date: z27.string(), sessions: z27.number() })),
3528
- topLandingPages: z27.array(z27.object({
3529
- page: z27.string(),
3530
- sessions: z27.number(),
3531
- users: z27.number()
3894
+ trend: z28.array(z28.object({ date: z28.string(), sessions: z28.number() })),
3895
+ topLandingPages: z28.array(z28.object({
3896
+ page: z28.string(),
3897
+ sessions: z28.number(),
3898
+ users: z28.number()
3532
3899
  }))
3533
3900
  });
3534
- var serverActivitySectionSchema = z27.object({
3901
+ var serverActivitySectionSchema = z28.object({
3535
3902
  /** ISO8601 inclusive lower bound of the report window (default: 7 days). */
3536
- windowStart: z27.string(),
3903
+ windowStart: z28.string(),
3537
3904
  /** ISO8601 inclusive upper bound. */
3538
- windowEnd: z27.string(),
3539
- hasData: z27.boolean(),
3905
+ windowEnd: z28.string(),
3906
+ hasData: z28.boolean(),
3540
3907
  /** Last-7d total verified crawler hits, with prior 7d for delta. */
3541
- verifiedCrawlerHits: z27.object({ current: z27.number(), prior: z27.number(), deltaPct: z27.number().nullable() }),
3908
+ verifiedCrawlerHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
3542
3909
  /** Last-7d total unverified crawler hits, separated from verified trust metrics. */
3543
- unverifiedCrawlerHits: z27.object({ current: z27.number(), prior: z27.number(), deltaPct: z27.number().nullable() }),
3910
+ unverifiedCrawlerHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
3544
3911
  /**
3545
3912
  * Last-7d on-demand per-user fetches from AI surfaces (ChatGPT-User,
3546
3913
  * Perplexity-User, MistralAI-User). Disjoint from `verifiedCrawlerHits` /
@@ -3549,19 +3916,19 @@ var serverActivitySectionSchema = z27.object({
3549
3916
  * because the operational question for user-fetch is "is this happening?"
3550
3917
  * not "is this a confirmed bot identity?"
3551
3918
  */
3552
- aiUserFetchHits: z27.object({ current: z27.number(), prior: z27.number(), deltaPct: z27.number().nullable() }),
3919
+ aiUserFetchHits: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
3553
3920
  /** Last-7d AI-referral sessions (sessionized from server-side request evidence). */
3554
- referralArrivals: z27.object({ current: z27.number(), prior: z27.number(), deltaPct: z27.number().nullable() }),
3921
+ referralArrivals: z28.object({ current: z28.number(), prior: z28.number(), deltaPct: z28.number().nullable() }),
3555
3922
  /** Per-AI-operator breakdown (OpenAI, Anthropic, Google AI, Perplexity, …). */
3556
- byOperator: z27.array(z27.object({
3557
- operator: z27.string(),
3558
- verifiedHits: z27.number(),
3923
+ byOperator: z28.array(z28.object({
3924
+ operator: z28.string(),
3925
+ verifiedHits: z28.number(),
3559
3926
  /** Shown to agency audience only: claimed-bot UA, source IP not in a published range. */
3560
- unverifiedHits: z27.number(),
3927
+ unverifiedHits: z28.number(),
3561
3928
  /** Per-user fetches from this operator's AI surface (ChatGPT-User, …). */
3562
- userFetchHits: z27.number(),
3563
- referralArrivals: z27.number(),
3564
- deltaPct: z27.number().nullable()
3929
+ userFetchHits: z28.number(),
3930
+ referralArrivals: z28.number(),
3931
+ deltaPct: z28.number().nullable()
3565
3932
  })),
3566
3933
  /**
3567
3934
  * Top crawled paths (verified only, last-7d). Path-level citation cross-reference
@@ -3571,84 +3938,84 @@ var serverActivitySectionSchema = z27.object({
3571
3938
  * citation evidence can extend this entry with a `citationState` field without
3572
3939
  * breaking the contract.
3573
3940
  */
3574
- topCrawledPaths: z27.array(z27.object({
3575
- path: z27.string(),
3576
- verifiedHits: z27.number(),
3941
+ topCrawledPaths: z28.array(z28.object({
3942
+ path: z28.string(),
3943
+ verifiedHits: z28.number(),
3577
3944
  /** How many distinct AI operators crawled this path in the window. */
3578
- distinctOperators: z27.number()
3945
+ distinctOperators: z28.number()
3579
3946
  })),
3580
3947
  /** AI products that sent ≥1 session in the window (referral by destination). */
3581
- referralProducts: z27.array(z27.object({
3582
- product: z27.string(),
3583
- arrivals: z27.number(),
3584
- distinctLandingPaths: z27.number()
3948
+ referralProducts: z28.array(z28.object({
3949
+ product: z28.string(),
3950
+ arrivals: z28.number(),
3951
+ distinctLandingPaths: z28.number()
3585
3952
  })),
3586
3953
  /** Daily trend, last 14d for sparkline / chart rendering. */
3587
- dailyTrend: z27.array(z27.object({
3588
- date: z27.string(),
3589
- verifiedCrawlerHits: z27.number(),
3590
- userFetchHits: z27.number(),
3591
- referralArrivals: z27.number()
3954
+ dailyTrend: z28.array(z28.object({
3955
+ date: z28.string(),
3956
+ verifiedCrawlerHits: z28.number(),
3957
+ userFetchHits: z28.number(),
3958
+ referralArrivals: z28.number()
3592
3959
  })),
3593
3960
  /**
3594
3961
  * Top landing paths for AI-referral sessions (last-7d).
3595
3962
  * Complements `topCrawledPaths` (what bots fetch) with what humans actually land on.
3596
3963
  */
3597
- topReferralLandingPaths: z27.array(z27.object({
3598
- path: z27.string(),
3599
- arrivals: z27.number(),
3600
- distinctProducts: z27.number()
3964
+ topReferralLandingPaths: z28.array(z28.object({
3965
+ path: z28.string(),
3966
+ arrivals: z28.number(),
3967
+ distinctProducts: z28.number()
3601
3968
  }))
3602
3969
  });
3603
- var indexingHealthSectionSchema = z27.object({
3970
+ var indexingHealthSectionSchema = z28.object({
3604
3971
  /** Source: 'google' | 'bing' | null when neither is connected. */
3605
- provider: z27.enum(["google", "bing"]).nullable(),
3606
- total: z27.number(),
3607
- indexed: z27.number(),
3608
- notIndexed: z27.number(),
3972
+ provider: z28.enum(["google", "bing"]).nullable(),
3973
+ total: z28.number(),
3974
+ indexed: z28.number(),
3975
+ notIndexed: z28.number(),
3609
3976
  /** Google-only — pages explicitly marked as deindexed. Bing reports 'unknown' instead. */
3610
- deindexed: z27.number(),
3977
+ deindexed: z28.number(),
3611
3978
  /** Bing-only — pages with no inspection data yet. */
3612
- unknown: z27.number(),
3979
+ unknown: z28.number(),
3613
3980
  /** 0..100. */
3614
- indexedPct: z27.number()
3981
+ indexedPct: z28.number()
3615
3982
  });
3616
- var citationsTrendPointSchema = z27.object({
3983
+ var citationsTrendPointSchema = z28.object({
3617
3984
  /** Run ID — anchor for cross-section linking. */
3618
- runId: z27.string(),
3985
+ runId: z28.string(),
3619
3986
  /** ISO timestamp when the run finished (or createdAt fallback). */
3620
- date: z27.string(),
3987
+ date: z28.string(),
3621
3988
  /**
3622
3989
  * 0..100 — same per-query unique-cited definition as
3623
3990
  * `ReportExecutiveSummary.citationRate`. Stable across runs with different
3624
3991
  * provider counts so the trend line measures real movement rather than
3625
3992
  * provider-count variance.
3626
3993
  */
3627
- citationRate: z27.number(),
3994
+ citationRate: z28.number(),
3628
3995
  /** Numerator of `citationRate` for this run. */
3629
- citedQueryCount: z27.number(),
3996
+ citedQueryCount: z28.number(),
3630
3997
  /** Denominator of `citationRate` for this run. */
3631
- totalQueryCount: z27.number(),
3998
+ totalQueryCount: z28.number(),
3632
3999
  /** 0..100 — same per-query unique-mentioned definition as `ReportExecutiveSummary.mentionRate`. */
3633
- mentionRate: z27.number(),
4000
+ mentionRate: z28.number(),
3634
4001
  /** Numerator of `mentionRate` for this run. */
3635
- mentionedQueryCount: z27.number(),
4002
+ mentionedQueryCount: z28.number(),
3636
4003
  /**
3637
4004
  * Per-provider rates for the same run. Each provider's rate is per-pair
3638
4005
  * within that provider (`cited / scanned`), so it remains comparable
3639
4006
  * between providers in the same run.
3640
4007
  */
3641
- providerRates: z27.array(z27.object({ provider: z27.string(), citationRate: z27.number() }))
3642
- });
3643
- var reportInsightSchema = z27.object({
3644
- id: z27.string(),
3645
- type: z27.enum(["regression", "gain", "opportunity"]),
3646
- severity: z27.enum(["critical", "high", "medium", "low"]),
3647
- title: z27.string(),
3648
- query: z27.string(),
3649
- provider: z27.string(),
3650
- recommendation: z27.string().nullable(),
3651
- createdAt: z27.string(),
4008
+ providerRates: z28.array(z28.object({ provider: z28.string(), citationRate: z28.number() }))
4009
+ });
4010
+ var reportInsightSchema = z28.object({
4011
+ id: z28.string(),
4012
+ type: z28.enum(["regression", "gain", "opportunity"]),
4013
+ severity: z28.enum(["critical", "high", "medium", "low"]),
4014
+ title: z28.string(),
4015
+ query: z28.string(),
4016
+ provider: z28.string(),
4017
+ recommendation: z28.string().nullable(),
4018
+ createdAt: z28.string(),
3652
4019
  /**
3653
4020
  * How many times this insight fired across recent runs for the same
3654
4021
  * `(query, provider, type)` tuple. Always ≥ 1. Insights returned by the
@@ -3656,57 +4023,57 @@ var reportInsightSchema = z27.object({
3656
4023
  * surfacing the multiplicity. Use it directly instead of grouping again
3657
4024
  * client-side — counts derived from raw insight rows will overcount.
3658
4025
  */
3659
- instanceCount: z27.number()
4026
+ instanceCount: z28.number()
3660
4027
  });
3661
- var recommendedNextStepSchema = z27.object({
4028
+ var recommendedNextStepSchema = z28.object({
3662
4029
  /** 'immediate' | 'short-term' | 'medium-term' — bucketed by severity heuristic. */
3663
- horizon: z27.enum(["immediate", "short-term", "medium-term"]),
3664
- title: z27.string(),
3665
- rationale: z27.string()
4030
+ horizon: z28.enum(["immediate", "short-term", "medium-term"]),
4031
+ title: z28.string(),
4032
+ rationale: z28.string()
3666
4033
  });
3667
- var reportRateDeltaSchema = z27.object({
4034
+ var reportRateDeltaSchema = z28.object({
3668
4035
  /** Current value (0..100 for rates, raw count otherwise). When `window`
3669
4036
  * is present this is the average over the last `window` checks. */
3670
- current: z27.number(),
4037
+ current: z28.number(),
3671
4038
  /** Prior value compared against. When `window` is present this is the
3672
4039
  * average over the prior `window` checks before that. */
3673
- prior: z27.number(),
4040
+ prior: z28.number(),
3674
4041
  /** Absolute delta (current − prior). Negative = decrease. */
3675
- deltaAbs: z27.number(),
4042
+ deltaAbs: z28.number(),
3676
4043
  /**
3677
4044
  * Direction tag for tone mapping. Threshold is metric-specific (3pp for
3678
4045
  * rates, 0.5 for counts) so small noise lands as 'flat' rather than
3679
4046
  * flipping up/down each run.
3680
4047
  */
3681
- direction: z27.enum(["up", "down", "flat"]),
4048
+ direction: z28.enum(["up", "down", "flat"]),
3682
4049
  /**
3683
4050
  * How many points went into each side of the average. Omitted (or 1)
3684
4051
  * means point-to-point (legacy "since last check"). Higher values mean
3685
4052
  * a rolling-average comparison — renderers should label it as
3686
4053
  * "vs prior N checks" when this is ≥ 2.
3687
4054
  */
3688
- window: z27.number().optional()
4055
+ window: z28.number().optional()
3689
4056
  });
3690
- var reportProviderMovementSchema = z27.object({
3691
- provider: z27.string(),
3692
- current: z27.number(),
3693
- prior: z27.number(),
3694
- deltaAbs: z27.number(),
3695
- direction: z27.enum(["up", "down", "flat"])
4057
+ var reportProviderMovementSchema = z28.object({
4058
+ provider: z28.string(),
4059
+ current: z28.number(),
4060
+ prior: z28.number(),
4061
+ deltaAbs: z28.number(),
4062
+ direction: z28.enum(["up", "down", "flat"])
3696
4063
  });
3697
- var whatsChangedSectionSchema = z27.object({
4064
+ var whatsChangedSectionSchema = z28.object({
3698
4065
  /**
3699
4066
  * False when there's no prior run (or fewer than the trend baseline),
3700
4067
  * meaning all per-metric deltas will be null. Renderers use this to swap
3701
4068
  * in a "establishing baseline" fallback rather than rendering empty
3702
4069
  * delta tiles.
3703
4070
  */
3704
- enoughHistory: z27.boolean(),
4071
+ enoughHistory: z28.boolean(),
3705
4072
  /**
3706
4073
  * One-sentence narrative summary suitable as a section subtitle.
3707
4074
  * Always present — even on baseline, narrates whatever signal exists.
3708
4075
  */
3709
- headline: z27.string(),
4076
+ headline: z28.string(),
3710
4077
  /** Citation rate delta vs the prior completed run. Null when no prior run. */
3711
4078
  citationRate: reportRateDeltaSchema.nullable(),
3712
4079
  /** Mention rate delta vs the prior completed run. Null when no prior run. */
@@ -3729,24 +4096,24 @@ var whatsChangedSectionSchema = z27.object({
3729
4096
  * when no prior run. Sorted by |deltaAbs| desc — providers with the
3730
4097
  * biggest swing first.
3731
4098
  */
3732
- providerMovements: z27.array(reportProviderMovementSchema),
4099
+ providerMovements: z28.array(reportProviderMovementSchema),
3733
4100
  /**
3734
4101
  * Top wins this period — gains surfaced by the intelligence engine.
3735
4102
  * Capped at 5; sourced from `insights` filtered to `type: 'gain'`.
3736
4103
  */
3737
- wins: z27.array(reportInsightSchema),
4104
+ wins: z28.array(reportInsightSchema),
3738
4105
  /**
3739
4106
  * Top regressions this period — citations or mentions lost. Capped at 5;
3740
4107
  * sourced from `insights` filtered to `type: 'regression'`.
3741
4108
  */
3742
- regressions: z27.array(reportInsightSchema)
3743
- });
3744
- var reportAudienceSchema = z27.enum(["agency", "client"]);
3745
- var reportActionAudienceSchema = z27.enum(["agency", "client", "both"]);
3746
- var reportActionHorizonSchema = z27.enum(["immediate", "short-term", "medium-term"]);
3747
- var reportActionConfidenceSchema = z27.enum(["high", "medium", "low"]);
3748
- var reportToneSchema = z27.enum(["positive", "caution", "negative", "neutral"]);
3749
- var reportActionCategorySchema = z27.enum([
4109
+ regressions: z28.array(reportInsightSchema)
4110
+ });
4111
+ var reportAudienceSchema = z28.enum(["agency", "client"]);
4112
+ var reportActionAudienceSchema = z28.enum(["agency", "client", "both"]);
4113
+ var reportActionHorizonSchema = z28.enum(["immediate", "short-term", "medium-term"]);
4114
+ var reportActionConfidenceSchema = z28.enum(["high", "medium", "low"]);
4115
+ var reportToneSchema = z28.enum(["positive", "caution", "negative", "neutral"]);
4116
+ var reportActionCategorySchema = z28.enum([
3750
4117
  "content",
3751
4118
  "competitors",
3752
4119
  "provider",
@@ -3755,23 +4122,23 @@ var reportActionCategorySchema = z27.enum([
3755
4122
  "location",
3756
4123
  "monitoring"
3757
4124
  ]);
3758
- var reportActionPlanItemSchema = z27.object({
4125
+ var reportActionPlanItemSchema = z28.object({
3759
4126
  /** Which report audience should see this action. `both` renders in both modes. */
3760
4127
  audience: reportActionAudienceSchema,
3761
4128
  /** Stable sort priority. Lower numbers render earlier. */
3762
- priority: z27.number(),
4129
+ priority: z28.number(),
3763
4130
  /** When this should be tackled. */
3764
4131
  horizon: reportActionHorizonSchema,
3765
4132
  category: reportActionCategorySchema,
3766
- title: z27.string(),
4133
+ title: z28.string(),
3767
4134
  /** Direct next step written as an operator/client-friendly imperative. */
3768
- action: z27.string(),
4135
+ action: z28.string(),
3769
4136
  /** Why this matters. Keep each entry concise and evidence-backed. */
3770
- why: z27.array(z27.string()),
4137
+ why: z28.array(z28.string()),
3771
4138
  /** Specific observations that justify the action. */
3772
- evidence: z27.array(z27.string()),
4139
+ evidence: z28.array(z28.string()),
3773
4140
  /** What should move if the action worked. */
3774
- successMetric: z27.string(),
4141
+ successMetric: z28.string(),
3775
4142
  /** Confidence in the recommendation based on the available evidence. */
3776
4143
  confidence: reportActionConfidenceSchema,
3777
4144
  /**
@@ -3783,23 +4150,23 @@ var reportActionPlanItemSchema = z27.object({
3783
4150
  * load. Actions sourced from other signals (competitor gaps, indexing
3784
4151
  * issues, etc.) omit this and use their own dismiss flows.
3785
4152
  */
3786
- targetRef: z27.string().optional()
4153
+ targetRef: z28.string().optional()
3787
4154
  });
3788
- var reportClientSummarySchema = z27.object({
3789
- headline: z27.string(),
3790
- overview: z27.string(),
3791
- actionItems: z27.array(reportActionPlanItemSchema),
3792
- confidenceNotes: z27.array(z27.string())
4155
+ var reportClientSummarySchema = z28.object({
4156
+ headline: z28.string(),
4157
+ overview: z28.string(),
4158
+ actionItems: z28.array(reportActionPlanItemSchema),
4159
+ confidenceNotes: z28.array(z28.string())
3793
4160
  });
3794
- var reportAgencyDiagnosticSchema = z27.object({
3795
- title: z27.string(),
3796
- detail: z27.string(),
3797
- severity: z27.enum(["positive", "caution", "negative", "neutral"]),
3798
- evidence: z27.array(z27.string())
4161
+ var reportAgencyDiagnosticSchema = z28.object({
4162
+ title: z28.string(),
4163
+ detail: z28.string(),
4164
+ severity: z28.enum(["positive", "caution", "negative", "neutral"]),
4165
+ evidence: z28.array(z28.string())
3799
4166
  });
3800
- var reportAgencyDiagnosticsSchema = z27.object({
3801
- priorities: z27.array(reportActionPlanItemSchema),
3802
- diagnostics: z27.array(reportAgencyDiagnosticSchema)
4167
+ var reportAgencyDiagnosticsSchema = z28.object({
4168
+ priorities: z28.array(reportActionPlanItemSchema),
4169
+ diagnostics: z28.array(reportAgencyDiagnosticSchema)
3803
4170
  });
3804
4171
  function reportActionTone(action) {
3805
4172
  if (action.horizon === "immediate") return "negative";
@@ -3857,7 +4224,7 @@ function reportConfidenceLabel(confidence) {
3857
4224
  return "Low";
3858
4225
  }
3859
4226
  }
3860
- var projectReportDtoSchema = z27.object({
4227
+ var projectReportDtoSchema = z28.object({
3861
4228
  meta: reportMetaSchema,
3862
4229
  executiveSummary: reportExecutiveSummarySchema,
3863
4230
  citationScorecard: citationScorecardSchema,
@@ -3871,16 +4238,16 @@ var projectReportDtoSchema = z27.object({
3871
4238
  /** Server-side log-evidence visibility (crawls + click-through sessions). Null when no traffic source connected. */
3872
4239
  serverActivity: serverActivitySectionSchema.nullable(),
3873
4240
  indexingHealth: indexingHealthSectionSchema.nullable(),
3874
- citationsTrend: z27.array(citationsTrendPointSchema),
4241
+ citationsTrend: z28.array(citationsTrendPointSchema),
3875
4242
  /**
3876
4243
  * Trend-focused "what's changed" summary for the report's act 2. Always
3877
4244
  * present; renderers gate empty/baseline states via `enoughHistory`.
3878
4245
  */
3879
4246
  whatsChanged: whatsChangedSectionSchema,
3880
- insights: z27.array(reportInsightSchema),
3881
- recommendedNextSteps: z27.array(recommendedNextStepSchema),
4247
+ insights: z28.array(reportInsightSchema),
4248
+ recommendedNextSteps: z28.array(recommendedNextStepSchema),
3882
4249
  /** Canonical structured actions shared by the client and agency render modes. */
3883
- actionPlan: z27.array(reportActionPlanItemSchema),
4250
+ actionPlan: z28.array(reportActionPlanItemSchema),
3884
4251
  /** Polished client-facing summary and action shortlist. */
3885
4252
  clientSummary: reportClientSummarySchema,
3886
4253
  /** Technical, evidence-oriented operator diagnostics for agency mode. */
@@ -3890,17 +4257,17 @@ var projectReportDtoSchema = z27.object({
3890
4257
  * intelligence layer (`buildContentTargetRows`). Empty when no run has
3891
4258
  * produced candidate queries with demand or competitor signal.
3892
4259
  */
3893
- contentOpportunities: z27.array(contentTargetRowDtoSchema),
4260
+ contentOpportunities: z28.array(contentTargetRowDtoSchema),
3894
4261
  /**
3895
4262
  * Queries where competitors were cited but the project was not. Sourced
3896
4263
  * from `buildContentGapRows`. Empty until the first answer-visibility run.
3897
4264
  */
3898
- contentGaps: z27.array(contentGapRowDtoSchema),
4265
+ contentGaps: z28.array(contentGapRowDtoSchema),
3899
4266
  /**
3900
4267
  * Per-query grounding source map (own + competitor cited URLs). Sourced
3901
4268
  * from `buildContentSourceRows`. Empty until the first answer-visibility run.
3902
4269
  */
3903
- groundingSources: z27.array(contentSourceRowDtoSchema)
4270
+ groundingSources: z28.array(contentSourceRowDtoSchema)
3904
4271
  });
3905
4272
 
3906
4273
  // ../contracts/src/report-dedup.ts
@@ -3971,10 +4338,10 @@ function dedupeReportOpportunities(report) {
3971
4338
  }
3972
4339
 
3973
4340
  // ../contracts/src/skills.ts
3974
- import { z as z28 } from "zod";
3975
- var codingAgentSchema = z28.enum(["claude", "codex"]);
4341
+ import { z as z29 } from "zod";
4342
+ var codingAgentSchema = z29.enum(["claude", "codex"]);
3976
4343
  var CodingAgents = codingAgentSchema.enum;
3977
- var skillsClientSchema = z28.enum(["claude", "codex", "all"]);
4344
+ var skillsClientSchema = z29.enum(["claude", "codex", "all"]);
3978
4345
  var SkillsClients = skillsClientSchema.enum;
3979
4346
  var SKILL_MANIFEST_FILENAME = ".canonry-skill-manifest.json";
3980
4347
  function classifySkillFile(params) {
@@ -3992,8 +4359,8 @@ function coerceSkillManifest(parsed) {
3992
4359
  }
3993
4360
 
3994
4361
  // ../contracts/src/traffic.ts
3995
- import { z as z29 } from "zod";
3996
- var trafficSourceTypeSchema = z29.enum([
4362
+ import { z as z30 } from "zod";
4363
+ var trafficSourceTypeSchema = z30.enum([
3997
4364
  "cloud-run",
3998
4365
  "wordpress",
3999
4366
  "cloudflare",
@@ -4001,7 +4368,7 @@ var trafficSourceTypeSchema = z29.enum([
4001
4368
  "generic-log"
4002
4369
  ]);
4003
4370
  var TrafficSourceTypes = trafficSourceTypeSchema.enum;
4004
- var trafficAdapterCapabilitySchema = z29.enum([
4371
+ var trafficAdapterCapabilitySchema = z30.enum([
4005
4372
  "raw-request-events",
4006
4373
  "aggregate-request-metrics",
4007
4374
  "request-url",
@@ -4012,285 +4379,216 @@ var trafficAdapterCapabilitySchema = z29.enum([
4012
4379
  "cursor-pull"
4013
4380
  ]);
4014
4381
  var TrafficAdapterCapabilities = trafficAdapterCapabilitySchema.enum;
4015
- var trafficEvidenceKindSchema = z29.enum(["raw-request", "aggregate-bucket"]);
4382
+ var trafficEvidenceKindSchema = z30.enum(["raw-request", "aggregate-bucket"]);
4016
4383
  var TrafficEvidenceKinds = trafficEvidenceKindSchema.enum;
4017
- var trafficEventConfidenceSchema = z29.enum(["observed", "provider-aggregated", "inferred"]);
4384
+ var trafficEventConfidenceSchema = z30.enum(["observed", "provider-aggregated", "inferred"]);
4018
4385
  var TrafficEventConfidences = trafficEventConfidenceSchema.enum;
4019
- var trafficProviderResourceSchema = z29.object({
4020
- type: z29.string().nullable(),
4021
- labels: z29.record(z29.string(), z29.string())
4386
+ var trafficProviderResourceSchema = z30.object({
4387
+ type: z30.string().nullable(),
4388
+ labels: z30.record(z30.string(), z30.string())
4022
4389
  });
4023
- var normalizedTrafficRequestSchema = z29.object({
4390
+ var normalizedTrafficRequestSchema = z30.object({
4024
4391
  sourceType: trafficSourceTypeSchema,
4025
- evidenceKind: z29.literal(TrafficEvidenceKinds["raw-request"]),
4026
- confidence: z29.literal(TrafficEventConfidences.observed),
4027
- eventId: z29.string().min(1),
4028
- observedAt: z29.string().min(1),
4029
- method: z29.string().nullable(),
4030
- requestUrl: z29.string().nullable(),
4031
- host: z29.string().nullable(),
4032
- path: z29.string().min(1),
4033
- queryString: z29.string().nullable(),
4034
- status: z29.number().int().nullable(),
4035
- userAgent: z29.string().nullable(),
4036
- remoteIp: z29.string().nullable(),
4037
- referer: z29.string().nullable(),
4038
- latencyMs: z29.number().nullable(),
4039
- requestSizeBytes: z29.number().int().nullable(),
4040
- responseSizeBytes: z29.number().int().nullable(),
4392
+ evidenceKind: z30.literal(TrafficEvidenceKinds["raw-request"]),
4393
+ confidence: z30.literal(TrafficEventConfidences.observed),
4394
+ eventId: z30.string().min(1),
4395
+ observedAt: z30.string().min(1),
4396
+ method: z30.string().nullable(),
4397
+ requestUrl: z30.string().nullable(),
4398
+ host: z30.string().nullable(),
4399
+ path: z30.string().min(1),
4400
+ queryString: z30.string().nullable(),
4401
+ status: z30.number().int().nullable(),
4402
+ userAgent: z30.string().nullable(),
4403
+ remoteIp: z30.string().nullable(),
4404
+ referer: z30.string().nullable(),
4405
+ latencyMs: z30.number().nullable(),
4406
+ requestSizeBytes: z30.number().int().nullable(),
4407
+ responseSizeBytes: z30.number().int().nullable(),
4041
4408
  providerResource: trafficProviderResourceSchema,
4042
- providerLabels: z29.record(z29.string(), z29.string())
4409
+ providerLabels: z30.record(z30.string(), z30.string())
4043
4410
  });
4044
- var normalizedTrafficPullPageSchema = z29.object({
4045
- events: z29.array(normalizedTrafficRequestSchema),
4046
- rawEntryCount: z29.number().int().nonnegative(),
4047
- skippedEntryCount: z29.number().int().nonnegative(),
4048
- nextPageToken: z29.string().optional(),
4049
- filter: z29.string()
4411
+ var normalizedTrafficPullPageSchema = z30.object({
4412
+ events: z30.array(normalizedTrafficRequestSchema),
4413
+ rawEntryCount: z30.number().int().nonnegative(),
4414
+ skippedEntryCount: z30.number().int().nonnegative(),
4415
+ nextPageToken: z30.string().optional(),
4416
+ filter: z30.string()
4050
4417
  });
4051
- var trafficSourceStatusSchema = z29.enum(["connected", "paused", "error", "archived"]);
4418
+ var trafficSourceStatusSchema = z30.enum(["connected", "paused", "error", "archived"]);
4052
4419
  var TrafficSourceStatuses = trafficSourceStatusSchema.enum;
4053
- var trafficSourceAuthModeSchema = z29.enum(["oauth", "service-account"]);
4420
+ var trafficSourceAuthModeSchema = z30.enum(["oauth", "service-account"]);
4054
4421
  var TrafficSourceAuthModes = trafficSourceAuthModeSchema.enum;
4055
- var verificationStatusSchema = z29.enum(["verified", "claimed_unverified", "unknown_ai_like"]);
4422
+ var verificationStatusSchema = z30.enum(["verified", "claimed_unverified", "unknown_ai_like"]);
4056
4423
  var VerificationStatuses = verificationStatusSchema.enum;
4057
- var cloudRunSourceConfigSchema = z29.object({
4058
- gcpProjectId: z29.string().min(1),
4059
- serviceName: z29.string().nullable().optional(),
4060
- location: z29.string().nullable().optional(),
4424
+ var cloudRunSourceConfigSchema = z30.object({
4425
+ gcpProjectId: z30.string().min(1),
4426
+ serviceName: z30.string().nullable().optional(),
4427
+ location: z30.string().nullable().optional(),
4061
4428
  authMode: trafficSourceAuthModeSchema
4062
4429
  });
4063
- var wordpressTrafficSourceConfigSchema = z29.object({
4064
- baseUrl: z29.string().url(),
4065
- username: z29.string().min(1)
4430
+ var wordpressTrafficSourceConfigSchema = z30.object({
4431
+ baseUrl: z30.string().url(),
4432
+ username: z30.string().min(1)
4066
4433
  });
4067
- var vercelTrafficEnvironmentSchema = z29.enum(["production", "preview"]);
4434
+ var vercelTrafficEnvironmentSchema = z30.enum(["production", "preview"]);
4068
4435
  var VercelTrafficEnvironments = vercelTrafficEnvironmentSchema.enum;
4069
- var vercelTrafficSourceConfigSchema = z29.object({
4436
+ var vercelTrafficSourceConfigSchema = z30.object({
4070
4437
  /** Vercel project id (e.g. `prj_...`). */
4071
- projectId: z29.string().min(1),
4438
+ projectId: z30.string().min(1),
4072
4439
  /** Vercel team or account id: the org that owns the project. */
4073
- teamId: z29.string().min(1),
4440
+ teamId: z30.string().min(1),
4074
4441
  environment: vercelTrafficEnvironmentSchema
4075
4442
  });
4076
- var trafficSourceDtoSchema = z29.object({
4077
- id: z29.string(),
4078
- projectId: z29.string(),
4443
+ var trafficSourceDtoSchema = z30.object({
4444
+ id: z30.string(),
4445
+ projectId: z30.string(),
4079
4446
  sourceType: trafficSourceTypeSchema,
4080
- displayName: z29.string(),
4447
+ displayName: z30.string(),
4081
4448
  status: trafficSourceStatusSchema,
4082
- lastSyncedAt: z29.string().nullable(),
4083
- lastCursor: z29.string().nullable(),
4084
- lastError: z29.string().nullable(),
4085
- archivedAt: z29.string().nullable(),
4086
- config: z29.record(z29.string(), z29.unknown()),
4087
- createdAt: z29.string(),
4088
- updatedAt: z29.string()
4089
- });
4090
- var trafficConnectCloudRunRequestSchema = z29.object({
4091
- gcpProjectId: z29.string().min(1),
4092
- serviceName: z29.string().min(1).optional(),
4093
- location: z29.string().min(1).optional(),
4094
- displayName: z29.string().min(1).optional(),
4449
+ lastSyncedAt: z30.string().nullable(),
4450
+ lastCursor: z30.string().nullable(),
4451
+ lastError: z30.string().nullable(),
4452
+ archivedAt: z30.string().nullable(),
4453
+ config: z30.record(z30.string(), z30.unknown()),
4454
+ createdAt: z30.string(),
4455
+ updatedAt: z30.string()
4456
+ });
4457
+ var trafficConnectCloudRunRequestSchema = z30.object({
4458
+ gcpProjectId: z30.string().min(1),
4459
+ serviceName: z30.string().min(1).optional(),
4460
+ location: z30.string().min(1).optional(),
4461
+ displayName: z30.string().min(1).optional(),
4095
4462
  /** Service-account JSON content (string). When omitted, defaults to OAuth via `canonry google connect <project> --type ga4` flow. */
4096
- keyJson: z29.string().optional()
4463
+ keyJson: z30.string().optional()
4097
4464
  });
4098
- var trafficConnectWordpressRequestSchema = z29.object({
4099
- baseUrl: z29.string().url(),
4100
- username: z29.string().min(1),
4465
+ var trafficConnectWordpressRequestSchema = z30.object({
4466
+ baseUrl: z30.string().url(),
4467
+ username: z30.string().min(1),
4101
4468
  /** WordPress Application Password (the same auth used by the content client). */
4102
- applicationPassword: z29.string().min(1),
4103
- displayName: z29.string().min(1).optional()
4469
+ applicationPassword: z30.string().min(1),
4470
+ displayName: z30.string().min(1).optional()
4104
4471
  });
4105
- var trafficConnectVercelRequestSchema = z29.object({
4472
+ var trafficConnectVercelRequestSchema = z30.object({
4106
4473
  /** Vercel project id (e.g. `prj_...`) — from the Vercel dashboard or `.vercel/project.json`. */
4107
- projectId: z29.string().min(1),
4474
+ projectId: z30.string().min(1),
4108
4475
  /** Vercel team or account id: the org that owns the project ("orgId" in .vercel/project.json). */
4109
- teamId: z29.string().min(1),
4476
+ teamId: z30.string().min(1),
4110
4477
  /** Vercel personal access token. Stored in `~/.canonry/config.yaml`, never the DB. */
4111
- token: z29.string().min(1),
4478
+ token: z30.string().min(1),
4112
4479
  /** Which deployment environment's request logs to pull. Default: `production`. */
4113
4480
  environment: vercelTrafficEnvironmentSchema.optional(),
4114
- displayName: z29.string().min(1).optional()
4481
+ displayName: z30.string().min(1).optional()
4115
4482
  });
4116
- var trafficSyncResponseSchema = z29.object({
4117
- sourceId: z29.string(),
4118
- runId: z29.string(),
4119
- syncedAt: z29.string(),
4120
- pulledEvents: z29.number().int().nonnegative(),
4483
+ var trafficSyncResponseSchema = z30.object({
4484
+ sourceId: z30.string(),
4485
+ runId: z30.string(),
4486
+ syncedAt: z30.string(),
4487
+ pulledEvents: z30.number().int().nonnegative(),
4121
4488
  /** Self-traffic events (Canonry's own tooling) dropped before rollup. */
4122
- selfTrafficExcluded: z29.number().int().nonnegative(),
4123
- crawlerHits: z29.number().int().nonnegative(),
4124
- aiUserFetchHits: z29.number().int().nonnegative(),
4125
- aiReferralHits: z29.number().int().nonnegative(),
4126
- unknownHits: z29.number().int().nonnegative(),
4127
- crawlerBucketRows: z29.number().int().nonnegative(),
4128
- aiUserFetchBucketRows: z29.number().int().nonnegative(),
4129
- aiReferralBucketRows: z29.number().int().nonnegative(),
4130
- sampleRows: z29.number().int().nonnegative(),
4131
- windowStart: z29.string(),
4132
- windowEnd: z29.string()
4133
- });
4134
- var trafficBackfillRequestSchema = z29.object({
4489
+ selfTrafficExcluded: z30.number().int().nonnegative(),
4490
+ crawlerHits: z30.number().int().nonnegative(),
4491
+ aiUserFetchHits: z30.number().int().nonnegative(),
4492
+ aiReferralHits: z30.number().int().nonnegative(),
4493
+ unknownHits: z30.number().int().nonnegative(),
4494
+ crawlerBucketRows: z30.number().int().nonnegative(),
4495
+ aiUserFetchBucketRows: z30.number().int().nonnegative(),
4496
+ aiReferralBucketRows: z30.number().int().nonnegative(),
4497
+ sampleRows: z30.number().int().nonnegative(),
4498
+ windowStart: z30.string(),
4499
+ windowEnd: z30.string()
4500
+ });
4501
+ var trafficBackfillRequestSchema = z30.object({
4135
4502
  /** Lookback window in days. Capped server-side at the upstream log retention ceiling (Cloud Logging _Default = 30d). Default: 30. */
4136
- days: z29.number().int().positive().optional()
4503
+ days: z30.number().int().positive().optional()
4137
4504
  });
4138
- var trafficResetRequestSchema = z29.object({
4139
- advanceToNow: z29.literal(true)
4505
+ var trafficResetRequestSchema = z30.object({
4506
+ advanceToNow: z30.literal(true)
4140
4507
  });
4141
- var trafficBackfillResponseSchema = z29.object({
4142
- sourceId: z29.string(),
4143
- runId: z29.string(),
4508
+ var trafficBackfillResponseSchema = z30.object({
4509
+ sourceId: z30.string(),
4510
+ runId: z30.string(),
4144
4511
  status: runStatusSchema,
4145
- windowStart: z29.string(),
4146
- windowEnd: z29.string(),
4512
+ windowStart: z30.string(),
4513
+ windowEnd: z30.string(),
4147
4514
  /** Days actually used after server-side clamping (≤ requested). */
4148
- daysRequested: z29.number().int().positive(),
4149
- daysApplied: z29.number().int().positive()
4515
+ daysRequested: z30.number().int().positive(),
4516
+ daysApplied: z30.number().int().positive()
4150
4517
  });
4151
- var trafficSourceTotalsSchema = z29.object({
4152
- crawlerHits: z29.number().int().nonnegative(),
4153
- aiUserFetchHits: z29.number().int().nonnegative(),
4154
- aiReferralHits: z29.number().int().nonnegative(),
4155
- sampleCount: z29.number().int().nonnegative()
4518
+ var trafficSourceTotalsSchema = z30.object({
4519
+ crawlerHits: z30.number().int().nonnegative(),
4520
+ aiUserFetchHits: z30.number().int().nonnegative(),
4521
+ aiReferralHits: z30.number().int().nonnegative(),
4522
+ sampleCount: z30.number().int().nonnegative()
4156
4523
  });
4157
- var trafficSourceListResponseSchema = z29.object({
4158
- sources: z29.array(trafficSourceDtoSchema)
4524
+ var trafficSourceListResponseSchema = z30.object({
4525
+ sources: z30.array(trafficSourceDtoSchema)
4159
4526
  });
4160
4527
  var trafficSourceDetailDtoSchema = trafficSourceDtoSchema.extend({
4161
4528
  totals24h: trafficSourceTotalsSchema,
4162
- latestRun: z29.object({
4163
- runId: z29.string(),
4529
+ latestRun: z30.object({
4530
+ runId: z30.string(),
4164
4531
  status: runStatusSchema,
4165
- startedAt: z29.string().nullable(),
4166
- finishedAt: z29.string().nullable(),
4167
- error: z29.string().nullable()
4532
+ startedAt: z30.string().nullable(),
4533
+ finishedAt: z30.string().nullable(),
4534
+ error: z30.string().nullable()
4168
4535
  }).nullable()
4169
4536
  });
4170
- var trafficStatusResponseSchema = z29.object({
4171
- sources: z29.array(trafficSourceDetailDtoSchema)
4537
+ var trafficStatusResponseSchema = z30.object({
4538
+ sources: z30.array(trafficSourceDetailDtoSchema)
4172
4539
  });
4173
- var trafficEventKindSchema = z29.enum(["crawler", "ai-user-fetch", "ai-referral"]);
4540
+ var trafficEventKindSchema = z30.enum(["crawler", "ai-user-fetch", "ai-referral"]);
4174
4541
  var TrafficEventKinds = trafficEventKindSchema.enum;
4175
- var trafficCrawlerEventEntrySchema = z29.object({
4176
- kind: z29.literal(TrafficEventKinds.crawler),
4177
- sourceId: z29.string(),
4178
- tsHour: z29.string(),
4179
- botId: z29.string(),
4180
- operator: z29.string(),
4181
- verificationStatus: z29.string(),
4182
- pathNormalized: z29.string(),
4183
- status: z29.number().int(),
4184
- hits: z29.number().int().nonnegative()
4185
- });
4186
- var trafficAiUserFetchEventEntrySchema = z29.object({
4187
- kind: z29.literal(TrafficEventKinds["ai-user-fetch"]),
4188
- sourceId: z29.string(),
4189
- tsHour: z29.string(),
4190
- botId: z29.string(),
4191
- operator: z29.string(),
4192
- verificationStatus: z29.string(),
4193
- pathNormalized: z29.string(),
4194
- status: z29.number().int(),
4195
- hits: z29.number().int().nonnegative()
4196
- });
4197
- var trafficAiReferralEventEntrySchema = z29.object({
4198
- kind: z29.literal(TrafficEventKinds["ai-referral"]),
4199
- sourceId: z29.string(),
4200
- tsHour: z29.string(),
4201
- product: z29.string(),
4202
- operator: z29.string(),
4203
- sourceDomain: z29.string(),
4204
- evidenceType: z29.string(),
4205
- landingPathNormalized: z29.string(),
4206
- status: z29.number().int(),
4207
- hits: z29.number().int().nonnegative()
4208
- });
4209
- var trafficEventEntrySchema = z29.discriminatedUnion("kind", [
4542
+ var trafficCrawlerEventEntrySchema = z30.object({
4543
+ kind: z30.literal(TrafficEventKinds.crawler),
4544
+ sourceId: z30.string(),
4545
+ tsHour: z30.string(),
4546
+ botId: z30.string(),
4547
+ operator: z30.string(),
4548
+ verificationStatus: z30.string(),
4549
+ pathNormalized: z30.string(),
4550
+ status: z30.number().int(),
4551
+ hits: z30.number().int().nonnegative()
4552
+ });
4553
+ var trafficAiUserFetchEventEntrySchema = z30.object({
4554
+ kind: z30.literal(TrafficEventKinds["ai-user-fetch"]),
4555
+ sourceId: z30.string(),
4556
+ tsHour: z30.string(),
4557
+ botId: z30.string(),
4558
+ operator: z30.string(),
4559
+ verificationStatus: z30.string(),
4560
+ pathNormalized: z30.string(),
4561
+ status: z30.number().int(),
4562
+ hits: z30.number().int().nonnegative()
4563
+ });
4564
+ var trafficAiReferralEventEntrySchema = z30.object({
4565
+ kind: z30.literal(TrafficEventKinds["ai-referral"]),
4566
+ sourceId: z30.string(),
4567
+ tsHour: z30.string(),
4568
+ product: z30.string(),
4569
+ operator: z30.string(),
4570
+ sourceDomain: z30.string(),
4571
+ evidenceType: z30.string(),
4572
+ landingPathNormalized: z30.string(),
4573
+ status: z30.number().int(),
4574
+ hits: z30.number().int().nonnegative()
4575
+ });
4576
+ var trafficEventEntrySchema = z30.discriminatedUnion("kind", [
4210
4577
  trafficCrawlerEventEntrySchema,
4211
4578
  trafficAiUserFetchEventEntrySchema,
4212
4579
  trafficAiReferralEventEntrySchema
4213
4580
  ]);
4214
- var trafficEventsResponseSchema = z29.object({
4215
- windowStart: z29.string(),
4216
- windowEnd: z29.string(),
4217
- totals: z29.object({
4218
- crawlerHits: z29.number().int().nonnegative(),
4219
- aiUserFetchHits: z29.number().int().nonnegative(),
4220
- aiReferralHits: z29.number().int().nonnegative()
4581
+ var trafficEventsResponseSchema = z30.object({
4582
+ windowStart: z30.string(),
4583
+ windowEnd: z30.string(),
4584
+ totals: z30.object({
4585
+ crawlerHits: z30.number().int().nonnegative(),
4586
+ aiUserFetchHits: z30.number().int().nonnegative(),
4587
+ aiReferralHits: z30.number().int().nonnegative()
4221
4588
  }),
4222
- events: z29.array(trafficEventEntrySchema)
4589
+ events: z30.array(trafficEventEntrySchema)
4223
4590
  });
4224
4591
 
4225
- // ../contracts/src/embeddings.ts
4226
- function cosineSimilarity(a, b) {
4227
- if (a.length === 0 || b.length === 0) {
4228
- throw new Error("cosineSimilarity: vectors must be non-empty");
4229
- }
4230
- if (a.length !== b.length) {
4231
- throw new Error(`cosineSimilarity: vector length mismatch (${a.length} vs ${b.length})`);
4232
- }
4233
- let dot = 0;
4234
- let magA = 0;
4235
- let magB = 0;
4236
- for (let i = 0; i < a.length; i++) {
4237
- dot += a[i] * b[i];
4238
- magA += a[i] * a[i];
4239
- magB += b[i] * b[i];
4240
- }
4241
- if (magA === 0 || magB === 0) return 0;
4242
- return dot / (Math.sqrt(magA) * Math.sqrt(magB));
4243
- }
4244
- function clusterByCosine(items, vectors, threshold) {
4245
- if (threshold < 0 || threshold > 1) {
4246
- throw new Error(`clusterByCosine: threshold must be in [0, 1], got ${threshold}`);
4247
- }
4248
- if (items.length !== vectors.length) {
4249
- throw new Error(`clusterByCosine: items/vectors length mismatch (${items.length} vs ${vectors.length})`);
4250
- }
4251
- if (items.length === 0) return [];
4252
- const parent = items.map((_, i) => i);
4253
- const find = (x) => {
4254
- let root = x;
4255
- while (parent[root] !== root) root = parent[root];
4256
- let cur = x;
4257
- while (parent[cur] !== root) {
4258
- const next = parent[cur];
4259
- parent[cur] = root;
4260
- cur = next;
4261
- }
4262
- return root;
4263
- };
4264
- const union = (a, b) => {
4265
- const ra = find(a);
4266
- const rb = find(b);
4267
- if (ra !== rb) parent[ra] = rb;
4268
- };
4269
- for (let i = 0; i < items.length; i++) {
4270
- for (let j = i + 1; j < items.length; j++) {
4271
- if (cosineSimilarity(vectors[i], vectors[j]) >= threshold) {
4272
- union(i, j);
4273
- }
4274
- }
4275
- }
4276
- const byRoot = /* @__PURE__ */ new Map();
4277
- for (let i = 0; i < items.length; i++) {
4278
- const root = find(i);
4279
- const existing = byRoot.get(root);
4280
- if (existing) existing.push(i);
4281
- else byRoot.set(root, [i]);
4282
- }
4283
- return Array.from(byRoot.values()).map((indices) => indices.map((idx) => items[idx]));
4284
- }
4285
- function pickClusterRepresentative(cluster) {
4286
- if (cluster.length === 0) throw new Error("pickClusterRepresentative: cluster is empty");
4287
- let best = cluster[0];
4288
- for (let i = 1; i < cluster.length; i++) {
4289
- if (cluster[i].length < best.length) best = cluster[i];
4290
- }
4291
- return best;
4292
- }
4293
-
4294
4592
  // ../contracts/src/formatting.ts
4295
4593
  function formatRatio(value) {
4296
4594
  if (!Number.isFinite(value) || value === 0) return "0%";
@@ -4332,6 +4630,12 @@ function formatDateRange(start, end) {
4332
4630
  if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
4333
4631
  return formatDate(start || end);
4334
4632
  }
4633
+ var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
4634
+ function parseInclusiveEndMs(iso) {
4635
+ const ms = Date.parse(iso);
4636
+ if (Number.isNaN(ms)) return null;
4637
+ return DATE_ONLY_PATTERN.test(iso) ? ms + 864e5 - 1 : ms;
4638
+ }
4335
4639
  function deltaPercent(current, prior) {
4336
4640
  if (prior <= 0) return null;
4337
4641
  return Math.round((current - prior) / prior * 100);
@@ -4350,105 +4654,105 @@ function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
4350
4654
  }
4351
4655
 
4352
4656
  // ../contracts/src/ads.ts
4353
- import { z as z30 } from "zod";
4354
- var adsConnectRequestSchema = z30.object({
4657
+ import { z as z31 } from "zod";
4658
+ var adsConnectRequestSchema = z31.object({
4355
4659
  /** Ads Manager "SDK key" scoped to one ad account. Stored in config.yaml, never the DB. */
4356
- apiKey: z30.string().min(1)
4357
- });
4358
- var adsConnectionStatusDtoSchema = z30.object({
4359
- connected: z30.boolean(),
4360
- adAccountId: z30.string().nullable().optional(),
4361
- displayName: z30.string().nullable().optional(),
4362
- currencyCode: z30.string().nullable().optional(),
4363
- timezone: z30.string().nullable().optional(),
4364
- status: z30.string().nullable().optional(),
4365
- lastSyncedAt: z30.string().nullable().optional()
4366
- });
4367
- var adsDisconnectResponseSchema = z30.object({
4368
- disconnected: z30.boolean()
4369
- });
4370
- var adsSyncResponseSchema = z30.object({
4371
- runId: z30.string(),
4372
- status: z30.string()
4373
- });
4374
- var adsCreativeDtoSchema = z30.object({
4375
- type: z30.string().nullable().optional(),
4376
- title: z30.string().nullable().optional(),
4377
- body: z30.string().nullable().optional(),
4378
- targetUrl: z30.string().nullable().optional()
4379
- });
4380
- var adsAdDtoSchema = z30.object({
4381
- id: z30.string(),
4382
- adGroupId: z30.string(),
4383
- name: z30.string(),
4384
- status: z30.string(),
4385
- reviewStatus: z30.string().nullable().optional(),
4660
+ apiKey: z31.string().min(1)
4661
+ });
4662
+ var adsConnectionStatusDtoSchema = z31.object({
4663
+ connected: z31.boolean(),
4664
+ adAccountId: z31.string().nullable().optional(),
4665
+ displayName: z31.string().nullable().optional(),
4666
+ currencyCode: z31.string().nullable().optional(),
4667
+ timezone: z31.string().nullable().optional(),
4668
+ status: z31.string().nullable().optional(),
4669
+ lastSyncedAt: z31.string().nullable().optional()
4670
+ });
4671
+ var adsDisconnectResponseSchema = z31.object({
4672
+ disconnected: z31.boolean()
4673
+ });
4674
+ var adsSyncResponseSchema = z31.object({
4675
+ runId: z31.string(),
4676
+ status: z31.string()
4677
+ });
4678
+ var adsCreativeDtoSchema = z31.object({
4679
+ type: z31.string().nullable().optional(),
4680
+ title: z31.string().nullable().optional(),
4681
+ body: z31.string().nullable().optional(),
4682
+ targetUrl: z31.string().nullable().optional()
4683
+ });
4684
+ var adsAdDtoSchema = z31.object({
4685
+ id: z31.string(),
4686
+ adGroupId: z31.string(),
4687
+ name: z31.string(),
4688
+ status: z31.string(),
4689
+ reviewStatus: z31.string().nullable().optional(),
4386
4690
  creative: adsCreativeDtoSchema.nullable().optional()
4387
4691
  });
4388
- var adsAdGroupDtoSchema = z30.object({
4389
- id: z30.string(),
4390
- campaignId: z30.string(),
4391
- name: z30.string(),
4392
- status: z30.string(),
4393
- billingEventType: z30.string().nullable().optional(),
4394
- maxBidMicros: z30.number().int().nullable().optional(),
4692
+ var adsAdGroupDtoSchema = z31.object({
4693
+ id: z31.string(),
4694
+ campaignId: z31.string(),
4695
+ name: z31.string(),
4696
+ status: z31.string(),
4697
+ billingEventType: z31.string().nullable().optional(),
4698
+ maxBidMicros: z31.number().int().nullable().optional(),
4395
4699
  /**
4396
4700
  * The targeting primitive: entries are multi-line strings of
4397
4701
  * newline-separated example queries (the live Ads Manager format).
4398
4702
  */
4399
- contextHints: z30.array(z30.string()).default([]),
4400
- ads: z30.array(adsAdDtoSchema).default([])
4401
- });
4402
- var adsCampaignDtoSchema = z30.object({
4403
- id: z30.string(),
4404
- name: z30.string(),
4405
- status: z30.string(),
4406
- biddingType: z30.string().nullable().optional(),
4407
- dailySpendLimitMicros: z30.number().int().nullable().optional(),
4408
- lifetimeSpendLimitMicros: z30.number().int().nullable().optional(),
4409
- adGroups: z30.array(adsAdGroupDtoSchema).default([])
4410
- });
4411
- var adsCampaignListResponseSchema = z30.object({
4412
- campaigns: z30.array(adsCampaignDtoSchema)
4413
- });
4414
- var adsInsightLevelSchema = z30.enum(["campaign", "ad_group"]);
4703
+ contextHints: z31.array(z31.string()).default([]),
4704
+ ads: z31.array(adsAdDtoSchema).default([])
4705
+ });
4706
+ var adsCampaignDtoSchema = z31.object({
4707
+ id: z31.string(),
4708
+ name: z31.string(),
4709
+ status: z31.string(),
4710
+ biddingType: z31.string().nullable().optional(),
4711
+ dailySpendLimitMicros: z31.number().int().nullable().optional(),
4712
+ lifetimeSpendLimitMicros: z31.number().int().nullable().optional(),
4713
+ adGroups: z31.array(adsAdGroupDtoSchema).default([])
4714
+ });
4715
+ var adsCampaignListResponseSchema = z31.object({
4716
+ campaigns: z31.array(adsCampaignDtoSchema)
4717
+ });
4718
+ var adsInsightLevelSchema = z31.enum(["campaign", "ad_group"]);
4415
4719
  var AdsInsightLevels = adsInsightLevelSchema.enum;
4416
- var adsInsightRowDtoSchema = z30.object({
4720
+ var adsInsightRowDtoSchema = z31.object({
4417
4721
  level: adsInsightLevelSchema,
4418
- entityId: z30.string(),
4419
- date: z30.string(),
4420
- impressions: z30.number().int(),
4421
- clicks: z30.number().int(),
4422
- spendMicros: z30.number().int(),
4722
+ entityId: z31.string(),
4723
+ date: z31.string(),
4724
+ impressions: z31.number().int(),
4725
+ clicks: z31.number().int(),
4726
+ spendMicros: z31.number().int(),
4423
4727
  /** clicks / impressions; null when impressions is 0. */
4424
- ctr: z30.number().nullable(),
4728
+ ctr: z31.number().nullable(),
4425
4729
  /** spendMicros / clicks, rounded to integer micros; null when clicks is 0. */
4426
- cpcMicros: z30.number().int().nullable()
4730
+ cpcMicros: z31.number().int().nullable()
4427
4731
  });
4428
- var adsInsightsResponseSchema = z30.object({
4429
- rows: z30.array(adsInsightRowDtoSchema),
4732
+ var adsInsightsResponseSchema = z31.object({
4733
+ rows: z31.array(adsInsightRowDtoSchema),
4430
4734
  /** Account currency for rendering spend/cpc; null before the first sync. */
4431
- currencyCode: z30.string().nullable().optional()
4432
- });
4433
- var adsTotalsDtoSchema = z30.object({
4434
- impressions: z30.number().int(),
4435
- clicks: z30.number().int(),
4436
- spendMicros: z30.number().int(),
4437
- ctr: z30.number().nullable(),
4438
- cpcMicros: z30.number().int().nullable()
4439
- });
4440
- var adsSummaryDtoSchema = z30.object({
4441
- connected: z30.boolean(),
4442
- displayName: z30.string().nullable().optional(),
4443
- currencyCode: z30.string().nullable().optional(),
4444
- lastSyncedAt: z30.string().nullable().optional(),
4445
- campaignCount: z30.number().int(),
4446
- adGroupCount: z30.number().int(),
4447
- adCount: z30.number().int(),
4735
+ currencyCode: z31.string().nullable().optional()
4736
+ });
4737
+ var adsTotalsDtoSchema = z31.object({
4738
+ impressions: z31.number().int(),
4739
+ clicks: z31.number().int(),
4740
+ spendMicros: z31.number().int(),
4741
+ ctr: z31.number().nullable(),
4742
+ cpcMicros: z31.number().int().nullable()
4743
+ });
4744
+ var adsSummaryDtoSchema = z31.object({
4745
+ connected: z31.boolean(),
4746
+ displayName: z31.string().nullable().optional(),
4747
+ currencyCode: z31.string().nullable().optional(),
4748
+ lastSyncedAt: z31.string().nullable().optional(),
4749
+ campaignCount: z31.number().int(),
4750
+ adGroupCount: z31.number().int(),
4751
+ adCount: z31.number().int(),
4448
4752
  /** Date range the totals cover (oldest/newest rollup date), null when empty. */
4449
- window: z30.object({
4450
- from: z30.string().nullable(),
4451
- to: z30.string().nullable()
4753
+ window: z31.object({
4754
+ from: z31.string().nullable(),
4755
+ to: z31.string().nullable()
4452
4756
  }),
4453
4757
  /** Campaign-level rollup totals over the window (levels are not summed across). */
4454
4758
  totals: adsTotalsDtoSchema
@@ -4659,6 +4963,11 @@ export {
4659
4963
  categorizeSource,
4660
4964
  categorizeSourceWithCompetitors,
4661
4965
  categoryLabel,
4966
+ clusterByCosine,
4967
+ pickClusterRepresentative,
4968
+ absolutizeProjectUrl,
4969
+ hostOf,
4970
+ normalizeUrlPath,
4662
4971
  discoveryBucketSchema,
4663
4972
  DiscoveryBuckets,
4664
4973
  DEFAULT_DISCOVERY_PROMOTE_BUCKETS,
@@ -4677,6 +4986,11 @@ export {
4677
4986
  discoveryPromoteRequestSchema,
4678
4987
  discoveryPromotePreviewSchema,
4679
4988
  discoveryPromoteResultSchema,
4989
+ discoveryHarvestDtoSchema,
4990
+ buildHarvestAnchorTerms,
4991
+ gateHarvestedSearchQueries,
4992
+ applyHarvestSemanticNovelty,
4993
+ aggregateHarvestedQueries,
4680
4994
  surfaceClassLabel,
4681
4995
  surfaceClassFromCompetitorType,
4682
4996
  classifySurfaceFromCategory,
@@ -4685,6 +4999,7 @@ export {
4685
4999
  sourceBreakdownDtoSchema,
4686
5000
  parseWindow,
4687
5001
  windowCutoff,
5002
+ visibilityStatsDtoSchema,
4688
5003
  ga4StatusDtoSchema,
4689
5004
  ga4SyncResponseDtoSchema,
4690
5005
  ga4AiReferralHistoryEntrySchema,
@@ -4737,9 +5052,6 @@ export {
4737
5052
  CheckCategories,
4738
5053
  doctorReportSchema,
4739
5054
  summarizeCheckResults,
4740
- absolutizeProjectUrl,
4741
- hostOf,
4742
- normalizeUrlPath,
4743
5055
  citationVisibilityResponseSchema,
4744
5056
  emptyCitationVisibility,
4745
5057
  citationStateToCited,
@@ -4776,13 +5088,12 @@ export {
4776
5088
  trafficEventKindSchema,
4777
5089
  TrafficEventKinds,
4778
5090
  trafficEventsResponseSchema,
4779
- clusterByCosine,
4780
- pickClusterRepresentative,
4781
5091
  formatRatio,
4782
5092
  formatNumber,
4783
5093
  formatDate,
4784
5094
  formatIsoDate,
4785
5095
  formatDateRange,
5096
+ parseInclusiveEndMs,
4786
5097
  deltaPercent,
4787
5098
  deltaTone,
4788
5099
  formatDeltaCopy,