@canonry/canonry 6.2.0 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +12 -0
  2. package/assets/assets/{AuditHistoryPanel-CuEMfkaA.js → AuditHistoryPanel-BUYWIjlG.js} +1 -1
  3. package/assets/assets/{BacklinksPage-CftadZ1-.js → BacklinksPage-aBcxpN_9.js} +1 -1
  4. package/assets/assets/{HistoryPage-DouRgshN.js → HistoryPage-PoBuxiY_.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-qfqv2qs9.js → MeasurementPropertyPage-DyjyqHac.js} +1 -1
  6. package/assets/assets/{ProjectPage-DqEVwe4t.js → ProjectPage-Mmv5YW-K.js} +1 -1
  7. package/assets/assets/{RunsPage-CUkMs0gB.js → RunsPage-CHHxOpzo.js} +1 -1
  8. package/assets/assets/{SettingsPage-LLQ40tSh.js → SettingsPage-CNKnrLXC.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-Ba7foDiU.js → SiteHealthSection-C9RivT7G.js} +3 -3
  10. package/assets/assets/{TrafficPage-Cu_ZR3--.js → TrafficPage-CY-ibXnb.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-NKZq7jf9.js → TrafficSourceDetailPage-BgiPXubs.js} +1 -1
  12. package/assets/assets/{extract-error-message-BdQYrz9N.js → extract-error-message-H1dzzfBP.js} +1 -1
  13. package/assets/assets/{index-HOeLz1ZL.js → index-DSCun3I9.js} +21 -21
  14. package/assets/assets/{react-sigma_core.esm.min-CZaoE5cU.js → react-sigma_core.esm.min-uiYtR_DU.js} +1 -1
  15. package/assets/assets/{v2-overview-adapter-BVx4nI49.js → v2-overview-adapter-BQokN2eb.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-2RFHQ6XT.js → chunk-4WZUR7XX.js} +240 -119
  18. package/dist/{chunk-4EKOKWQT.js → chunk-5FYTFE5T.js} +2 -2
  19. package/dist/{chunk-Y2B7JRSB.js → chunk-5GY5WBAF.js} +3 -3
  20. package/dist/{chunk-ENI3GOWW.js → chunk-7CVTTQRV.js} +95 -26
  21. package/dist/{chunk-W3GDZZNF.js → chunk-DERWXLHX.js} +15 -1
  22. package/dist/{chunk-6EPN4XY5.js → chunk-EV5G5QRR.js} +324 -280
  23. package/dist/{chunk-V6JUBTNL.js → chunk-ZZEPF3A5.js} +23 -3
  24. package/dist/cli.js +53 -8
  25. package/dist/{demo-server-CH7MOBXW.js → demo-server-AIFR77TP.js} +4 -4
  26. package/dist/index.js +6 -6
  27. package/dist/{intelligence-service-4IONMOFB.js → intelligence-service-SYISPW25.js} +2 -2
  28. package/dist/mcp.js +4 -4
  29. package/package.json +13 -13
@@ -19,21 +19,21 @@ import {
19
19
  runWithUsageTags,
20
20
  saveConfigPatch,
21
21
  sentimentInstallReadiness
22
- } from "./chunk-V6JUBTNL.js";
22
+ } from "./chunk-ZZEPF3A5.js";
23
23
  import {
24
24
  computeFactorAverages,
25
25
  deleteSiteCrawlGraphLayout,
26
26
  persistSiteCrawlGraphLayout,
27
27
  prepareSiteCrawlGraphLayout,
28
28
  recordedSharePct
29
- } from "./chunk-4EKOKWQT.js";
29
+ } from "./chunk-5FYTFE5T.js";
30
30
  import {
31
31
  CanonryMcpToolNames,
32
32
  CliError,
33
33
  PACKAGE_VERSION,
34
34
  canonryMcpTools,
35
35
  isMachineFormat
36
- } from "./chunk-W3GDZZNF.js";
36
+ } from "./chunk-DERWXLHX.js";
37
37
  import {
38
38
  CC_CACHE_DIR,
39
39
  DUCKDB_SPEC,
@@ -214,7 +214,7 @@ import {
214
214
  siteLivenessState,
215
215
  toAlertView,
216
216
  usageCounters
217
- } from "./chunk-ENI3GOWW.js";
217
+ } from "./chunk-7CVTTQRV.js";
218
218
  import {
219
219
  AGENT_MEMORY_VALUE_MAX_BYTES,
220
220
  AGENT_NONE,
@@ -232,6 +232,7 @@ import {
232
232
  CheckNotificationPolicies,
233
233
  DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
234
234
  DiscoveryCompetitorTypes,
235
+ FEEDBACK_LIMITS,
235
236
  GOOGLE_ADS_CAMPAIGN_METRICS_MAX_CAMPAIGNS,
236
237
  GOOGLE_ADS_CAMPAIGN_METRICS_MAX_DAYS,
237
238
  GOOGLE_ADS_CAMPAIGN_METRICS_MAX_ROWS,
@@ -316,6 +317,7 @@ import {
316
317
  contentBriefDtoSchema,
317
318
  createTemplateLinkDetectionTally,
318
319
  createTemplateLinkPairIndex,
320
+ deliveryFailed,
319
321
  deriveCitedUrlCandidates,
320
322
  deriveGoogleAdsEffectiveGoalGraph,
321
323
  deriveIndexCoverage,
@@ -385,7 +387,9 @@ import {
385
387
  percentOf,
386
388
  providerError,
387
389
  providerSupportsLocationContext,
390
+ quotaExceeded,
388
391
  ratioUnitSchema,
392
+ redactFeedbackText,
389
393
  registrableDomain,
390
394
  reportMonthsForDoctor,
391
395
  resolveNegativeReviewMaxStars,
@@ -416,7 +420,7 @@ import {
416
420
  visibilityReportRequestSchema,
417
421
  winnabilityClassLabel,
418
422
  withRetry
419
- } from "./chunk-6EPN4XY5.js";
423
+ } from "./chunk-EV5G5QRR.js";
420
424
 
421
425
  // src/runtime-context.ts
422
426
  function cliRuntimeContext(env = process.env, stdio = process) {
@@ -3631,7 +3635,7 @@ function createGoogleMarketingRuntime(options) {
3631
3635
 
3632
3636
  // src/server.ts
3633
3637
  import { createRequire as createRequire3 } from "module";
3634
- import crypto28 from "crypto";
3638
+ import crypto29 from "crypto";
3635
3639
  import fs9 from "fs";
3636
3640
  import path8 from "path";
3637
3641
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -3937,14 +3941,17 @@ var SentimentWorker = class {
3937
3941
  this.reconcile();
3938
3942
  const gate = this.repository.dispatchGate({ now: this.now(), credentialFingerprint: credentialFingerprint(config.apiKey) });
3939
3943
  if (gate === "closed") return 0;
3944
+ const used = this.repository.recentDispatch(this.now());
3945
+ if (used.tokens >= config.maxInputTokensPerMinute) return 0;
3946
+ const allowed = Math.min(gate === "probe" ? 1 : config.maxConcurrency, config.maxRequestsPerMinute - used.requests);
3940
3947
  const claims = [];
3941
- for (let index = 0; index < (gate === "probe" ? 1 : config.maxConcurrency); index++) {
3948
+ for (let index = 0; index < allowed; index++) {
3942
3949
  const claim = this.repository.claim({ owner: randomUUID(), now: this.now(), leaseMs: 12e4, maxConcurrent: config.maxConcurrency });
3943
3950
  if (!claim) break;
3944
3951
  claims.push(claim);
3945
3952
  }
3946
- await Promise.all(claims.map((work) => this.execute(work)));
3947
- return claims.length;
3953
+ const outcomes = await Promise.all(claims.map((work) => this.execute(work)));
3954
+ return outcomes.filter((outcome) => outcome !== "released").length;
3948
3955
  }
3949
3956
  async execute(work) {
3950
3957
  const owner = work.leaseOwner;
@@ -3986,6 +3993,11 @@ var SentimentWorker = class {
3986
3993
  }
3987
3994
  const attempt = this.repository.startAttempt({ workItemId: work.id, owner, requestedModel: input.definition.requestedModel, now: this.now(), estimatedInputTokens: estimate, maxRequestsPerMinute: config.maxRequestsPerMinute, maxAttempts: config.maxAttempts, maxInputTokensPerMinute: config.maxInputTokensPerMinute });
3988
3995
  if (!attempt) {
3996
+ const used = this.repository.recentDispatch(this.now());
3997
+ if (used.requests >= config.maxRequestsPerMinute || used.tokens + estimate > config.maxInputTokensPerMinute) {
3998
+ this.repository.releaseWork({ workItemId: work.id, owner, now: this.now() });
3999
+ return "released";
4000
+ }
3989
4001
  this.repository.failWork({ workItemId: work.id, owner, now: this.now(), errorCode: "RATE_LIMIT_WAIT", retryAt: new Date(Date.parse(this.now()) + 6e4).toISOString() });
3990
4002
  return;
3991
4003
  }
@@ -4026,6 +4038,28 @@ var SentimentWorker = class {
4026
4038
  }
4027
4039
  }
4028
4040
  };
4041
+ function createSentimentPoller(tick, onError, schedule = setImmediate) {
4042
+ let inFlight2 = null;
4043
+ let stopped = false;
4044
+ const poll = () => {
4045
+ if (inFlight2 || stopped) return;
4046
+ inFlight2 = tick().catch(() => {
4047
+ onError();
4048
+ return 0;
4049
+ }).then((claimed) => {
4050
+ inFlight2 = null;
4051
+ if (claimed > 0 && !stopped) schedule(poll);
4052
+ });
4053
+ };
4054
+ return {
4055
+ poll,
4056
+ /** The tick in flight, if any. */
4057
+ settled: () => inFlight2,
4058
+ stop: () => {
4059
+ stopped = true;
4060
+ }
4061
+ };
4062
+ }
4029
4063
 
4030
4064
  // src/sentiment-config.ts
4031
4065
  import fs2 from "fs";
@@ -7854,13 +7888,101 @@ function createApiUsageTelemetry(options = {}) {
7854
7888
  };
7855
7889
  }
7856
7890
 
7891
+ // src/feedback.ts
7892
+ import crypto4 from "crypto";
7893
+ var FEEDBACK_ENDPOINT = "https://canonry.ai/api/feedback";
7894
+ var TIMEOUT_MS2 = 1e4;
7895
+ var FEEDBACK_MAX_BODY_BYTES = 8e3;
7896
+ function feedbackSource(context) {
7897
+ const surface = classifyUsageSurface({
7898
+ userAgent: context.userAgent,
7899
+ usageLabels: { surface: context.surface }
7900
+ });
7901
+ if (surface === "cli" || surface === "dashboard") return surface;
7902
+ if (surface === "mcp-stdio" || surface === "mcp-http") return "mcp";
7903
+ return "api";
7904
+ }
7905
+ async function sendFeedback(submission, context, deps = {}) {
7906
+ const doFetch = deps.fetch ?? fetch;
7907
+ const telemetryOn = (deps.telemetryEnabled ?? isTelemetryEnabled)();
7908
+ const anonymousId = telemetryOn ? (deps.anonymousId ?? getOrCreateAnonymousId)() : void 0;
7909
+ const agent = [context.agent, context.mcpClient].map((label) => normalizeAgentSlug(label)).find((slug) => !!slug && slug !== "none");
7910
+ const payload = {
7911
+ feedbackId: crypto4.randomUUID(),
7912
+ ...anonymousId ? { anonymousId } : {},
7913
+ kind: submission.kind,
7914
+ summary: redactFeedbackText(submission.summary, FEEDBACK_LIMITS.summary),
7915
+ ...submission.details ? { details: redactFeedbackText(submission.details, FEEDBACK_LIMITS.details) } : {},
7916
+ ...submission.area ? { area: redactFeedbackText(submission.area, FEEDBACK_LIMITS.area) } : {},
7917
+ ...submission.command ? { command: redactFeedbackText(submission.command, FEEDBACK_LIMITS.command) } : {},
7918
+ ...submission.errorCode ? { errorCode: redactFeedbackText(submission.errorCode, FEEDBACK_LIMITS.errorCode) } : {},
7919
+ source: feedbackSource(context),
7920
+ ...agent ? { agent } : {},
7921
+ version: PACKAGE_VERSION.slice(0, 20),
7922
+ nodeVersion: process.versions.node.slice(0, 20),
7923
+ os: process.platform.slice(0, 20),
7924
+ arch: process.arch.slice(0, 20),
7925
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
7926
+ };
7927
+ const body = fitToByteBudget(payload);
7928
+ let response;
7929
+ try {
7930
+ response = await doFetch(FEEDBACK_ENDPOINT, {
7931
+ method: "POST",
7932
+ headers: { "Content-Type": "application/json" },
7933
+ body,
7934
+ signal: AbortSignal.timeout(TIMEOUT_MS2)
7935
+ });
7936
+ } catch {
7937
+ throw deliveryFailed("Could not reach canonry.ai to send feedback. Check the network and try again.");
7938
+ }
7939
+ if (response.status === 202 || response.ok) {
7940
+ const body2 = await response.json().catch(() => ({}));
7941
+ return { accepted: body2.accepted ?? true, id: typeof body2.id === "string" ? body2.id : payload.feedbackId };
7942
+ }
7943
+ if (response.status === 429) throw quotaExceeded("feedback submissions", { retryLater: true });
7944
+ if (response.status === 400 || response.status === 413) {
7945
+ const body2 = await response.json().catch(() => ({}));
7946
+ throw validationError("canonry.ai rejected the feedback", body2.issues ? { issues: body2.issues } : void 0);
7947
+ }
7948
+ throw deliveryFailed(`canonry.ai could not store the feedback (HTTP ${response.status}). Try again later.`);
7949
+ }
7950
+ function truncateCodePoints(value, maxCodePoints) {
7951
+ return Array.from(value).slice(0, Math.max(0, maxCodePoints)).join("");
7952
+ }
7953
+ function fitToByteBudget(payload) {
7954
+ let current = { ...payload };
7955
+ for (const field of ["details", "summary"]) {
7956
+ let serialized = JSON.stringify(current);
7957
+ let bytes = Buffer.byteLength(serialized, "utf8");
7958
+ while (bytes > FEEDBACK_MAX_BODY_BYTES) {
7959
+ const text = current[field];
7960
+ if (typeof text !== "string" || text.length === 0) break;
7961
+ const points = Array.from(text).length;
7962
+ const target = points - Math.max(1, Math.ceil((bytes - FEEDBACK_MAX_BODY_BYTES) / 4));
7963
+ if (field === "summary" && target < 1) break;
7964
+ const shorter = truncateCodePoints(text, target);
7965
+ if (field === "details" && shorter.length === 0) {
7966
+ const { details: _dropped, ...rest } = current;
7967
+ current = rest;
7968
+ } else {
7969
+ current = { ...current, [field]: shorter };
7970
+ }
7971
+ serialized = JSON.stringify(current);
7972
+ bytes = Buffer.byteLength(serialized, "utf8");
7973
+ }
7974
+ if (bytes <= FEEDBACK_MAX_BODY_BYTES) return serialized;
7975
+ }
7976
+ return JSON.stringify(current);
7977
+ }
7978
+
7857
7979
  // src/instance-identity.ts
7858
7980
  function nonBlank(value) {
7859
7981
  const trimmed = value?.trim();
7860
7982
  return trimmed ? trimmed : void 0;
7861
7983
  }
7862
7984
  function resolveBuildCommit(env = process.env) {
7863
- const embedded = true ? "633bd63832cf6af1da610e63f376dfc872975b49" : void 0;
7985
+ const embedded = true ? "a9545eefb45af8bb1a00366b6e75ce25bb3f4c2b" : void 0;
7864
7986
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
7865
7987
  }
7866
7988
  function resolveInstanceIdentity(env = process.env) {
@@ -7871,7 +7993,7 @@ function resolveInstanceIdentity(env = process.env) {
7871
7993
  }
7872
7994
 
7873
7995
  // src/job-runner.ts
7874
- import crypto6 from "crypto";
7996
+ import crypto7 from "crypto";
7875
7997
  import fs4 from "fs";
7876
7998
  import path3 from "path";
7877
7999
  import os3 from "os";
@@ -7879,7 +8001,7 @@ import { setImmediate as yieldToEventLoop } from "timers/promises";
7879
8001
  import { and as and3, asc as asc2, count, desc, eq as eq3, inArray, isNotNull, isNull, lt, ne, sql as sql2 } from "drizzle-orm";
7880
8002
 
7881
8003
  // src/run-telemetry.ts
7882
- import crypto4 from "crypto";
8004
+ import crypto5 from "crypto";
7883
8005
  function extractRegistrableHost(input) {
7884
8006
  if (!input) return null;
7885
8007
  const trimmed = input.trim();
@@ -7899,7 +8021,7 @@ function extractRegistrableHost(input) {
7899
8021
  function hashDomain(input) {
7900
8022
  const host = extractRegistrableHost(input);
7901
8023
  if (!host) return null;
7902
- return crypto4.createHash("sha256").update(host).digest("hex");
8024
+ return crypto5.createHash("sha256").update(host).digest("hex");
7903
8025
  }
7904
8026
  function buildRunCompletedProps(input) {
7905
8027
  const totalMs = input.phases?.total_ms ?? Date.now() - input.startTime;
@@ -7980,7 +8102,7 @@ function failureStreakSampling(status, priorStreak, runId) {
7980
8102
  if (priorStreak < FAILURE_STREAK_SAMPLE_AFTER) {
7981
8103
  return { report: true, props: { failureStreak: priorStreak } };
7982
8104
  }
7983
- const bucket = crypto4.createHash("sha256").update(runId).digest().readUInt32BE(0) % FAILURE_STREAK_SAMPLE_RATE;
8105
+ const bucket = crypto5.createHash("sha256").update(runId).digest().readUInt32BE(0) % FAILURE_STREAK_SAMPLE_RATE;
7984
8106
  return {
7985
8107
  report: bucket === 0,
7986
8108
  props: { failureStreak: priorStreak, sampleRate: FAILURE_STREAK_SAMPLE_RATE }
@@ -8053,7 +8175,7 @@ function getSharedProviderExecutionGate(providerName, maxConcurrency, maxPerMinu
8053
8175
  }
8054
8176
 
8055
8177
  // src/usage-quota.ts
8056
- import crypto5 from "crypto";
8178
+ import crypto6 from "crypto";
8057
8179
  import { and as and2, eq as eq2, lte, sql } from "drizzle-orm";
8058
8180
  function getCurrentUsageDay() {
8059
8181
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -8070,7 +8192,7 @@ function reserveDailyQueryQuota(db, input) {
8070
8192
  )).run();
8071
8193
  if (reserveExisting().changes === 1) return { reserved: true, used: 0 };
8072
8194
  const inserted = tx.insert(usageCounters).values({
8073
- id: crypto5.randomUUID(),
8195
+ id: crypto6.randomUUID(),
8074
8196
  scope: input.scope,
8075
8197
  period: input.period,
8076
8198
  metric: "queries",
@@ -8665,7 +8787,7 @@ var JobRunner = class {
8665
8787
  const answerLocation = runLocation && isSearchLocationIgnored(providerName, normalized.retrievalStatus) ? { location: null, requestedContext: runLocation, supportedContext: { status: "ignored" } } : { location: runLocation?.label ?? null };
8666
8788
  let screenshotRelPath = null;
8667
8789
  if (raw.screenshotPath && fs4.existsSync(raw.screenshotPath)) {
8668
- const snapshotId = crypto6.randomUUID();
8790
+ const snapshotId = crypto7.randomUUID();
8669
8791
  const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", runId);
8670
8792
  if (!fs4.existsSync(screenshotDir)) fs4.mkdirSync(screenshotDir, { recursive: true });
8671
8793
  const destPath = path3.join(screenshotDir, `${snapshotId}.png`);
@@ -8709,7 +8831,7 @@ var JobRunner = class {
8709
8831
  }).run();
8710
8832
  } else {
8711
8833
  this.db.insert(querySnapshots).values({
8712
- id: crypto6.randomUUID(),
8834
+ id: crypto7.randomUUID(),
8713
8835
  runId,
8714
8836
  queryId: q.id,
8715
8837
  queryText: q.query,
@@ -9249,7 +9371,7 @@ var JobRunner = class {
9249
9371
  syncUnits.push(unit);
9250
9372
  continue;
9251
9373
  }
9252
- const customId = crypto6.randomUUID().replace(/-/g, "");
9374
+ const customId = crypto7.randomUUID().replace(/-/g, "");
9253
9375
  const lines = linesByModel.get(model) ?? [];
9254
9376
  lines.push({ unit, requestedModel, customId, request, bytes: Buffer.byteLength(JSON.stringify({ custom_id: customId, params: request.body })) });
9255
9377
  linesByModel.set(model, lines);
@@ -9285,7 +9407,7 @@ var JobRunner = class {
9285
9407
  if (!capability || !reservation) throw new Error(`Provider ${providerName} cannot submit a batch for run ${ctx.runId}`);
9286
9408
  const deadlineMs = (config.batch?.deadlineHours ?? capability.defaultDeadlineHours) * HOUR_MS;
9287
9409
  const created = /* @__PURE__ */ new Date();
9288
- const rowId = crypto6.randomUUID();
9410
+ const rowId = crypto7.randomUUID();
9289
9411
  const ledger = lines.map((line) => ({
9290
9412
  id: line.customId,
9291
9413
  batchId: rowId,
@@ -9978,7 +10100,7 @@ var JobRunner = class {
9978
10100
  competitorAliases
9979
10101
  );
9980
10102
  const answerContext = supportedContext && isSearchLocationIgnored(providerName, normalized.retrievalStatus) ? { status: "ignored" } : supportedContext;
9981
- const snapshotId = crypto6.randomUUID();
10103
+ const snapshotId = crypto7.randomUUID();
9982
10104
  let screenshotRelPath = null;
9983
10105
  if (raw.screenshotPath && fs4.existsSync(raw.screenshotPath)) {
9984
10106
  const screenshotDir = path3.join(os3.homedir(), ".canonry", "screenshots", ctx.runId);
@@ -10080,7 +10202,7 @@ var JobRunner = class {
10080
10202
  const now = (/* @__PURE__ */ new Date()).toISOString();
10081
10203
  const period = now.slice(0, 10);
10082
10204
  this.db.insert(usageCounters).values({
10083
- id: crypto6.randomUUID(),
10205
+ id: crypto7.randomUUID(),
10084
10206
  scope,
10085
10207
  period,
10086
10208
  metric,
@@ -10214,11 +10336,11 @@ function maybeShowActivationNotice(io = {}) {
10214
10336
  }
10215
10337
 
10216
10338
  // src/gsc-sync.ts
10217
- import crypto8 from "crypto";
10339
+ import crypto9 from "crypto";
10218
10340
  import { eq as eq5, and as and5, sql as sql3 } from "drizzle-orm";
10219
10341
 
10220
10342
  // src/gsc-coverage-snapshot.ts
10221
- import crypto7 from "crypto";
10343
+ import crypto8 from "crypto";
10222
10344
  import { and as and4, desc as desc2, eq as eq4, gte } from "drizzle-orm";
10223
10345
  var COVERAGE_IMPRESSION_WINDOW_DAYS = 30;
10224
10346
  function pageKey(raw) {
@@ -10280,7 +10402,7 @@ function writeCoverageSnapshot(db, projectId, runId, opts = {}) {
10280
10402
  const snapshotDate = now.toISOString().split("T")[0];
10281
10403
  db.delete(gscCoverageSnapshots).where(and4(eq4(gscCoverageSnapshots.projectId, projectId), eq4(gscCoverageSnapshots.date, snapshotDate))).run();
10282
10404
  db.insert(gscCoverageSnapshots).values({
10283
- id: crypto7.randomUUID(),
10405
+ id: crypto8.randomUUID(),
10284
10406
  projectId,
10285
10407
  syncRunId: runId,
10286
10408
  date: snapshotDate,
@@ -10359,7 +10481,7 @@ async function executeGscSync(db, runId, projectId, opts) {
10359
10481
  for (const row of batch) {
10360
10482
  const [query, page, country, device, date] = row.keys;
10361
10483
  db.insert(gscSearchData).values({
10362
- id: crypto8.randomUUID(),
10484
+ id: crypto9.randomUUID(),
10363
10485
  projectId,
10364
10486
  syncRunId: runId,
10365
10487
  date: date ?? "",
@@ -10391,7 +10513,7 @@ async function executeGscSync(db, runId, projectId, opts) {
10391
10513
  for (const row of totalRows) {
10392
10514
  const [date] = row.keys;
10393
10515
  db.insert(gscDailyTotals).values({
10394
- id: crypto8.randomUUID(),
10516
+ id: crypto9.randomUUID(),
10395
10517
  projectId,
10396
10518
  date: date ?? "",
10397
10519
  clicks: row.clicks,
@@ -10440,7 +10562,7 @@ async function executeGscSync(db, runId, projectId, opts) {
10440
10562
  const [date, query] = row.keys;
10441
10563
  if (!date || !query) continue;
10442
10564
  db.insert(gscQueryDailyTotals).values({
10443
- id: crypto8.randomUUID(),
10565
+ id: crypto9.randomUUID(),
10444
10566
  projectId,
10445
10567
  date,
10446
10568
  query,
@@ -10465,11 +10587,11 @@ async function executeGscSync(db, runId, projectId, opts) {
10465
10587
  }
10466
10588
 
10467
10589
  // src/gbp-sync.ts
10468
- import crypto10 from "crypto";
10590
+ import crypto11 from "crypto";
10469
10591
  import { eq as eq7, and as and7, desc as desc4, inArray as inArray3, lt as lt2 } from "drizzle-orm";
10470
10592
 
10471
10593
  // src/gbp-reviews.ts
10472
- import crypto9 from "crypto";
10594
+ import crypto10 from "crypto";
10473
10595
  import { and as and6, desc as desc3, eq as eq6, inArray as inArray2, sql as sql4 } from "drizzle-orm";
10474
10596
  var log4 = createLogger("GbpReviews");
10475
10597
  var MS_PER_DAY = 864e5;
@@ -10588,7 +10710,7 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
10588
10710
  let queuedRatingDrop = false;
10589
10711
  if (!latestRating) {
10590
10712
  tx.insert(gbpReviewRatings).values({
10591
- id: crypto9.randomUUID(),
10713
+ id: crypto10.randomUUID(),
10592
10714
  projectId,
10593
10715
  locationName: obs.locationName,
10594
10716
  origin: obs.origin,
@@ -10608,7 +10730,7 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
10608
10730
  const alertState = ratingAlertState(obs.origin, latestRating.rating, obs.rating);
10609
10731
  queuedRatingDrop = alertState === "pending";
10610
10732
  tx.insert(gbpReviewRatings).values({
10611
- id: crypto9.randomUUID(),
10733
+ id: crypto10.randomUUID(),
10612
10734
  projectId,
10613
10735
  locationName: obs.locationName,
10614
10736
  origin: obs.origin,
@@ -10658,7 +10780,7 @@ function persistReviewObservation(tx, projectId, runId, obs, ctx) {
10658
10780
  const alertState = reviewAlertState(review, { baseline, now, negativeMaxStars });
10659
10781
  if (alertState === "pending") queuedReviews++;
10660
10782
  tx.insert(gbpReviews).values({
10661
- id: crypto9.randomUUID(),
10783
+ id: crypto10.randomUUID(),
10662
10784
  projectId,
10663
10785
  origin: obs.origin,
10664
10786
  reviewName: review.reviewName,
@@ -10819,7 +10941,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10819
10941
  tx.delete(gbpDailyMetrics).where(and7(eq7(gbpDailyMetrics.projectId, projectId), eq7(gbpDailyMetrics.locationName, loc.locationName))).run();
10820
10942
  for (const row of metricRows) {
10821
10943
  tx.insert(gbpDailyMetrics).values({
10822
- id: crypto10.randomUUID(),
10944
+ id: crypto11.randomUUID(),
10823
10945
  projectId,
10824
10946
  locationName: loc.locationName,
10825
10947
  date: row.date,
@@ -10831,7 +10953,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10831
10953
  tx.delete(gbpKeywordImpressions).where(and7(eq7(gbpKeywordImpressions.projectId, projectId), eq7(gbpKeywordImpressions.locationName, loc.locationName))).run();
10832
10954
  for (const row of keywordRows) {
10833
10955
  tx.insert(gbpKeywordImpressions).values({
10834
- id: crypto10.randomUUID(),
10956
+ id: crypto11.randomUUID(),
10835
10957
  projectId,
10836
10958
  locationName: loc.locationName,
10837
10959
  periodStart: monthKey(keywordsStart),
@@ -10852,7 +10974,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10852
10974
  for (const { month, rows } of monthlyKeywordResults) {
10853
10975
  for (const row of rows) {
10854
10976
  tx.insert(gbpKeywordMonthly).values({
10855
- id: crypto10.randomUUID(),
10977
+ id: crypto11.randomUUID(),
10856
10978
  projectId,
10857
10979
  locationName: loc.locationName,
10858
10980
  month,
@@ -10873,7 +10995,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10873
10995
  tx.delete(gbpPlaceActions).where(and7(eq7(gbpPlaceActions.projectId, projectId), eq7(gbpPlaceActions.locationName, loc.locationName))).run();
10874
10996
  for (const row of placeActionRows) {
10875
10997
  tx.insert(gbpPlaceActions).values({
10876
- id: crypto10.randomUUID(),
10998
+ id: crypto11.randomUUID(),
10877
10999
  projectId,
10878
11000
  locationName: loc.locationName,
10879
11001
  placeActionLinkName: row.placeActionLinkName,
@@ -10886,7 +11008,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10886
11008
  }
10887
11009
  if (lodging !== null && lodgingChanged) {
10888
11010
  tx.insert(gbpLodgingSnapshots).values({
10889
- id: crypto10.randomUUID(),
11011
+ id: crypto11.randomUUID(),
10890
11012
  projectId,
10891
11013
  locationName: loc.locationName,
10892
11014
  contentHash: lodgingHash,
@@ -10900,7 +11022,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10900
11022
  }
10901
11023
  if (attributesChanged) {
10902
11024
  tx.insert(gbpAttributesSnapshots).values({
10903
- id: crypto10.randomUUID(),
11025
+ id: crypto11.randomUUID(),
10904
11026
  projectId,
10905
11027
  locationName: loc.locationName,
10906
11028
  contentHash: attributesHash,
@@ -10914,7 +11036,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
10914
11036
  }
10915
11037
  if (placeToWrite) {
10916
11038
  tx.insert(gbpPlaceDetails).values({
10917
- id: crypto10.randomUUID(),
11039
+ id: crypto11.randomUUID(),
10918
11040
  projectId,
10919
11041
  locationName: loc.locationName,
10920
11042
  placeId: placeToWrite.placeId,
@@ -11019,7 +11141,7 @@ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locat
11019
11141
  }
11020
11142
 
11021
11143
  // src/ads-sync.ts
11022
- import crypto11 from "crypto";
11144
+ import crypto12 from "crypto";
11023
11145
  import { and as and8, eq as eq8, inArray as inArray4, notInArray } from "drizzle-orm";
11024
11146
 
11025
11147
  // ../integration-openai-ads/src/constants.ts
@@ -11794,7 +11916,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
11794
11916
  }
11795
11917
  for (const upsert of insightUpserts) {
11796
11918
  tx.insert(adsInsightsDaily).values({
11797
- id: crypto11.randomUUID(),
11919
+ id: crypto12.randomUUID(),
11798
11920
  projectId,
11799
11921
  level: upsert.level,
11800
11922
  entityId: upsert.entityId,
@@ -11862,7 +11984,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
11862
11984
  }
11863
11985
 
11864
11986
  // src/gsc-inspect-sitemap.ts
11865
- import crypto13 from "crypto";
11987
+ import crypto14 from "crypto";
11866
11988
  import { eq as eq9 } from "drizzle-orm";
11867
11989
 
11868
11990
  // src/sitemap-parser.ts
@@ -11962,7 +12084,7 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
11962
12084
  }
11963
12085
 
11964
12086
  // src/inspect-rate-gate.ts
11965
- import crypto12 from "crypto";
12087
+ import crypto13 from "crypto";
11966
12088
  var gates = /* @__PURE__ */ new Map();
11967
12089
  function sharedRateGate(key) {
11968
12090
  let entry = gates.get(key);
@@ -11981,7 +12103,7 @@ function sharedRateGate(key) {
11981
12103
  };
11982
12104
  }
11983
12105
  function credentialGateKey(prefix, secret) {
11984
- const digest = crypto12.createHash("sha256").update(secret).digest("hex").slice(0, 12);
12106
+ const digest = crypto13.createHash("sha256").update(secret).digest("hex").slice(0, 12);
11985
12107
  return `${prefix}:${digest}`;
11986
12108
  }
11987
12109
 
@@ -12136,7 +12258,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
12136
12258
  const rich = ir.richResultsResult;
12137
12259
  const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
12138
12260
  db.insert(gscUrlInspections).values({
12139
- id: crypto13.randomUUID(),
12261
+ id: crypto14.randomUUID(),
12140
12262
  projectId,
12141
12263
  syncRunId: runId,
12142
12264
  url: pageUrl,
@@ -12194,7 +12316,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
12194
12316
  }
12195
12317
 
12196
12318
  // src/bing-inspect-sitemap.ts
12197
- import crypto14 from "crypto";
12319
+ import crypto15 from "crypto";
12198
12320
  import { eq as eq10, desc as desc5 } from "drizzle-orm";
12199
12321
  var log9 = createLogger("BingInspectSitemap");
12200
12322
  var BING_INSPECT_SPACING_MS = 2e3;
@@ -12299,7 +12421,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
12299
12421
  derivedInIndex = false;
12300
12422
  }
12301
12423
  db.insert(bingUrlInspections).values({
12302
- id: crypto14.randomUUID(),
12424
+ id: crypto15.randomUUID(),
12303
12425
  projectId,
12304
12426
  url: pageUrl,
12305
12427
  httpCode,
@@ -12383,7 +12505,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
12383
12505
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
12384
12506
  const snapNow = (/* @__PURE__ */ new Date()).toISOString();
12385
12507
  db.insert(bingCoverageSnapshots).values({
12386
- id: crypto14.randomUUID(),
12508
+ id: crypto15.randomUUID(),
12387
12509
  projectId,
12388
12510
  syncRunId: runId,
12389
12511
  date: snapshotDate,
@@ -12424,7 +12546,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
12424
12546
  }
12425
12547
 
12426
12548
  // src/coverage-refresh.ts
12427
- import crypto15 from "crypto";
12549
+ import crypto16 from "crypto";
12428
12550
  import { and as and9, desc as desc6, eq as eq11, inArray as inArray5 } from "drizzle-orm";
12429
12551
  var log10 = createLogger("CoverageRefresh");
12430
12552
  var COVERAGE_REFRESH_MIN_INTERVAL_MS = 60 * 60 * 1e3;
@@ -12474,7 +12596,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
12474
12596
  return null;
12475
12597
  }
12476
12598
  }
12477
- const runId = crypto15.randomUUID();
12599
+ const runId = crypto16.randomUUID();
12478
12600
  db.insert(runs).values({
12479
12601
  id: runId,
12480
12602
  projectId,
@@ -12497,7 +12619,7 @@ async function maybeRefreshGscCoverage(db, config, projectId, deps = defaultDeps
12497
12619
  }
12498
12620
 
12499
12621
  // src/commoncrawl-sync.ts
12500
- import crypto16 from "crypto";
12622
+ import crypto17 from "crypto";
12501
12623
  import path4 from "path";
12502
12624
  import { and as and10, eq as eq12, sql as sql5 } from "drizzle-orm";
12503
12625
  var log11 = createLogger("CommonCrawlSync");
@@ -12572,7 +12694,7 @@ async function executeReleaseSync(db, syncId, opts) {
12572
12694
  if (!projectIds) continue;
12573
12695
  for (const projectId of projectIds) {
12574
12696
  expanded.push({
12575
- id: crypto16.randomUUID(),
12697
+ id: crypto17.randomUUID(),
12576
12698
  projectId,
12577
12699
  releaseSyncId: syncId,
12578
12700
  release,
@@ -12592,7 +12714,7 @@ async function executeReleaseSync(db, syncId, opts) {
12592
12714
  const projectRows = rowsByProject.get(p.id) ?? [];
12593
12715
  const summary = computeSummary(projectRows);
12594
12716
  tx.insert(backlinkSummaries).values({
12595
- id: crypto16.randomUUID(),
12717
+ id: crypto17.randomUUID(),
12596
12718
  projectId: p.id,
12597
12719
  releaseSyncId: syncId,
12598
12720
  source: BacklinkSources.commoncrawl,
@@ -12689,7 +12811,7 @@ function computeSummary(rows) {
12689
12811
  }
12690
12812
 
12691
12813
  // src/backlink-extract.ts
12692
- import crypto17 from "crypto";
12814
+ import crypto18 from "crypto";
12693
12815
  import fs5 from "fs";
12694
12816
  import { and as and11, desc as desc7, eq as eq13 } from "drizzle-orm";
12695
12817
  var log12 = createLogger("BacklinkExtract");
@@ -12745,7 +12867,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
12745
12867
  ).run();
12746
12868
  if (rows.length > 0) {
12747
12869
  const values = rows.map((r) => ({
12748
- id: crypto17.randomUUID(),
12870
+ id: crypto18.randomUUID(),
12749
12871
  projectId,
12750
12872
  releaseSyncId: syncId,
12751
12873
  source: BacklinkSources.commoncrawl,
@@ -12759,7 +12881,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
12759
12881
  }
12760
12882
  const summary = computeSummary2(rows);
12761
12883
  tx.insert(backlinkSummaries).values({
12762
- id: crypto17.randomUUID(),
12884
+ id: crypto18.randomUUID(),
12763
12885
  projectId,
12764
12886
  releaseSyncId: syncId,
12765
12887
  source: BacklinkSources.commoncrawl,
@@ -12802,7 +12924,7 @@ function computeSummary2(rows) {
12802
12924
  }
12803
12925
 
12804
12926
  // src/discovery-run.ts
12805
- import crypto18 from "crypto";
12927
+ import crypto19 from "crypto";
12806
12928
  import { and as and12, eq as eq14 } from "drizzle-orm";
12807
12929
  var log13 = createLogger("DiscoveryRun");
12808
12930
  var DEFAULT_SEED_COUNT = 30;
@@ -13157,7 +13279,7 @@ function writeDiscoveryInsight(db, input) {
13157
13279
  eq14(insights.dismissed, false)
13158
13280
  )).run();
13159
13281
  tx.insert(insights).values({
13160
- id: crypto18.randomUUID(),
13282
+ id: crypto19.randomUUID(),
13161
13283
  projectId: input.projectId,
13162
13284
  runId: input.runId,
13163
13285
  type: "discovery.basket-divergence",
@@ -13193,7 +13315,7 @@ function buildDiscoveryInsightTitle(input) {
13193
13315
  }
13194
13316
 
13195
13317
  // src/execute-site-audit.ts
13196
- import crypto19 from "crypto";
13318
+ import crypto20 from "crypto";
13197
13319
  import { and as and14, eq as eq16, ne as ne2, sql as sql6 } from "drizzle-orm";
13198
13320
  import { runSiteCrawl } from "@canonry/aeo-audit";
13199
13321
 
@@ -13644,7 +13766,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
13644
13766
  const trigger = db.select({ trigger: runs.trigger }).from(runs).where(eq16(runs.id, runId)).get()?.trigger ?? null;
13645
13767
  let canonicalDomain = null;
13646
13768
  const attemptNumber = (db.select({ value: sql6`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq16(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
13647
- const attemptId = crypto19.randomUUID();
13769
+ const attemptId = crypto20.randomUUID();
13648
13770
  db.insert(siteCrawlAttempts).values({
13649
13771
  id: attemptId,
13650
13772
  projectId,
@@ -13722,7 +13844,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
13722
13844
  nodeKey: page.key
13723
13845
  });
13724
13846
  tx.insert(siteCrawlPages).values({
13725
- id: crypto19.randomUUID(),
13847
+ id: crypto20.randomUUID(),
13726
13848
  projectId,
13727
13849
  runId,
13728
13850
  attemptId,
@@ -13815,7 +13937,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
13815
13937
  if (isSelfLink(edge.from, edge.to)) return;
13816
13938
  observedEdges.set(edge.key, edge);
13817
13939
  tx.insert(siteCrawlEdges).values({
13818
- id: crypto19.randomUUID(),
13940
+ id: crypto20.randomUUID(),
13819
13941
  projectId,
13820
13942
  runId,
13821
13943
  attemptId,
@@ -13885,7 +14007,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
13885
14007
  return;
13886
14008
  }
13887
14009
  tx.insert(siteCrawlEventReceipts).values({
13888
- id: crypto19.randomUUID(),
14010
+ id: crypto20.randomUUID(),
13889
14011
  projectId,
13890
14012
  runId,
13891
14013
  attemptId,
@@ -14021,7 +14143,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
14021
14143
  if (claim2.changes === 0) return false;
14022
14144
  if (hasAuditedPages) {
14023
14145
  tx.insert(siteAuditSnapshots).values({
14024
- id: crypto19.randomUUID(),
14146
+ id: crypto20.randomUUID(),
14025
14147
  projectId,
14026
14148
  runId,
14027
14149
  sitemapUrl: opts.sitemapUrl ?? new URL("/sitemap.xml", crawlRootUrl).href,
@@ -14039,7 +14161,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
14039
14161
  for (const page of observedPages.values()) {
14040
14162
  if (!isLegacyScorecardPage(page)) continue;
14041
14163
  tx.insert(siteAuditPages).values({
14042
- id: crypto19.randomUUID(),
14164
+ id: crypto20.randomUUID(),
14043
14165
  projectId,
14044
14166
  runId,
14045
14167
  url: page.finalUrl ?? page.requestedUrl,
@@ -14052,7 +14174,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
14052
14174
  }
14053
14175
  }
14054
14176
  tx.insert(siteCrawlSnapshots).values({
14055
- id: crypto19.randomUUID(),
14177
+ id: crypto20.randomUUID(),
14056
14178
  projectId,
14057
14179
  runId,
14058
14180
  attemptId,
@@ -14098,7 +14220,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
14098
14220
  if (opts.checkDeadLinks) {
14099
14221
  for (const finding of deadLinkFindings) {
14100
14222
  tx.insert(siteCrawlFindings).values({
14101
- id: crypto19.randomUUID(),
14223
+ id: crypto20.randomUUID(),
14102
14224
  projectId,
14103
14225
  runId,
14104
14226
  attemptId,
@@ -14673,7 +14795,7 @@ function readStoredGroundingSources(rawResponse) {
14673
14795
  return result2;
14674
14796
  }
14675
14797
  async function backfillInsightsCommand(project, opts) {
14676
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4IONMOFB.js");
14798
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-SYISPW25.js");
14677
14799
  const config = loadConfig();
14678
14800
  const db = createClient(config.database);
14679
14801
  migrate(db);
@@ -15263,7 +15385,7 @@ var ProviderRegistry = class {
15263
15385
  import { createHash as createHash4 } from "crypto";
15264
15386
  var TTL_MS = 60 * 60 * 1e3;
15265
15387
  var RETRY_MS = 60 * 1e3;
15266
- var TIMEOUT_MS2 = 3e3;
15388
+ var TIMEOUT_MS3 = 3e3;
15267
15389
  function createProviderModelCatalog(registry) {
15268
15390
  const cache = /* @__PURE__ */ new Map();
15269
15391
  const read = async (name) => {
@@ -15289,7 +15411,7 @@ function createProviderModelCatalog(registry) {
15289
15411
  timer = setTimeout(() => {
15290
15412
  controller.abort();
15291
15413
  reject(new Error("Model discovery timed out"));
15292
- }, TIMEOUT_MS2);
15414
+ }, TIMEOUT_MS3);
15293
15415
  });
15294
15416
  current.pending = Promise.race([
15295
15417
  Promise.resolve().then(() => provider.adapter.listModels(provider.config, controller.signal)),
@@ -15320,14 +15442,14 @@ function createProviderModelCatalog(registry) {
15320
15442
  }
15321
15443
 
15322
15444
  // src/scheduler.ts
15323
- import crypto20 from "crypto";
15445
+ import crypto21 from "crypto";
15324
15446
  import cron from "node-cron";
15325
15447
  import { and as and16, eq as eq18, inArray as inArray8, notExists, sql as sql8 } from "drizzle-orm";
15326
15448
  var log15 = createLogger("Scheduler");
15327
15449
  var DEFAULT_HEALTH_CRON = "0 */6 * * *";
15328
15450
  function ensureDefaultHealthSchedule(db, projectId, now = (/* @__PURE__ */ new Date()).toISOString()) {
15329
15451
  const result2 = db.insert(schedules).values({
15330
- id: crypto20.randomUUID(),
15452
+ id: crypto21.randomUUID(),
15331
15453
  projectId,
15332
15454
  kind: SchedulableRunKinds.doctor,
15333
15455
  cronExpr: DEFAULT_HEALTH_CRON,
@@ -15645,7 +15767,7 @@ var Scheduler = class {
15645
15767
  log15.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
15646
15768
  return;
15647
15769
  }
15648
- const runId2 = crypto20.randomUUID();
15770
+ const runId2 = crypto21.randomUUID();
15649
15771
  this.db.insert(runs).values({
15650
15772
  id: runId2,
15651
15773
  projectId,
@@ -15677,7 +15799,7 @@ var Scheduler = class {
15677
15799
  this.updateScheduleTiming(currentSchedule.id, { nextRunAt });
15678
15800
  return;
15679
15801
  }
15680
- const runId2 = crypto20.randomUUID();
15802
+ const runId2 = crypto21.randomUUID();
15681
15803
  this.db.insert(runs).values({
15682
15804
  id: runId2,
15683
15805
  projectId,
@@ -15748,7 +15870,7 @@ var Scheduler = class {
15748
15870
  this.updateScheduleTiming(currentSchedule.id, { nextRunAt });
15749
15871
  return;
15750
15872
  }
15751
- const runId2 = crypto20.randomUUID();
15873
+ const runId2 = crypto21.randomUUID();
15752
15874
  const effectiveRequest = normalizeSiteAuditRunRequest({ maxPages: SITE_AUDIT_MAX_PAGE_LIMIT });
15753
15875
  this.db.transaction((tx) => {
15754
15876
  tx.insert(runs).values({
@@ -16091,7 +16213,7 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
16091
16213
 
16092
16214
  // src/notifier.ts
16093
16215
  import { eq as eq20, desc as desc9, and as and18, inArray as inArray10, or } from "drizzle-orm";
16094
- import crypto21 from "crypto";
16216
+ import crypto22 from "crypto";
16095
16217
  var log19 = createLogger("Notifier");
16096
16218
  var MATERIAL_MAGNITUDE_DELTA = 20;
16097
16219
  var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
@@ -16718,7 +16840,7 @@ var Notifier = class {
16718
16840
  }
16719
16841
  logDelivery(projectId, notificationId, event, status, error) {
16720
16842
  this.db.insert(auditLog).values({
16721
- id: crypto21.randomUUID(),
16843
+ id: crypto22.randomUUID(),
16722
16844
  projectId,
16723
16845
  actor: "scheduler",
16724
16846
  action: `notification.${status}`,
@@ -16894,7 +17016,7 @@ function analysisResultFromInsights(insights2) {
16894
17016
  }
16895
17017
 
16896
17018
  // src/agent/session-registry.ts
16897
- import crypto25 from "crypto";
17019
+ import crypto26 from "crypto";
16898
17020
  import { eq as eq24 } from "drizzle-orm";
16899
17021
 
16900
17022
  // src/agent/session.ts
@@ -18147,7 +18269,7 @@ function buildAeroStateTools(ctx, opts = {}) {
18147
18269
  }
18148
18270
 
18149
18271
  // src/agent/llm-usage.ts
18150
- import crypto22 from "crypto";
18272
+ import crypto23 from "crypto";
18151
18273
  var AeroLlmUsageFeatures = {
18152
18274
  turn: "aero.turn"
18153
18275
  };
@@ -18166,7 +18288,7 @@ function recordLlmUsageEvent(args) {
18166
18288
  const usage = args.message.usage;
18167
18289
  const now = (/* @__PURE__ */ new Date()).toISOString();
18168
18290
  args.db.insert(llmUsageEvents).values({
18169
- id: crypto22.randomUUID(),
18291
+ id: crypto23.randomUUID(),
18170
18292
  projectId: args.projectId,
18171
18293
  runId: args.runId,
18172
18294
  agentSessionId: args.agentSessionId,
@@ -18536,7 +18658,7 @@ This context applies to this turn. Explicit user scope takes precedence; do not
18536
18658
  }
18537
18659
 
18538
18660
  // src/agent/tool-usage.ts
18539
- import crypto23 from "crypto";
18661
+ import crypto24 from "crypto";
18540
18662
  var AeroToolEventStatuses = {
18541
18663
  success: "success",
18542
18664
  error: "error"
@@ -18599,7 +18721,7 @@ function createAeroToolUsageHooks(args) {
18599
18721
  function recordAgentToolEvent(args) {
18600
18722
  try {
18601
18723
  args.db.insert(agentToolEvents).values({
18602
- id: crypto23.randomUUID(),
18724
+ id: crypto24.randomUUID(),
18603
18725
  projectId: args.projectId,
18604
18726
  agentSessionId: args.agentSessionId,
18605
18727
  toolCallId: args.toolCallId,
@@ -18854,7 +18976,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
18854
18976
  }
18855
18977
 
18856
18978
  // src/agent/memory-store.ts
18857
- import crypto24 from "crypto";
18979
+ import crypto25 from "crypto";
18858
18980
  import { and as and19, desc as desc10, eq as eq22, like, sql as sql10, or as or2 } from "drizzle-orm";
18859
18981
  var COMPACTION_KEY_PREFIX = "compaction:";
18860
18982
  var COMPACTION_NOTES_PER_SESSION = 3;
@@ -18883,7 +19005,7 @@ function upsertMemoryEntry(db, args) {
18883
19005
  throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
18884
19006
  }
18885
19007
  const now = (/* @__PURE__ */ new Date()).toISOString();
18886
- const id = crypto24.randomUUID();
19008
+ const id = crypto25.randomUUID();
18887
19009
  db.insert(agentMemory).values({
18888
19010
  id,
18889
19011
  projectId: args.projectId,
@@ -18925,7 +19047,7 @@ function writeCompactionNote(db, args) {
18925
19047
  }
18926
19048
  const now = (/* @__PURE__ */ new Date()).toISOString();
18927
19049
  const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
18928
- const id = crypto24.randomUUID();
19050
+ const id = crypto25.randomUUID();
18929
19051
  let inserted;
18930
19052
  db.transaction((tx) => {
18931
19053
  tx.insert(agentMemory).values({
@@ -19297,7 +19419,7 @@ var SessionRegistry = class {
19297
19419
  return agent2;
19298
19420
  }
19299
19421
  const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
19300
- const sessionId = crypto25.randomUUID();
19422
+ const sessionId = crypto26.randomUUID();
19301
19423
  const agent = createAeroSession({
19302
19424
  projectName,
19303
19425
  client: this.opts.client,
@@ -19749,7 +19871,7 @@ ${lines.join("\n")}
19749
19871
  insertRow(params) {
19750
19872
  const now = (/* @__PURE__ */ new Date()).toISOString();
19751
19873
  this.opts.db.insert(agentSessions).values({
19752
- id: params.id ?? crypto25.randomUUID(),
19874
+ id: params.id ?? crypto26.randomUUID(),
19753
19875
  projectId: params.projectId,
19754
19876
  systemPrompt: params.systemPrompt,
19755
19877
  modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
@@ -19776,7 +19898,7 @@ ${lines.join("\n")}
19776
19898
  };
19777
19899
 
19778
19900
  // src/agent/viewer-sessions.ts
19779
- import crypto26 from "crypto";
19901
+ import crypto27 from "crypto";
19780
19902
  import { and as and21, eq as eq25, isNotNull as isNotNull3, like as like2 } from "drizzle-orm";
19781
19903
  var AERO_VIEWER_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
19782
19904
  CanonryMcpToolNames.canonry_ads_account,
@@ -19830,8 +19952,8 @@ function trimViewerTranscript(messages, max = MAX_VIEWER_MESSAGES) {
19830
19952
  }
19831
19953
  var VIEWER_IDLE_MS = 12 * 60 * 60 * 1e3;
19832
19954
  function mintViewerAeroKey(db, userId, now = /* @__PURE__ */ new Date()) {
19833
- const raw = `cnry_${crypto26.randomBytes(24).toString("hex")}`;
19834
- const id = crypto26.randomUUID();
19955
+ const raw = `cnry_${crypto27.randomBytes(24).toString("hex")}`;
19956
+ const id = crypto27.randomUUID();
19835
19957
  db.insert(apiKeys).values({
19836
19958
  id,
19837
19959
  name: `${VIEWER_AERO_KEY_PREFIX}${userId}`,
@@ -19983,15 +20105,15 @@ var ViewerAeroSessions = class {
19983
20105
  };
19984
20106
 
19985
20107
  // src/mcp-http.ts
19986
- import crypto27 from "crypto";
20108
+ import crypto28 from "crypto";
19987
20109
  import { eq as eq26 } from "drizzle-orm";
19988
20110
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
19989
20111
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
19990
20112
  var SESSION_IDLE_MS = 10 * 60 * 1e3;
19991
20113
  var SWEEP_INTERVAL_MS = 60 * 1e3;
19992
20114
  function mintSessionKey(db, scopes, userId) {
19993
- const raw = `cnry_${crypto27.randomBytes(24).toString("hex")}`;
19994
- const id = crypto27.randomUUID();
20115
+ const raw = `cnry_${crypto28.randomBytes(24).toString("hex")}`;
20116
+ const id = crypto28.randomUUID();
19995
20117
  db.insert(apiKeys).values({
19996
20118
  id,
19997
20119
  name: `mcp-session:${userId}`,
@@ -20075,7 +20197,7 @@ function registerMcpHttpRoutes(scope, opts) {
20075
20197
  clientName: `canonry-mcp/${PACKAGE_VERSION}`,
20076
20198
  surface: "mcp-http",
20077
20199
  // Correlation only: this is never accepted as caller identity or authority.
20078
- actorSession: crypto27.randomUUID()
20200
+ actorSession: crypto28.randomUUID()
20079
20201
  });
20080
20202
  let server;
20081
20203
  try {
@@ -20092,7 +20214,7 @@ function registerMcpHttpRoutes(scope, opts) {
20092
20214
  throw error;
20093
20215
  }
20094
20216
  const transport = new StreamableHTTPServerTransport({
20095
- sessionIdGenerator: () => crypto27.randomUUID(),
20217
+ sessionIdGenerator: () => crypto28.randomUUID(),
20096
20218
  onsessioninitialized: (id) => {
20097
20219
  sessions.set(id, { transport, close, authorizationId, segmentId: segment.id, lastSeenAt: now() });
20098
20220
  }
@@ -21801,14 +21923,14 @@ function summarizeProviderConfig(config) {
21801
21923
  };
21802
21924
  }
21803
21925
  function hashApiKey2(key) {
21804
- return crypto28.createHash("sha256").update(key).digest("hex");
21926
+ return crypto29.createHash("sha256").update(key).digest("hex");
21805
21927
  }
21806
21928
  var DASHBOARD_SCRYPT_KEYLEN = 64;
21807
21929
  var DASHBOARD_SCRYPT_COST = 1 << 15;
21808
21930
  var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
21809
21931
  function hashDashboardPassword(password) {
21810
- const salt = crypto28.randomBytes(16);
21811
- const derived = crypto28.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
21932
+ const salt = crypto29.randomBytes(16);
21933
+ const derived = crypto29.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
21812
21934
  N: DASHBOARD_SCRYPT_COST,
21813
21935
  maxmem: DASHBOARD_SCRYPT_MAXMEM
21814
21936
  });
@@ -21829,14 +21951,14 @@ function verifyDashboardPassword(password, storedHash) {
21829
21951
  } catch {
21830
21952
  return { ok: false, needsRehash: false };
21831
21953
  }
21832
- const derived = crypto28.scryptSync(password, salt, expected.length, {
21954
+ const derived = crypto29.scryptSync(password, salt, expected.length, {
21833
21955
  N: DASHBOARD_SCRYPT_COST,
21834
21956
  maxmem: DASHBOARD_SCRYPT_MAXMEM
21835
21957
  });
21836
21958
  if (derived.length !== expected.length)
21837
21959
  return { ok: false, needsRehash: false };
21838
21960
  return {
21839
- ok: crypto28.timingSafeEqual(derived, expected),
21961
+ ok: crypto29.timingSafeEqual(derived, expected),
21840
21962
  needsRehash: false
21841
21963
  };
21842
21964
  }
@@ -21845,7 +21967,7 @@ function verifyDashboardPassword(password, storedHash) {
21845
21967
  const expected = Buffer.from(storedHash, "hex");
21846
21968
  if (candidate.length !== expected.length)
21847
21969
  return { ok: false, needsRehash: false };
21848
- const ok = crypto28.timingSafeEqual(candidate, expected);
21970
+ const ok = crypto29.timingSafeEqual(candidate, expected);
21849
21971
  return { ok, needsRehash: ok };
21850
21972
  }
21851
21973
  return { ok: false, needsRehash: false };
@@ -21981,7 +22103,7 @@ function cloneGtmConfig(config) {
21981
22103
  function createGoogleMarketingConfigCredentialStore(input) {
21982
22104
  const persistConfigPatch = input.saveConfigPatch ?? saveConfigPatch;
21983
22105
  const env = input.env ?? process.env;
21984
- const randomUUID3 = input.randomUUID ?? crypto28.randomUUID;
22106
+ const randomUUID3 = input.randomUUID ?? crypto29.randomUUID;
21985
22107
  const snapshot = () => ({
21986
22108
  googleAds: cloneGoogleAdsConfig(input.config.googleAds),
21987
22109
  gtm: cloneGtmConfig(input.config.gtm)
@@ -22084,7 +22206,7 @@ async function createServer(opts) {
22084
22206
  const trustProxy = resolveTrustProxy(process.env.CANONRY_TRUST_PROXY);
22085
22207
  const app = Fastify({
22086
22208
  loggerInstance: logger,
22087
- genReqId: () => crypto28.randomUUID(),
22209
+ genReqId: () => crypto29.randomUUID(),
22088
22210
  trustProxy
22089
22211
  });
22090
22212
  const registry = new ProviderRegistry();
@@ -22851,7 +22973,7 @@ async function createServer(opts) {
22851
22973
  return removed;
22852
22974
  }
22853
22975
  };
22854
- const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto28.randomBytes(32).toString("hex");
22976
+ const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto29.randomBytes(32).toString("hex");
22855
22977
  const googleConnectionStore = {
22856
22978
  listConnections: (domain) => listGoogleConnections(opts.config, domain),
22857
22979
  getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
@@ -22945,7 +23067,7 @@ async function createServer(opts) {
22945
23067
  if (!existing) {
22946
23068
  const prefix = opts.config.apiKey.slice(0, 12);
22947
23069
  opts.db.insert(apiKeys).values({
22948
- id: `key_${crypto28.randomBytes(8).toString("hex")}`,
23070
+ id: `key_${crypto29.randomBytes(8).toString("hex")}`,
22949
23071
  name: "default",
22950
23072
  keyHash,
22951
23073
  keyPrefix: prefix,
@@ -22969,7 +23091,7 @@ async function createServer(opts) {
22969
23091
  };
22970
23092
  const createSession = (apiKeyId) => {
22971
23093
  pruneExpiredSessions();
22972
- const sessionId = crypto28.randomBytes(32).toString("hex");
23094
+ const sessionId = crypto29.randomBytes(32).toString("hex");
22973
23095
  sessions.set(sessionId, {
22974
23096
  apiKeyId,
22975
23097
  expiresAt: Date.now() + SESSION_TTL_MS
@@ -23185,16 +23307,13 @@ async function createServer(opts) {
23185
23307
  return built.ok ? { ok: true, estimatedInputTokens: built.estimate.inputTokens } : built;
23186
23308
  }
23187
23309
  });
23188
- let sentimentTick = null;
23189
- const pollSentiment = () => {
23190
- if (sentimentTick) return;
23191
- sentimentTick = sentimentWorker.tick().catch(() => {
23310
+ const sentimentPoller = createSentimentPoller(
23311
+ () => sentimentWorker.tick(),
23312
+ () => {
23192
23313
  log24.error("sentiment.worker-failed", { reason: "Stored sentiment worker could not finish its tick." });
23193
- return 0;
23194
- }).finally(() => {
23195
- sentimentTick = null;
23196
- });
23197
- };
23314
+ }
23315
+ );
23316
+ const pollSentiment = sentimentPoller.poll;
23198
23317
  let sentimentTimer;
23199
23318
  runCoordinator.onSentimentCompleted = async () => {
23200
23319
  pollSentiment();
@@ -23206,7 +23325,8 @@ async function createServer(opts) {
23206
23325
  });
23207
23326
  app.addHook("onClose", async () => {
23208
23327
  clearInterval(sentimentTimer);
23209
- await sentimentTick;
23328
+ sentimentPoller.stop();
23329
+ await sentimentPoller.settled();
23210
23330
  });
23211
23331
  const providerModelCatalog = createProviderModelCatalog(registry);
23212
23332
  await app.register(apiRoutes, {
@@ -23442,7 +23562,7 @@ async function createServer(opts) {
23442
23562
  deps: {
23443
23563
  enqueueAutoExtract: ({ projectId, release: r }) => {
23444
23564
  const now = (/* @__PURE__ */ new Date()).toISOString();
23445
- const runId = crypto28.randomUUID();
23565
+ const runId = crypto29.randomUUID();
23446
23566
  opts.db.insert(runs).values({
23447
23567
  id: runId,
23448
23568
  projectId,
@@ -23686,7 +23806,7 @@ async function createServer(opts) {
23686
23806
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
23687
23807
  opts.db.insert(auditLog).values(
23688
23808
  targetProjectIds.map((projectId) => ({
23689
- id: crypto28.randomUUID(),
23809
+ id: crypto29.randomUUID(),
23690
23810
  projectId,
23691
23811
  actor: auditContext?.actor ?? "api",
23692
23812
  userAgent: auditContext?.userAgent ?? null,
@@ -23783,6 +23903,7 @@ async function createServer(opts) {
23783
23903
  const { event: eventName, eventId, ...properties } = event;
23784
23904
  trackEvent(eventName, properties, { source: "dashboard", eventId });
23785
23905
  },
23906
+ submitFeedback: (submission, context) => sendFeedback(submission, context),
23786
23907
  onCdpConfigure: async (host, port2) => {
23787
23908
  if (!opts.config.cdp) opts.config.cdp = {};
23788
23909
  opts.config.cdp.host = host;