@canonry/canonry 7.83.1 → 7.84.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 (110) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -0
  2. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +14 -1
  3. package/dist/{ads-BSVYKQ2L.js → ads-KBXA3LV6.js} +3 -3
  4. package/dist/{agent-L7VUHOHS.js → agent-OMNIUTNZ.js} +7 -7
  5. package/dist/{agent-ask-A4HQCNLV.js → agent-ask-3JQNG2PE.js} +3 -3
  6. package/dist/{agent-conversations-L3TJJKPJ.js → agent-conversations-KO5PDSM2.js} +3 -3
  7. package/dist/{agent-journeys-FEXRAZYN.js → agent-journeys-QL6NG3BE.js} +3 -3
  8. package/dist/{agent-memory-LBTLOHUA.js → agent-memory-2WZ6LVAN.js} +3 -3
  9. package/dist/{agent-providers-X7V2IHRK.js → agent-providers-TABMSD4J.js} +3 -3
  10. package/dist/{agent-transcript-YSP2HKUZ.js → agent-transcript-YNF63MPR.js} +3 -3
  11. package/dist/{analytics-FBPHES6M.js → analytics-5FGY3FHC.js} +3 -3
  12. package/dist/{apply-PBCXZC5F.js → apply-2HX4KYT6.js} +3 -3
  13. package/dist/{backfill-FPKFZZD6.js → backfill-ABSW6J3S.js} +10 -9
  14. package/dist/{backlinks-QMLTF2VF.js → backlinks-7CRPWNNN.js} +3 -3
  15. package/dist/{bing-H65WGTXU.js → bing-IT46EUFH.js} +3 -3
  16. package/dist/{bootstrap-JAZNMAKP.js → bootstrap-CHOXLC7Q.js} +10 -10
  17. package/dist/{cdp-4DAMAHTB.js → cdp-HQWKZGVQ.js} +7 -7
  18. package/dist/{chunk-QAJDHHC5.js → chunk-2MROJL32.js} +1 -1
  19. package/dist/{chunk-7M7X54PL.js → chunk-3EI4FHR5.js} +4 -4
  20. package/dist/{chunk-SKH3N6WI.js → chunk-3H6FSUKM.js} +3 -3
  21. package/dist/{chunk-LZGVQSZL.js → chunk-43CPBLRJ.js} +1 -1
  22. package/dist/{chunk-ITQ3KPG2.js → chunk-5PEVGTNX.js} +338 -338
  23. package/dist/{chunk-ZDOT2XF2.js → chunk-6SVAUILQ.js} +3233 -3211
  24. package/dist/{chunk-X7U7XYJQ.js → chunk-6TLHA6XA.js} +3 -3
  25. package/dist/{chunk-7Q5BSCKN.js → chunk-6Y75QV2S.js} +2 -2
  26. package/dist/{chunk-IPPNN5KE.js → chunk-AAKRCOQV.js} +2 -2
  27. package/dist/{chunk-OMNS42ZQ.js → chunk-B42R2SPU.js} +341 -4
  28. package/dist/{chunk-3UFQI4KL.js → chunk-BIGIIVGV.js} +21 -1
  29. package/dist/{chunk-W5ZBMBBZ.js → chunk-CEOIGQTS.js} +1 -1
  30. package/dist/{chunk-WDEDEEWA.js → chunk-D37PJYLD.js} +1 -1
  31. package/dist/{chunk-M3FWZMO4.js → chunk-EMSZDVDP.js} +7 -7
  32. package/dist/{chunk-32U5LFIT.js → chunk-GJE3OG4Y.js} +3 -3
  33. package/dist/{chunk-JCWXEVYW.js → chunk-HYEPL5UY.js} +3 -3
  34. package/dist/{chunk-KCFIQJOL.js → chunk-I642VITF.js} +2 -2
  35. package/dist/{chunk-PHVIMUAQ.js → chunk-K3OY52MI.js} +3 -1
  36. package/dist/{chunk-YJOGWS7F.js → chunk-LE6ZODCY.js} +2 -2
  37. package/dist/{chunk-YCRPOY2Y.js → chunk-LHAD6EK3.js} +2 -2
  38. package/dist/{chunk-P7UE7HIX.js → chunk-LWYALZ6F.js} +4 -4
  39. package/dist/{chunk-KFMU6CLU.js → chunk-LYNYNNOD.js} +1 -1
  40. package/dist/{chunk-DH4DXUX4.js → chunk-MZDIWY3K.js} +6 -6
  41. package/dist/{chunk-IQTMHY23.js → chunk-O7TWL6ZH.js} +17607 -17548
  42. package/dist/{chunk-Y7WHB65J.js → chunk-OBGP3DRQ.js} +2 -2
  43. package/dist/{chunk-AA6P5NK3.js → chunk-OMUINNSO.js} +1 -1
  44. package/dist/{chunk-NCM6PX64.js → chunk-Q57GN464.js} +1 -1
  45. package/dist/{chunk-BIBFCAYR.js → chunk-QLKG7G4Y.js} +30 -25
  46. package/dist/{chunk-2LHPVDO5.js → chunk-TZCUMC7U.js} +29 -171
  47. package/dist/{chunk-P76XOEWL.js → chunk-UYR5JI6R.js} +4 -4
  48. package/dist/{chunk-SEAXXCU6.js → chunk-VCUNNS4R.js} +1 -1
  49. package/dist/{chunk-SVMTN7MI.js → chunk-WJITVHAU.js} +2 -2
  50. package/dist/{chunk-WA4FD5Y6.js → chunk-WR4PA335.js} +1 -1
  51. package/dist/{chunk-CBAYQHNX.js → chunk-YPR2GVJC.js} +1 -1
  52. package/dist/{chunk-HIC62JTS.js → chunk-ZRL5FO5G.js} +3 -3
  53. package/dist/{citations-MNLMO33V.js → citations-YRBUXEWU.js} +3 -3
  54. package/dist/{cli-catalog-IKF4A7ZS.js → cli-catalog-JTIC72BY.js} +3 -3
  55. package/dist/{cli-commands-OFL44XAT.js → cli-commands-LUGJZGJU.js} +7 -7
  56. package/dist/cli.js +13 -13
  57. package/dist/{competitor-5DY5PKNU.js → competitor-ZKNDADMJ.js} +3 -3
  58. package/dist/{content-XNK2YY4S.js → content-ANRSBG3Y.js} +3 -3
  59. package/dist/{dashboard-TKY5UW2E.js → dashboard-HTJBWAPW.js} +6 -6
  60. package/dist/{demo-ZAVNNINJ.js → demo-V3ODU4JK.js} +1 -1
  61. package/dist/{demo-server-K5CZX35Q.js → demo-server-QYMT3KT7.js} +12 -12
  62. package/dist/{discover-D4H4ALIE.js → discover-DX7KIWHU.js} +3 -3
  63. package/dist/{discover-eval-2CID2IBT.js → discover-eval-PXWJYPHE.js} +3 -3
  64. package/dist/{doctor-OWRQNDWF.js → doctor-W2MTLKX7.js} +3 -3
  65. package/dist/{evidence-SQX4IMQM.js → evidence-PKKLSVNG.js} +3 -3
  66. package/dist/{export-cmd-73PYMZZ5.js → export-cmd-3PRWU7NH.js} +3 -3
  67. package/dist/{ga-S6AKEVAL.js → ga-QPH56JDO.js} +3 -3
  68. package/dist/{gbp-57J3AXEY.js → gbp-MJPIQZYZ.js} +3 -3
  69. package/dist/{google-PPU5MJSV.js → google-2XZC7DNJ.js} +4 -4
  70. package/dist/{google-marketing-W4KZ74HX.js → google-marketing-EDXL7ODG.js} +1 -1
  71. package/dist/{health-cmd-QYP4MS3E.js → health-cmd-YR2MFYNK.js} +3 -3
  72. package/dist/{history-MFHBHYH7.js → history-OM4OXDOZ.js} +3 -3
  73. package/dist/index.d.ts +160 -76
  74. package/dist/index.js +25 -25
  75. package/dist/{init-B3QKVWPF.js → init-5FAQPNH4.js} +12 -12
  76. package/dist/{insights-IIMMXGRO.js → insights-YGMPBDVA.js} +3 -3
  77. package/dist/intelligence-service-GCN4S6QT.js +16 -0
  78. package/dist/{journeys-DXXRNANM.js → journeys-ZPQCEHXY.js} +3 -3
  79. package/dist/{keys-O3T52MLX.js → keys-YZBRRGYE.js} +3 -3
  80. package/dist/{keyword-MS23BR7O.js → keyword-HSPNKAJQ.js} +3 -3
  81. package/dist/{logs-SKR5OLTY.js → logs-NO2N7W34.js} +3 -3
  82. package/dist/{mcp-NFC2PBGD.js → mcp-EAGJ2Y2D.js} +4 -4
  83. package/dist/mcp.js +9 -9
  84. package/dist/{measurement-plan-Z73LMQYB.js → measurement-plan-6VCBAM5B.js} +5 -5
  85. package/dist/{notify-2RUWK2AR.js → notify-SS5H5GRB.js} +3 -3
  86. package/dist/{organic-evidence-BGAOEVMZ.js → organic-evidence-XOZQXTHF.js} +3 -3
  87. package/dist/{overview-DONWVYZT.js → overview-3PLQYWDY.js} +4 -4
  88. package/dist/{project-FFSPWR6R.js → project-ARUHBFCG.js} +3 -3
  89. package/dist/{query-AXEUWIW3.js → query-4KCFDJ5V.js} +3 -3
  90. package/dist/{research-YURAJRCV.js → research-3NTYXGWN.js} +3 -3
  91. package/dist/{results-export-ECSJKZHZ.js → results-export-YOQQXBMP.js} +3 -3
  92. package/dist/{run-27EXPBE4.js → run-64UJAPPG.js} +4 -4
  93. package/dist/{schedule-NY55SZZF.js → schedule-JN5JE4EK.js} +3 -3
  94. package/dist/{search-RXCBX3ST.js → search-2QIHUMR7.js} +3 -3
  95. package/dist/{serve-E4HDMCY5.js → serve-V2NPNYMP.js} +26 -26
  96. package/dist/{session-EVHTLNIH.js → session-347VV5OG.js} +13 -13
  97. package/dist/{settings-IPC5SSDU.js → settings-XM5NGHFW.js} +3 -3
  98. package/dist/{skills-Y3VNCWQM.js → skills-4MWWCA36.js} +2 -2
  99. package/dist/{snapshot-TPDSMADU.js → snapshot-RWHR3VJ4.js} +3 -3
  100. package/dist/{src-IDX2F2SR.js → src-HUF2NBUG.js} +4 -2
  101. package/dist/{status-YSQJBNM7.js → status-3OI7PTQ5.js} +4 -4
  102. package/dist/{technical-aeo-NMUQVIFG.js → technical-aeo-RXHIFLHD.js} +3 -3
  103. package/dist/{telemetry-OA6W7Q54.js → telemetry-DZSITJKQ.js} +4 -4
  104. package/dist/{tools-JPYR7UTF.js → tools-ZUUOSB75.js} +11 -11
  105. package/dist/{traffic-CE47IFFI.js → traffic-KIQBUHJW.js} +5 -5
  106. package/dist/{users-NMWBTCDI.js → users-ID4O7NXQ.js} +3 -3
  107. package/dist/{visibility-stats-DE64FVOJ.js → visibility-stats-X5MTD47P.js} +3 -3
  108. package/dist/{wordpress-AJWIJSM7.js → wordpress-WU2HLWIW.js} +3 -3
  109. package/package.json +1 -1
  110. package/dist/intelligence-service-HZ2LWOHB.js +0 -16
@@ -3,7 +3,7 @@ import {
3
3
  resolveCliAccess,
4
4
  resolveCliAccessForInvocation,
5
5
  windowOptions
6
- } from "./chunk-SEAXXCU6.js";
6
+ } from "./chunk-VCUNNS4R.js";
7
7
  import {
8
8
  emitJsonl
9
9
  } from "./chunk-Z7IUAJOF.js";
@@ -14,7 +14,7 @@ import {
14
14
  } from "./chunk-HGMYEDAQ.js";
15
15
  import {
16
16
  OperationAccesses
17
- } from "./chunk-PHVIMUAQ.js";
17
+ } from "./chunk-K3OY52MI.js";
18
18
 
19
19
  // src/cli-catalog.ts
20
20
  import { createRequire } from "module";
@@ -111,7 +111,7 @@ async function dispatchRegisteredCommand(args, fallbackFormat, specs) {
111
111
  const accessValue = accessFilterFromArgs(argsWithoutHelp);
112
112
  validateAccessFilter(accessValue);
113
113
  if (fallbackFormat !== "text") {
114
- const { printCliCatalog: printCliCatalog2 } = await import("./cli-catalog-IKF4A7ZS.js");
114
+ const { printCliCatalog: printCliCatalog2 } = await import("./cli-catalog-JTIC72BY.js");
115
115
  const group2 = argsWithoutHelp.find((arg) => !arg.startsWith("--"));
116
116
  printCliCatalog2(specs, fallbackFormat, group2, accessValue);
117
117
  return true;
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-VJWDBFSL.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-IPPNN5KE.js";
7
+ } from "./chunk-AAKRCOQV.js";
8
8
  import {
9
9
  CliError,
10
10
  isMachineFormat
11
11
  } from "./chunk-HGMYEDAQ.js";
12
12
  import {
13
13
  describeError
14
- } from "./chunk-PHVIMUAQ.js";
14
+ } from "./chunk-K3OY52MI.js";
15
15
 
16
16
  // src/commands/mcp.ts
17
17
  import fs from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadConfig
3
- } from "./chunk-WA4FD5Y6.js";
3
+ } from "./chunk-WR4PA335.js";
4
4
  import {
5
5
  PACKAGE_VERSION
6
6
  } from "./chunk-AOJU5LKX.js";
@@ -36,7 +36,7 @@ import {
36
36
  sentimentSettingsReadSchema,
37
37
  sentimentSummaryReadSchema,
38
38
  upgradeCommandFor
39
- } from "./chunk-PHVIMUAQ.js";
39
+ } from "./chunk-K3OY52MI.js";
40
40
 
41
41
  // ../api-client-generated/src/generated/core/bodySerializer.gen.ts
42
42
  var jsonBodySerializer = {
@@ -1,9 +1,36 @@
1
1
  import {
2
- parseJsonColumn
3
- } from "./chunk-3UFQI4KL.js";
2
+ createLogger
3
+ } from "./chunk-WJITVHAU.js";
4
+ import {
5
+ addLikelyFakeAgentHits,
6
+ writeUnrecognizedAgentBuckets
7
+ } from "./chunk-6SVAUILQ.js";
8
+ import {
9
+ BROWSER_SHAPED_AGENT_MARKERS,
10
+ DEFAULT_AI_CRAWLER_RULES,
11
+ GENERIC_HTTP_CLIENTS,
12
+ SELF_TRAFFIC_USER_AGENT_PATTERNS,
13
+ UNLISTED_AGENT_SKIPPED_EXTENSIONS,
14
+ UNRECOGNIZED_AGENT_NAMING_VERSION,
15
+ classifyAiUserFetch,
16
+ classifyCrawler,
17
+ isSelfTraffic,
18
+ unrecognizedAgentName
19
+ } from "./chunk-O7TWL6ZH.js";
20
+ import {
21
+ RawEventSampleEventTypes,
22
+ aiUserFetchEventsHourly,
23
+ crawlerEventsHourly,
24
+ migrationsTable,
25
+ parseJsonColumn,
26
+ rawEventSamples,
27
+ trafficReclassificationState,
28
+ unrecognizedAgentEventsHourly
29
+ } from "./chunk-BIGIIVGV.js";
4
30
  import {
5
31
  AI_ENGINE_DOMAINS,
6
32
  AI_ENGINE_SELF_DOMAINS,
33
+ AgentTrustLevels,
7
34
  AnchoredSpanSources,
8
35
  OPENROUTER_MODEL_PATTERN,
9
36
  OPENROUTER_ROUTE_FAMILY,
@@ -12,6 +39,10 @@ import {
12
39
  ProviderNames,
13
40
  RetrievalContracts,
14
41
  RetrievalStatuses,
42
+ TrafficEventConfidences,
43
+ TrafficEvidenceKinds,
44
+ TrafficSourceTypes,
45
+ UNRECORDED_TRAFFIC_PATH,
15
46
  VERTEX_AI_SEARCH_PROXY_DOMAIN,
16
47
  asRecord,
17
48
  describeError,
@@ -26,7 +57,7 @@ import {
26
57
  retryAfterDelayMs,
27
58
  usageCount,
28
59
  withRetry
29
- } from "./chunk-PHVIMUAQ.js";
60
+ } from "./chunk-K3OY52MI.js";
30
61
 
31
62
  // ../provider-openai/src/normalize.ts
32
63
  import OpenAI from "openai";
@@ -2895,6 +2926,310 @@ function extractStoredAnswerAnchors(provider, rawResponse) {
2895
2926
  }
2896
2927
  }
2897
2928
 
2929
+ // src/traffic-reclassify.ts
2930
+ import { createHash } from "crypto";
2931
+ import { setImmediate as yieldToEventLoop } from "timers/promises";
2932
+ import { and, eq, inArray, sql } from "drizzle-orm";
2933
+ var log = createLogger("TrafficReclassify");
2934
+ var STATE_ID = "default";
2935
+ var BATCH_SIZE = 200;
2936
+ var UNRECOGNIZED_TABLE_MIGRATION = 201;
2937
+ var TRAFFIC_RECLASSIFY_START_DELAY_MS = 3e4;
2938
+ function trafficClassificationFingerprint() {
2939
+ const pattern = (value) => `/${value.source}/${value.flags}`;
2940
+ return createHash("sha256").update(JSON.stringify({
2941
+ rules: DEFAULT_AI_CRAWLER_RULES.map((rule) => ({
2942
+ id: rule.id,
2943
+ operator: rule.operator,
2944
+ product: rule.product,
2945
+ purpose: rule.purpose,
2946
+ patterns: rule.userAgentPatterns.map(pattern)
2947
+ })),
2948
+ self: SELF_TRAFFIC_USER_AGENT_PATTERNS.map(pattern),
2949
+ naming: UNRECOGNIZED_AGENT_NAMING_VERSION,
2950
+ genericClients: GENERIC_HTTP_CLIENTS,
2951
+ browserMarkers: BROWSER_SHAPED_AGENT_MARKERS,
2952
+ skippedExtensions: UNLISTED_AGENT_SKIPPED_EXTENSIONS
2953
+ })).digest("hex");
2954
+ }
2955
+ function probeRequest(userAgent, path, observedAt) {
2956
+ return {
2957
+ sourceType: TrafficSourceTypes["cloud-run"],
2958
+ evidenceKind: TrafficEvidenceKinds["raw-request"],
2959
+ confidence: TrafficEventConfidences.observed,
2960
+ eventId: "reclassify",
2961
+ observedAt,
2962
+ method: "GET",
2963
+ requestUrl: `https://placeholder${path.startsWith("/") ? path : "/"}`,
2964
+ host: "placeholder",
2965
+ path,
2966
+ queryString: null,
2967
+ status: 200,
2968
+ userAgent,
2969
+ // Stored traffic keeps no client address, so no replay can verify an IP.
2970
+ remoteIp: null,
2971
+ referer: null,
2972
+ latencyMs: null,
2973
+ requestSizeBytes: null,
2974
+ responseSizeBytes: null,
2975
+ providerResource: { type: "cloud_run_revision", labels: {} },
2976
+ providerLabels: {}
2977
+ };
2978
+ }
2979
+ function classifyStored(userAgent, path, observedAt) {
2980
+ const request2 = probeRequest(userAgent, path, observedAt);
2981
+ if (isSelfTraffic(request2)) return { kind: "self" };
2982
+ const fetch2 = classifyAiUserFetch(request2);
2983
+ if (fetch2) return { kind: "user-fetch", fetch: fetch2 };
2984
+ const crawler = classifyCrawler(request2);
2985
+ if (crawler) return { kind: "crawler", crawler };
2986
+ return { kind: "unknown" };
2987
+ }
2988
+ function sqliteUtcToIso(value) {
2989
+ return /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(value) ? `${value.replace(" ", "T")}Z` : value;
2990
+ }
2991
+ function hourOf(iso) {
2992
+ const hour = new Date(iso);
2993
+ hour.setUTCMinutes(0, 0, 0);
2994
+ return hour.toISOString();
2995
+ }
2996
+ function unrecognizedCountingStart(db) {
2997
+ const row = db.select({ appliedAt: migrationsTable.appliedAt }).from(migrationsTable).where(eq(migrationsTable.version, UNRECOGNIZED_TABLE_MIGRATION)).get();
2998
+ return row ? hourOf(sqliteUtcToIso(row.appliedAt)) : null;
2999
+ }
3000
+ function addHourlyHits(tx, opts) {
3001
+ const { projectId, sourceId, tsHour, verdict, pathNormalized, status, hits, sampledUserAgent, now } = opts;
3002
+ if (verdict.kind === "user-fetch" && verdict.fetch.trust === AgentTrustLevels.likely_fake) {
3003
+ addLikelyFakeAgentHits(tx, {
3004
+ projectId,
3005
+ sourceId,
3006
+ buckets: [{ tsHour, botId: verdict.fetch.botId, operator: verdict.fetch.operator, reason: verdict.fetch.trustReasons[0], hits }],
3007
+ now
3008
+ });
3009
+ return;
3010
+ }
3011
+ const table = verdict.kind === "user-fetch" ? aiUserFetchEventsHourly : crawlerEventsHourly;
3012
+ const classified = verdict.kind === "user-fetch" ? verdict.fetch : verdict.crawler;
3013
+ tx.insert(table).values({
3014
+ projectId,
3015
+ sourceId,
3016
+ tsHour,
3017
+ botId: classified.botId,
3018
+ operator: classified.operator,
3019
+ verificationStatus: classified.verificationStatus,
3020
+ pathNormalized,
3021
+ status,
3022
+ hits,
3023
+ sampledUserAgent,
3024
+ createdAt: now,
3025
+ updatedAt: now
3026
+ }).onConflictDoUpdate({
3027
+ target: [table.projectId, table.sourceId, table.tsHour, table.botId, table.verificationStatus, table.pathNormalized, table.status],
3028
+ set: { hits: sql`${table.hits} + ${hits}`, updatedAt: now }
3029
+ }).run();
3030
+ }
3031
+ function botIdOf(verdict) {
3032
+ return verdict.kind === "user-fetch" ? verdict.fetch.botId : verdict.crawler.botId;
3033
+ }
3034
+ async function reclassifyUnrecognizedRows(db, opts) {
3035
+ const result = { examined: 0, moved: 0, movedHits: 0, renamed: 0, removed: 0, byBot: {} };
3036
+ const table = unrecognizedAgentEventsHourly;
3037
+ const verdicts = /* @__PURE__ */ new Map();
3038
+ const lastStartingRowid = Number(db.select({ max: sql`max(rowid)` }).from(table).get()?.max ?? 0);
3039
+ let lastRowid = 0;
3040
+ for (; ; ) {
3041
+ if (opts.shouldStop()) break;
3042
+ const keys = db.select({ rowid: sql`rowid`, projectId: table.projectId, sourceId: table.sourceId, tsHour: table.tsHour, agentName: table.agentName }).from(table).where(and(
3043
+ sql`rowid > ${lastRowid}`,
3044
+ sql`rowid <= ${lastStartingRowid}`,
3045
+ opts.projectIds ? inArray(table.projectId, [...opts.projectIds]) : void 0
3046
+ )).orderBy(sql`rowid`).limit(BATCH_SIZE).all();
3047
+ if (keys.length === 0) break;
3048
+ lastRowid = keys[keys.length - 1].rowid;
3049
+ result.examined += keys.length;
3050
+ db.transaction((tx) => {
3051
+ for (const key of keys) {
3052
+ const match = and(
3053
+ eq(table.projectId, key.projectId),
3054
+ eq(table.sourceId, key.sourceId),
3055
+ eq(table.tsHour, key.tsHour),
3056
+ eq(table.agentName, key.agentName)
3057
+ );
3058
+ const row = tx.select().from(table).where(match).get();
3059
+ if (!row?.sampledUserAgent) continue;
3060
+ const userAgent = row.sampledUserAgent;
3061
+ let verdict = verdicts.get(userAgent);
3062
+ if (!verdict) {
3063
+ verdict = classifyStored(userAgent, "/", row.tsHour);
3064
+ verdicts.set(userAgent, verdict);
3065
+ }
3066
+ if (verdict.kind === "self") {
3067
+ result.removed++;
3068
+ if (!opts.dryRun) tx.delete(table).where(match).run();
3069
+ continue;
3070
+ }
3071
+ if (verdict.kind === "user-fetch" || verdict.kind === "crawler") {
3072
+ result.moved++;
3073
+ result.movedHits += row.hits;
3074
+ const botId = botIdOf(verdict);
3075
+ result.byBot[botId] = (result.byBot[botId] ?? 0) + row.hits;
3076
+ if (opts.dryRun) continue;
3077
+ addHourlyHits(tx, {
3078
+ projectId: row.projectId,
3079
+ sourceId: row.sourceId,
3080
+ tsHour: row.tsHour,
3081
+ verdict,
3082
+ pathNormalized: UNRECORDED_TRAFFIC_PATH,
3083
+ status: 0,
3084
+ hits: row.hits,
3085
+ sampledUserAgent: userAgent,
3086
+ now: opts.now
3087
+ });
3088
+ tx.delete(table).where(match).run();
3089
+ continue;
3090
+ }
3091
+ const name = unrecognizedAgentName({ method: "GET", path: row.sampledPath, userAgent });
3092
+ if (name === row.agentName) continue;
3093
+ if (name === null) {
3094
+ result.removed++;
3095
+ if (!opts.dryRun) tx.delete(table).where(match).run();
3096
+ continue;
3097
+ }
3098
+ result.renamed++;
3099
+ if (opts.dryRun) continue;
3100
+ tx.delete(table).where(match).run();
3101
+ writeUnrecognizedAgentBuckets(tx, {
3102
+ projectId: row.projectId,
3103
+ sourceId: row.sourceId,
3104
+ buckets: [{ tsHour: row.tsHour, agentName: name, hits: row.hits, sampledUserAgent: userAgent, sampledPath: row.sampledPath ?? "" }],
3105
+ now: opts.now
3106
+ });
3107
+ }
3108
+ }, { behavior: "immediate" });
3109
+ await yieldToEventLoop();
3110
+ }
3111
+ return result;
3112
+ }
3113
+ async function reclassifyUnknownSamples(db, opts) {
3114
+ const result = { examined: 0, reclassified: 0, counted: 0, removed: 0, byBot: {} };
3115
+ const countingStart = unrecognizedCountingStart(db);
3116
+ const lastStartingRowid = Number(db.select({ max: sql`max(rowid)` }).from(rawEventSamples).get()?.max ?? 0);
3117
+ let lastRowid = 0;
3118
+ for (; ; ) {
3119
+ if (opts.shouldStop()) break;
3120
+ const samples = db.select({
3121
+ rowid: sql`rowid`,
3122
+ id: rawEventSamples.id,
3123
+ projectId: rawEventSamples.projectId,
3124
+ sourceId: rawEventSamples.sourceId,
3125
+ ts: rawEventSamples.ts,
3126
+ userAgent: rawEventSamples.userAgent,
3127
+ pathNormalized: rawEventSamples.pathNormalized,
3128
+ status: rawEventSamples.status
3129
+ }).from(rawEventSamples).where(and(
3130
+ eq(rawEventSamples.eventType, RawEventSampleEventTypes.unknown),
3131
+ sql`rowid > ${lastRowid}`,
3132
+ sql`rowid <= ${lastStartingRowid}`,
3133
+ opts.projectIds ? inArray(rawEventSamples.projectId, [...opts.projectIds]) : void 0
3134
+ )).orderBy(sql`rowid`).limit(BATCH_SIZE).all();
3135
+ if (samples.length === 0) break;
3136
+ lastRowid = samples[samples.length - 1].rowid;
3137
+ result.examined += samples.length;
3138
+ db.transaction((tx) => {
3139
+ for (const sample of samples) {
3140
+ if (!sample.userAgent) continue;
3141
+ const verdict = classifyStored(sample.userAgent, sample.pathNormalized, sample.ts);
3142
+ if (verdict.kind === "self") {
3143
+ result.removed++;
3144
+ if (!opts.dryRun) tx.delete(rawEventSamples).where(eq(rawEventSamples.id, sample.id)).run();
3145
+ continue;
3146
+ }
3147
+ if (verdict.kind === "unknown") continue;
3148
+ result.reclassified++;
3149
+ const botId = botIdOf(verdict);
3150
+ result.byBot[botId] = (result.byBot[botId] ?? 0) + 1;
3151
+ const tsHour = hourOf(sample.ts);
3152
+ const countSample = countingStart === null || tsHour < countingStart || unrecognizedAgentName({ method: "GET", path: sample.pathNormalized, userAgent: sample.userAgent }) === null;
3153
+ if (countSample) result.counted++;
3154
+ if (opts.dryRun) continue;
3155
+ tx.update(rawEventSamples).set({
3156
+ eventType: verdict.kind === "user-fetch" ? RawEventSampleEventTypes.aiUserFetch : RawEventSampleEventTypes.crawler,
3157
+ classifierDetailsJson: {
3158
+ crawler: verdict.kind === "crawler" ? { ...verdict.crawler, verificationManifest: null } : null,
3159
+ aiUserFetch: verdict.kind === "user-fetch" ? { ...verdict.fetch, verificationManifest: null } : null,
3160
+ aiReferral: null
3161
+ }
3162
+ }).where(eq(rawEventSamples.id, sample.id)).run();
3163
+ if (!countSample) continue;
3164
+ addHourlyHits(tx, {
3165
+ projectId: sample.projectId,
3166
+ sourceId: sample.sourceId,
3167
+ tsHour,
3168
+ verdict,
3169
+ pathNormalized: sample.pathNormalized,
3170
+ status: sample.status ?? 200,
3171
+ hits: 1,
3172
+ sampledUserAgent: sample.userAgent,
3173
+ now: opts.now
3174
+ });
3175
+ }
3176
+ }, { behavior: "immediate" });
3177
+ await yieldToEventLoop();
3178
+ }
3179
+ return result;
3180
+ }
3181
+ async function reclassifyStoredTraffic(db, opts = {}) {
3182
+ const shared = {
3183
+ projectIds: opts.projectIds,
3184
+ dryRun: opts.dryRun === true,
3185
+ now: opts.now ?? (/* @__PURE__ */ new Date()).toISOString(),
3186
+ shouldStop: opts.shouldStop ?? (() => false)
3187
+ };
3188
+ const samples = await reclassifyUnknownSamples(db, shared);
3189
+ const unrecognized = shared.shouldStop() ? { examined: 0, moved: 0, movedHits: 0, renamed: 0, removed: 0, byBot: {} } : await reclassifyUnrecognizedRows(db, shared);
3190
+ return { dryRun: shared.dryRun, samples, unrecognized };
3191
+ }
3192
+ async function reclassifyStoredTrafficIfRulesChanged(db, opts = {}) {
3193
+ const fingerprint = trafficClassificationFingerprint();
3194
+ const state = db.select().from(trafficReclassificationState).where(eq(trafficReclassificationState.id, STATE_ID)).get();
3195
+ if (state?.fingerprint === fingerprint) return { ran: false };
3196
+ const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
3197
+ const shouldStop = opts.shouldStop ?? (() => false);
3198
+ const result = await reclassifyStoredTraffic(db, { now: now(), shouldStop });
3199
+ if (shouldStop()) return { ran: false };
3200
+ const completedAt = now();
3201
+ db.insert(trafficReclassificationState).values({ id: STATE_ID, fingerprint, completedAt, resultJson: { ...result } }).onConflictDoUpdate({
3202
+ target: trafficReclassificationState.id,
3203
+ set: { fingerprint, completedAt, resultJson: { ...result } }
3204
+ }).run();
3205
+ return { ran: true, result };
3206
+ }
3207
+ function startTrafficReclassification(opts) {
3208
+ let stopped = false;
3209
+ const timer = setTimeout(() => {
3210
+ reclassifyStoredTrafficIfRulesChanged(opts.db, { shouldStop: () => stopped }).then((outcome) => {
3211
+ if (!outcome.ran || !outcome.result) return;
3212
+ const { samples, unrecognized } = outcome.result;
3213
+ log.info("traffic-reclassify.completed", {
3214
+ samplesReclassified: samples.reclassified,
3215
+ samplesCounted: samples.counted,
3216
+ samplesRemoved: samples.removed,
3217
+ unrecognizedMoved: unrecognized.moved,
3218
+ unrecognizedMovedHits: unrecognized.movedHits,
3219
+ unrecognizedRenamed: unrecognized.renamed,
3220
+ unrecognizedRemoved: unrecognized.removed
3221
+ });
3222
+ }).catch((err) => {
3223
+ if (!stopped) log.error("traffic-reclassify.failed", { error: describeError(err) });
3224
+ });
3225
+ }, opts.delayMs ?? TRAFFIC_RECLASSIFY_START_DELAY_MS);
3226
+ timer.unref();
3227
+ return () => {
3228
+ stopped = true;
3229
+ clearTimeout(timer);
3230
+ };
3231
+ }
3232
+
2898
3233
  export {
2899
3234
  OPENAI_RETRIEVAL_CONTRACT,
2900
3235
  reparseStoredResult,
@@ -2911,5 +3246,7 @@ export {
2911
3246
  museAdapter,
2912
3247
  openrouterAdapter,
2913
3248
  resolveStoredApiResponse,
2914
- extractStoredAnswerAnchors
3249
+ extractStoredAnswerAnchors,
3250
+ reclassifyStoredTraffic,
3251
+ startTrafficReclassification
2915
3252
  };
@@ -20,7 +20,7 @@ import {
20
20
  templateLinkRatio,
21
21
  templateLinkUbiquityAvailable,
22
22
  validationError
23
- } from "./chunk-PHVIMUAQ.js";
23
+ } from "./chunk-K3OY52MI.js";
24
24
  import {
25
25
  __export
26
26
  } from "./chunk-MLKGABMK.js";
@@ -176,6 +176,7 @@ __export(schema_exports, {
176
176
  siteCrawlSnapshots: () => siteCrawlSnapshots,
177
177
  siteLivenessState: () => siteLivenessState,
178
178
  trafficEventReceipts: () => trafficEventReceipts,
179
+ trafficReclassificationState: () => trafficReclassificationState,
179
180
  trafficSources: () => trafficSources,
180
181
  unrecognizedAgentEventsHourly: () => unrecognizedAgentEventsHourly,
181
182
  unrecognizedAgentNotifyState: () => unrecognizedAgentNotifyState,
@@ -2596,6 +2597,12 @@ var unrecognizedAgentNotifyState = sqliteTable2("unrecognized_agent_notify_state
2596
2597
  }, (table) => [
2597
2598
  primaryKey2({ columns: [table.projectId, table.agentName] })
2598
2599
  ]);
2600
+ var trafficReclassificationState = sqliteTable2("traffic_reclassification_state", {
2601
+ id: text2("id").primaryKey(),
2602
+ fingerprint: text2("fingerprint").notNull(),
2603
+ completedAt: text2("completed_at").notNull(),
2604
+ resultJson: text2("result_json", { mode: "json" }).$type()
2605
+ });
2599
2606
  var aiReferralEventsHourly = sqliteTable2("ai_referral_events_hourly", {
2600
2607
  projectId: text2("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
2601
2608
  sourceId: text2("source_id").notNull().references(() => trafficSources.id, { onDelete: "cascade" }),
@@ -8538,6 +8545,18 @@ var MIGRATION_VERSIONS = [
8538
8545
  `CREATE INDEX IF NOT EXISTS idx_ai_user_fetch_likely_fake_project_ts ON ai_user_fetch_likely_fake_hourly(project_id, ts_hour)`,
8539
8546
  `CREATE INDEX IF NOT EXISTS idx_ai_user_fetch_likely_fake_source_ts ON ai_user_fetch_likely_fake_hourly(source_id, ts_hour)`
8540
8547
  ]
8548
+ },
8549
+ {
8550
+ version: 204,
8551
+ name: "traffic-reclassification-state",
8552
+ statements: [
8553
+ `CREATE TABLE IF NOT EXISTS traffic_reclassification_state (
8554
+ id TEXT PRIMARY KEY,
8555
+ fingerprint TEXT NOT NULL,
8556
+ completed_at TEXT NOT NULL,
8557
+ result_json TEXT
8558
+ )`
8559
+ ]
8541
8560
  }
8542
8561
  ];
8543
8562
  var AERO_LEDGER_REBUILDS = [
@@ -10046,6 +10065,7 @@ export {
10046
10065
  aiUserFetchVerificationManifestsHourly,
10047
10066
  unrecognizedAgentEventsHourly,
10048
10067
  unrecognizedAgentNotifyState,
10068
+ trafficReclassificationState,
10049
10069
  aiReferralEventsHourly,
10050
10070
  rawEventSamples,
10051
10071
  RawEventSampleEventTypes,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-HGMYEDAQ.js";
5
5
  import {
6
6
  describeError
7
- } from "./chunk-PHVIMUAQ.js";
7
+ } from "./chunk-K3OY52MI.js";
8
8
 
9
9
  // src/gsc-sitemap-submission.ts
10
10
  function dedupeGscSitemapUrls(urls) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  USAGE_TELEMETRY_HEADERS,
3
3
  redactLogString
4
- } from "./chunk-PHVIMUAQ.js";
4
+ } from "./chunk-K3OY52MI.js";
5
5
 
6
6
  // ../api-routes/src/request-context.ts
7
7
  import { AsyncLocalStorage } from "async_hooks";
@@ -1,9 +1,9 @@
1
- import {
2
- createLogger
3
- } from "./chunk-SVMTN7MI.js";
4
1
  import {
5
2
  pickProjectCitedDomain
6
- } from "./chunk-LZGVQSZL.js";
3
+ } from "./chunk-43CPBLRJ.js";
4
+ import {
5
+ createLogger
6
+ } from "./chunk-WJITVHAU.js";
7
7
  import {
8
8
  GBP_INSIGHT_PROVIDER,
9
9
  PERSISTENT_GAP_THRESHOLD,
@@ -17,7 +17,7 @@ import {
17
17
  mergeGscQueryTotalsWithFallback,
18
18
  readGscQueryDailyRows,
19
19
  readLatestGscDataDate
20
- } from "./chunk-ZDOT2XF2.js";
20
+ } from "./chunk-6SVAUILQ.js";
21
21
  import {
22
22
  competitors,
23
23
  gbpDailyMetrics,
@@ -35,14 +35,14 @@ import {
35
35
  queries,
36
36
  querySnapshots,
37
37
  runs
38
- } from "./chunk-3UFQI4KL.js";
38
+ } from "./chunk-BIGIIVGV.js";
39
39
  import {
40
40
  CitationStates,
41
41
  RunKinds,
42
42
  RunStatuses,
43
43
  RunTriggers,
44
44
  effectiveDomains
45
- } from "./chunk-PHVIMUAQ.js";
45
+ } from "./chunk-K3OY52MI.js";
46
46
 
47
47
  // src/intelligence-service.ts
48
48
  import { eq, desc, asc, and, ne, or, inArray, gte, lte, isNull, sql, exists } from "drizzle-orm";
@@ -7,12 +7,12 @@ import {
7
7
  import {
8
8
  createApiClient,
9
9
  runWithUsageTags
10
- } from "./chunk-IPPNN5KE.js";
10
+ } from "./chunk-AAKRCOQV.js";
11
11
  import {
12
12
  canonryMcpTools,
13
13
  mcpOutputSchema,
14
14
  toolOperationAccess
15
- } from "./chunk-HIC62JTS.js";
15
+ } from "./chunk-ZRL5FO5G.js";
16
16
  import {
17
17
  PACKAGE_VERSION
18
18
  } from "./chunk-AOJU5LKX.js";
@@ -25,7 +25,7 @@ import {
25
25
  isReadOnlyKey,
26
26
  restrictedWriteScopes,
27
27
  upgradeCaveatFor
28
- } from "./chunk-PHVIMUAQ.js";
28
+ } from "./chunk-K3OY52MI.js";
29
29
 
30
30
  // src/mcp/server.ts
31
31
  import { randomUUID } from "crypto";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-IPPNN5KE.js";
3
+ } from "./chunk-AAKRCOQV.js";
4
4
  import {
5
5
  emitJsonl
6
6
  } from "./chunk-Z7IUAJOF.js";
7
7
  import {
8
8
  measurementDraftOperationSchema,
9
9
  runMeasurementDraftAction
10
- } from "./chunk-KFMU6CLU.js";
10
+ } from "./chunk-LYNYNNOD.js";
11
11
  import {
12
12
  isMachineFormat,
13
13
  systemError
@@ -37,7 +37,7 @@ import {
37
37
  unattributedAnswersLabel,
38
38
  uncheckedSourcesLabel,
39
39
  visibilityReportRequestSchema
40
- } from "./chunk-PHVIMUAQ.js";
40
+ } from "./chunk-K3OY52MI.js";
41
41
 
42
42
  // src/commands/measurement-plan.ts
43
43
  import crypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-IPPNN5KE.js";
3
+ } from "./chunk-AAKRCOQV.js";
4
4
  import {
5
5
  emitJsonl
6
6
  } from "./chunk-Z7IUAJOF.js";
@@ -19,7 +19,7 @@ import {
19
19
  formatMicros,
20
20
  formatRunErrorOneLine,
21
21
  resolveProviderInput
22
- } from "./chunk-PHVIMUAQ.js";
22
+ } from "./chunk-K3OY52MI.js";
23
23
 
24
24
  // src/commands/run.ts
25
25
  function getClient() {
@@ -25423,9 +25423,10 @@ var DOWNLOAD_EXTENSIONS = /* @__PURE__ */ new Set([
25423
25423
  "sql"
25424
25424
  ]);
25425
25425
  var SITEMAP_BASENAME = /^sitemap(?:[-_]index)?$/;
25426
+ var UNRECORDED_TRAFFIC_PATH = "(path not recorded)";
25426
25427
  function classifyTrafficPath(pathNormalized) {
25427
25428
  const raw = (pathNormalized ?? "").trim();
25428
- if (!raw) return TrafficPathClasses.other;
25429
+ if (!raw || raw === UNRECORDED_TRAFFIC_PATH) return TrafficPathClasses.other;
25429
25430
  const rawPath = raw.split(/[?#]/)[0] ?? "";
25430
25431
  const pathOnly = rawPath.length > 1 ? rawPath.replace(/\/+$/, "") || "/" : rawPath;
25431
25432
  if (!pathOnly) return TrafficPathClasses.other;
@@ -27888,6 +27889,7 @@ export {
27888
27889
  agentJourneysResponseSchema,
27889
27890
  agentJourneyDetailResponseSchema,
27890
27891
  TrafficPathClasses,
27892
+ UNRECORDED_TRAFFIC_PATH,
27891
27893
  classifyTrafficPath,
27892
27894
  segmentCrawlerHits,
27893
27895
  sumInfraHits,
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-WA4FD5Y6.js";
8
+ } from "./chunk-WR4PA335.js";
9
9
  import {
10
10
  CANONRY_NPM_PACKAGE_URL,
11
11
  compareSemver,
@@ -18,7 +18,7 @@ import {
18
18
  updateCheckEnvOptOut,
19
19
  upgradeCaveatFor,
20
20
  upgradeCommandFor
21
- } from "./chunk-PHVIMUAQ.js";
21
+ } from "./chunk-K3OY52MI.js";
22
22
 
23
23
  // src/runtime-context.ts
24
24
  function cliRuntimeContext(env = process.env, stdio = process) {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  trackIntegrationConnection
3
- } from "./chunk-P76XOEWL.js";
3
+ } from "./chunk-UYR5JI6R.js";
4
4
  import {
5
5
  cliRuntimeContext
6
- } from "./chunk-YJOGWS7F.js";
6
+ } from "./chunk-LE6ZODCY.js";
7
7
 
8
8
  // src/cli-connection-telemetry.ts
9
9
  function trackCliConnection(properties) {